From 3e0c5ce1c4546163603c041f9c711d60ea317c25 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Wed, 4 Sep 2024 18:10:54 +0800 Subject: [PATCH 001/132] init commit for FXmac ethernet --- Cargo.toml | 17 + LICENSE | 674 ++++++++++++++++++++++ README.md | 34 ++ rust-toolchain | 5 + src/fxmac.rs | 1385 +++++++++++++++++++++++++++++++++++++++++++++ src/fxmac_dma.rs | 202 +++++++ src/fxmac_phy.rs | 411 ++++++++++++++ src/lib.rs | 23 + src/macb_const.rs | 894 +++++++++++++++++++++++++++++ src/mii_const.rs | 274 +++++++++ 10 files changed, 3919 insertions(+) create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 rust-toolchain create mode 100644 src/fxmac.rs create mode 100644 src/fxmac_dma.rs create mode 100644 src/fxmac_phy.rs create mode 100644 src/lib.rs create mode 100644 src/macb_const.rs create mode 100644 src/mii_const.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..7a5ce232b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +cargo-features = ["edition2024"] + +[package] +name = "fxmac_rs" +authors = ["xiaoluoyuan@163.com"] +version = "0.1.0" +edition = "2024" +license = "GPL-2.0" +description = "fxmac_rs ethernet rust driver on SiFive FU740 board." +homepage = "https://github.com/elliott10/cadence-macb.git" +documentation = "https://github.com/elliott10/cadence-macb/blob/main/README.md" +repository = "https://github.com/elliott10/cadence-macb.git" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +log = "0.4" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 000000000..315160f80 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# Cadence Macb ethernet driver +cadence-macb ethernet Rust driver on SiFive FU740 board. + +### Quick Start + +Initialize ethernet driver +``` +pub struct MEM; +impl MemMapping for MEM { + fn dma_alloc_coherent(pages: usize) -> usize { + ... + } + fn dma_free_coherent(paddr: usize, pages: usize) { + ... + } +} + +let mut macb_device = cadence_macb::eth_macb::open::(&mac).unwrap(); +``` + +Sending network packets +``` +cadence_macb::eth_macb_ops::macb_send(&mut macb_device, &packet); + +``` + +Receiving network packets +``` +cadence_macb::eth_macb_ops::macb_recv(&mut macb_device, &mut rx_buffer); + +``` + +## Reference +* Linux and U-Boot C code diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 000000000..2b7ce9778 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,5 @@ +[toolchain] +channel = "nightly-2024-08-15" +components = [ "llvm-tools-preview" ] +targets = [ "aarch64-unknown-none-softfloat" ] +profile = "minimal" diff --git a/src/fxmac.rs b/src/fxmac.rs new file mode 100644 index 000000000..397c3fc3b --- /dev/null +++ b/src/fxmac.rs @@ -0,0 +1,1385 @@ + +pub struct FXmac { + FXmacConfig config; + is_ready: u32, /* Device is ininitialized and ready*/ + is_started: u32, + link_status: u32, /* indicates link status ,FXMAC_LINKUP is link up ,FXMAC_LINKDOWN is link down,FXMAC_NEGOTIATING is need to negotiating*/ + options: u32, + mask: u32, /* indicates intr mask */ + caps: u32, /* Capability mask bits */ + + FXmacQueue tx_bd_queue; /* Transmit Queue */ + FXmacQueue rx_bd_queue; /* Receive Queue */ + + /* + FXmacIrqHandler send_irq_handler; + void *send_args; + + FXmacIrqHandler recv_irq_handler; + void *recv_args; + + FXmacErrorIrqHandler error_irq_handler; + void *error_args; + + FXmacIrqHandler link_change_handler; + void *link_change_args; + + FXmacIrqHandler restart_handler; + void *restart_args; + */ + + moudle_id: u32, /* Module identification number */ + max_mtu_size: u32, + max_frame_size: u32, + + phy_address: u32, /* phy address */ + rxbuf_mask: u32, /* 1000,100,10 */ + +} + +pub struct FXmacConfig { + instance_id: u32, /* Id of device*/ + base_address: u64, + extral_mode_base: u64, + extral_loopback_base: u64, + interface: FXmacPhyInterface; + speed: u32, /* FXMAC_SPEED_XXX */ + duplex: u32, /* 1 is full-duplex , 0 is half-duplex */ + auto_neg: u32, /* Enable auto-negotiation - when set active high, autonegotiation operation is enabled. */ + pclk_hz: u32, + max_queue_num: u32, /* Number of Xmac Controller Queues */ + tx_queue_id: u32, /* 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number */ + rx_queue_id: u32, /* 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number */ + hotplug_irq_num: u32, + dma_brust_length: u32, /* burst length */ + network_default_config: u32, + queue_irq_num[u32; FXMAC_QUEUE_MAX_NUM]; /* mac0 8个 ,其他的 4个 */ + caps: u32, /* used to configure tail ptr feature */ +} + +/// Interface Mode definitions +pub enum FXmacPhyInterface +{ + FXMAC_PHY_INTERFACE_MODE_SGMII = 0, + FXMAC_PHY_INTERFACE_MODE_RMII = 1, + FXMAC_PHY_INTERFACE_MODE_RGMII = 2, + FXMAC_PHY_INTERFACE_MODE_XGMII = 3, + FXMAC_PHY_INTERFACE_MODE_USXGMII = 4, + FXMAC_PHY_INTERFACE_MODE_5GBASER = 5, + FXMAC_PHY_INTERFACE_MODE_2500BASEX = 6, +} + +pub fn xmac_init() -> i32 { + + let xmac = FXmac{ }; + + //mii_interface = FXMAC_LWIP_PORT_INTERFACE_SGMII; + + + +/* step 1: initialize instance */ +/* step 2: depend on config set some options : JUMBO / IGMP */ +/* step 3: FXmacSelectClk */ +/* step 4: FXmacInitInterface */ +/* step 5: initialize phy */ +/* step 6: initialize dma */ +/* step 7: initialize interrupt */ +/* step 8: start mac */ + +// FXmacLwipPortInit() + + +// fxmac_cfg_tbl[0].base_address = 0x3200c000 +// xmac_config: interface=FXMAC_PHY_INTERFACE_MODE_SGMII, autonegotiation=0, phy_speed=FXMAC_PHY_SPEED_100M, phy_duplex=FXMAC_PHY_FULL_DUPLEX + +// Reset the hardware and set default options +let link_status = FXMAC_LINKDOWN; +let is_ready = FT_COMPONENT_IS_READY; +FXmacReset(); + +// irq_handler = (FXmacIrqHandler)FXmacIrqStubHandler; +let mask = FXMAC_INTR_MASK; + +if (config.caps & FXMAC_CAPS_TAILPTR) != 0 +{ + FXmacSetOptions(xmac_p, FXMAC_TAIL_PTR_OPTION, 0); + xmac_p->mask &= !FXMAC_IXR_TXUSED_MASK; +} + +FxmacFeatureSetOptions(instance_p->feature,xmac_p); + +status = FXmacSetMacAddress(xmac_p, (void *)(instance_p->hwaddr), 0); + + +if(mac_config.interface != FXMAC_PHY_INTERFACE_MODE_USXGMII) +{ + /* initialize phy */ + status = FXmacPhyInit(xmac_p, xmac_p->config.speed, xmac_p->config.duplex, xmac_p->config.auto_neg, XMAC_PHY_RESET_ENABLE); + if (status != FT_SUCCESS) + { + warn!("FXmacPhyInit is error"); + } +} else { + info!("interface == FXMAC_PHY_INTERFACE_MODE_USXGMII"); +} + +FXmacSelectClk(xmac_p); +FXmacInitInterface(xmac_p); + +// initialize dma +dmacrreg = FXMAC_READREG32(xmac_p->config.base_address, FXMAC_DMACR_OFFSET); +dmacrreg &= !(FXMAC_DMACR_BLENGTH_MASK); +dmacrreg = dmacrreg | FXMAC_DMACR_INCR16_AHB_AXI_BURST; /* Attempt to use bursts of up to 16. */ +write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, dmacrreg); + +// TODO +FXmacInitDma(instance_p); + +/* initialize interrupt */ +FXmacSetupIsr(instance_p); + +return FT_SUCCESS; + + + + + + +// Set unicast hash table + +FXmac_SetHash() + +/* + +ethernetif_link_detect() + +ethernetif_input() + +ethernetif_deinit() + +ethernetif_start() + +ethernetif_debug() + +*/ + + +} + +/* + * Perform a graceful reset of the Ethernet MAC. Resets the DMA channels, the + * transmitter, and the receiver. + * + * Steps to reset + * - Stops transmit and receive channels + * - Stops DMA + * - Configure transmit and receive buffer size to default + * - Clear transmit and receive status register and counters + * - Clear all interrupt sources + * - Clear phy (if there is any previously detected) address + * - Clear MAC addresses (1-4) as well as Type IDs and hash value + * + */ + +fn FXmacReset() { + let mac_addr: [u8; 6] = [0; 6]; + let mut reg_val: u32 = 0 + let mut write_reg: u32 = 0; + + /* Stop the device and reset hardware */ + FXmacStop(); + + // Module identification number + let moudle_id = ( read_reg((FXMAC_IOBASE + FXMAC_REVISION_REG_OFFSET) as *const u32) + & FXMAC_IDENTIFICATION_MASK ) >> 16; + let mut max_mtu_size = FXMAC_MTU; + let mut max_frame_size = FXMAC_MAX_FRAME_SIZE; + let max_queue_num = 16; + let network_default_config = FXMAC_DEFAULT_OPTIONS; + + let netctrl = (FXMAC_NWCTRL_STATCLR_MASK & !(FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK as u32)) | FXMAC_NWCTRL_MDEN_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, netctrl); + + FXmacConfigureCaps(); + + // mdio clock division + let mut write_reg = FXmacClkDivGet(moudle_id); + // DMA bus width + write_reg |= FXmacDmaWidth(moudle_id); + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, write_reg); + + FXmacDmaReset(moudle_id, max_frame_size, max_queue_num); + + // This register, when read provides details of the status of the receive path. + write_reg((FXMAC_IOBASE + FXMAC_RXSR_OFFSET) as *mut u32, FXMAC_SR_ALL_MASK); + + // write 1 ro the relavant bit location disable that particular interrupt + write_reg((FXMAC_IOBASE + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_ALL_MASK); + + reg_val = read_reg((FXMAC_IOBASE + FXMAC_ISR_OFFSET) as *const u32); + write_reg((FXMAC_IOBASE + FXMAC_ISR_OFFSET) as *mut u32, reg_val); + + write_reg((FXMAC_IOBASE + FXMAC_TXSR_OFFSET) as *mut u32, FXMAC_SR_ALL_MASK); + + FXmacClearHash(); + + // set default mac address + for i in 0..4 { + FXmacSetMacAddress(mac_addr, i); + FXmacGetMacAddress(mac_addr, i); + FXmacSetTypeIdCheck(0, i); + } + + /* clear all counters */ + for i in 0..((FXMAC_LAST_OFFSET - FXMAC_OCTTXL_OFFSET) / 4) { + read_reg((FXMAC_IOBASE + FXMAC_OCTTXL_OFFSET + (i * 4) as u32) as *mut u32); + } + + /* Sync default options with hardware but leave receiver and + * transmitter disabled. They get enabled with FXmacStart() if + * FXMAC_TRANSMITTER_ENABLE_OPTION and FXMAC_RECEIVER_ENABLE_OPTION are set. + */ + FXmacSetOptions(max_mtu_size, max_frame_size, max_queue_num, network_default_config & !((FXMAC_TRANSMITTER_ENABLE_OPTION | FXMAC_RECEIVER_ENABLE_OPTION) as u32), 0); + FXmacClearOptions(max_mtu_size, max_frame_size, max_queue_num, !network_default_config, 0); +} +/* + * Gracefully stop the Ethernet MAC as follows: + * - Disable all interrupts from this device + * - Stop DMA channels + * - Disable the tansmitter and receiver + */ + pub fn FXmacStop() { + // Disable all interrupts + write_reg((FXMAC_IOBASE + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_ALL_MASK); + + // Disable the receiver & transmitter + let reg_val = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg_val &= (!FXMAC_NWCTRL_RXEN_MASK) as u32; + reg_val &= (!FXMAC_NWCTRL_TXEN_MASK) as u32; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_val); + } + + fn FXmacDmaReset(moudle_id: u32, max_frame_size: u32, max_queue_num: u32) + { + let mut dmacfg: u32 = 0; + //let max_queue_num = 16; + let dma_brust_length = 16; + + let mut rx_buf_size: u32 = max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += ((max_frame_size % FXMAC_RX_BUF_UNIT) != 0) ? 1 : 0; /* roundup */ + + let FXMAC_RXBUFQX_SIZE_OFFSET = |value: u32| (FXMAC_RXBUFQ1_SIZE_OFFSET + (value << 2)); + + // moudle_id=12 + if (moudle_id >= 2) + { + for queue in 0..max_queue_num { + dmacfg = 0; + FXmacSetQueuePtr(0, queue, FXMAC_SEND); + FXmacSetQueuePtr(0, queue, FXMAC_RECV); + + if queue != 0 + { + write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue)) as *mut u32, rx_buf_size); + } + else /* queue is 0 */ + { + dmacfg |= (FXMAC_DMACR_RXBUF_MASK & (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT)); + } + } + + dmacfg |= (dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); + + dmacfg &= !FXMAC_DMACR_ENDIAN_MASK; + dmacfg &= !FXMAC_DMACR_SWAP_MANAGEMENT_MASK; /* 选择小端 */ + + dmacfg &= !FXMAC_DMACR_TCPCKSUM_MASK; /* close transmitter checksum generation engine */ + + dmacfg &= !FXMAC_DMACR_ADDR_WIDTH_64; + dmacfg |= FXMAC_DMACR_RXSIZE_MASK | FXMAC_DMACR_TXSIZE_MASK; + /* + set this bit can enable auto discard rx frame when lack of receive source, + which avoid endless rx buffer not available error intrrupts. + */ + dmacfg |= FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK; /* force_discard_on_rx_err */ + dmacfg |= FXMAC_DMACR_ADDR_WIDTH_64; // Just for aarch64 + } + else + { + FXmacSetQueuePtr(0, 0, FXMAC_SEND); + FXmacSetQueuePtr(0, 0, FXMAC_RECV); + dmacfg |= (FXMAC_DMACR_RXBUF_MASK & (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT)); + dmacfg |= (dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); + + dmacfg &= !FXMAC_DMACR_ENDIAN_MASK; + dmacfg &= !FXMAC_DMACR_SWAP_MANAGEMENT_MASK; /* 选择小端 */ + + dmacfg &= !FXMAC_DMACR_TCPCKSUM_MASK; /* close transmitter checksum generation engine */ + + dmacfg &= !FXMAC_DMACR_ADDR_WIDTH_64; + dmacfg |= FXMAC_DMACR_RXSIZE_MASK | FXMAC_DMACR_TXSIZE_MASK; + /* + set this bit can enable auto discard rx frame when lack of receive source, + which avoid endless rx buffer not available error intrrupts. + */ + dmacfg |= FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK; /* force_discard_on_rx_err */ + dmacfg |= FXMAC_DMACR_ADDR_WIDTH_64; // Just for aarch64 + } + + write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, dmacfg); + } + + +fn FXmacDmaWidth(moudle_id: u32) -> u32 { + if moudle_id < 2 { + return FXMAC_NWCFG_BUS_WIDTH_32_MASK; + } + + let read_regs = read_reg(FXMAC_IOBASE, FXMAC_DESIGNCFG_DEBUG1_OFFSET); + match ((read_regs & FXMAC_DESIGNCFG_DEBUG1_BUS_WIDTH_MASK) >> 25) { + 4 => { + info!("bus width is 128"); + FXMAC_NWCFG_BUS_WIDTH_128_MASK + } + 2 => { + info!("bus width is 64"); + FXMAC_NWCFG_BUS_WIDTH_64_MASK + } + _ => { + info!("bus width is 32"); + FXMAC_NWCFG_BUS_WIDTH_32_MASK + } + } +} + + + +fn FxmacFeatureSetOptions(feature: u32, FXmac* xmac_p) +{ + let mut options: u32 = 0; + + if (feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 + { + info!("FXMAC_JUMBO_ENABLE_OPTION is ok"); + options |= FXMAC_JUMBO_ENABLE_OPTION; + } + + if (feature & FXMAC_LWIP_PORT_CONFIG_UNICAST_ADDRESS_FILITER) !=0 + { + info!("FXMAC_UNICAST_OPTION is ok"); + options |= FXMAC_UNICAST_OPTION; + } + + if (feature & FXMAC_LWIP_PORT_CONFIG_MULTICAST_ADDRESS_FILITER) !=0 + { + info!("FXMAC_MULTICAST_OPTION is ok"); + options |= FXMAC_MULTICAST_OPTION; + } + /* enable copy all frames */ + if (feature & FXMAC_LWIP_PORT_CONFIG_COPY_ALL_FRAMES) != 0 + { + info!("FXMAC_PROMISC_OPTION is ok"); + options |= FXMAC_PROMISC_OPTION; + } + /* close fcs check */ + if (feature & FXMAC_LWIP_PORT_CONFIG_CLOSE_FCS_CHECK) != 0 + { + info!("FXMAC_FCS_STRIP_OPTION is ok"); + options |= FXMAC_FCS_STRIP_OPTION; + } + + FXmacSetOptions(xmac_p, options, 0); +} + +/** + * This function sets the start address of the transmit/receive buffer queue. + * + * @param instance_p is a pointer to the instance to be worked on. + * @param queue_p is the address of the Queue to be written + * @param queue_num is the Buffer Queue Index + * @param direction indicates Transmit/Receive + * + * @note + * The buffer queue addresses has to be set before starting the transfer, so + * this function has to be called in prior to FXmacStart() + */ + fn FXmacSetQueuePtr(queue_p: u64, queue_num: u8, direction: u32) { + + let flag_queue_p = if queue_p == 0 { 1 }else{ 0 }; + + let FXMAC_QUEUE_REGISTER_OFFSET = |base_addr: u32, queue_id: u32| (base_addr + (queue_id - 1) * 4); + + if queue_num == 0x00U { + if direction == FXMAC_SEND + { + /* set base start address of TX buffer queue (tx buffer descriptor list) */ + write_reg((FXMAC_IOBASE + FXMAC_TXQBASE_OFFSET) as *mut u32, + (queue_p & ULONG64_LO_MASK) | flag_queue_p); + } + else + { + /* set base start address of RX buffer queue (rx buffer descriptor list) */ + write_reg((FXMAC_IOBASE + FXMAC_RXQBASE_OFFSET) as *mut u32, + (queue_p & ULONG64_LO_MASK) | flag_queue_p); + } + } + else + { + if direction == FXMAC_SEND + { + write_reg((FXMAC_IOBASE + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, queue_num),) as *mut u32, + (queue_p & ULONG64_LO_MASK) | flag_queue_p); + } + else + { + write_reg((FXMAC_IOBASE + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_RXQ1BASE_OFFSET, queue_num)) as *mut u32, + (queue_p & ULONG64_LO_MASK) | flag_queue_p); + } + } + + if direction == FXMAC_SEND // Only for aarch64 + { + /* Set the MSB of TX Queue start address */ + write_reg((FXMAC_IOBASE + FXMAC_MSBBUF_TXQBASE_OFFSET) as *mut u32, + ((queue_p & ULONG64_HI_MASK) >> 32U) as u32); + } else { + /* Set the MSB of RX Queue start address */ + write_reg((FXMAC_IOBASE + FXMAC_MSBBUF_RXQBASE_OFFSET) as *mut u32, + ((queue_p & ULONG64_HI_MASK) >> 32U) as u32); + } +} + +fn FXmacConfigureCaps() { + let read_regs = read_reg((FXMAC_IOBASE + FXMAC_DESIGNCFG_DEBUG1_OFFSET) as *const u32); + if (read_regs & FXMAC_DESIGNCFG_DEBUG1_BUS_IRQCOR_MASK) == 0 { + // let caps |= FXMAC_CAPS_ISR_CLEAR_ON_WRITE; + info!("Design ConfigReg1: {:#x} Has FXMAC_CAPS_ISR_CLEAR_ON_WRITE feature", read_regs); + } +} + + +fn FXmacClkDivGet(moudle_id: u32) -> u32 { + // moudle_id=12 + // let pclk_hz = 50000000; + let pclk_hz = FXMAC0_PCLK; + + if (pclk_hz <= 20000000) + { + return FXMAC_NWCFG_CLOCK_DIV8_MASK; + } + else if (pclk_hz <= 40000000) + { + return FXMAC_NWCFG_CLOCK_DIV16_MASK; + } + else if (pclk_hz <= 80000000) + { + return FXMAC_NWCFG_CLOCK_DIV32_MASK; + } + else if (moudle_id >= 2) + { + if (pclk_hz <= 120000000) + { + return FXMAC_NWCFG_CLOCK_DIV48_MASK; + } + else if (pclk_hz <= 160000000) + { + return FXMAC_NWCFG_CLOCK_DIV64_MASK; + } + else if (pclk_hz <= 240000000) + { + return FXMAC_NWCFG_CLOCK_DIV96_MASK; + } + else if (pclk_hz <= 320000000) + { + return FXMAC_NWCFG_CLOCK_DIV128_MASK; + } + else + { + return FXMAC_NWCFG_CLOCK_DIV224_MASK; + } + } + else + { + return FXMAC_NWCFG_CLOCK_DIV64_MASK; + } +} + + +/** + * Set options for the driver/device. The driver should be stopped with + * FXmacStop() before changing options. + */ + fn FXmacSetOptions(max_mtu_size: mut u32, max_frame_size: mut u32, max_queue_num: u32, options: mut u32, queue_num: u32) -> u32 { + let mut reg: u32 = 0; /* Generic register contents */ + let mut reg_netcfg: u32 = 0; /* Reflects original contents of NET_CONFIG */ + let mut reg_new_netcfg: u32 = 0; /* Reflects new contents of NET_CONFIG */ + let mut status: u32 = 0; + + let FXMAC_RXBUFQX_SIZE_OFFSET = |value: u32| (FXMAC_RXBUFQ1_SIZE_OFFSET + (value << 2)); + let is_started = 0; + + info!("FXmacSetOptions, is_started={}, options={}, queue_num={}, max_queue_num={}", is_started, options, queue_num, max_queue_num); + + /* Be sure device has been stopped */ + if is_started == FT_COMPONENT_IS_STARTED + { + status = FXMAC_ERR_MAC_IS_PROCESSING; + error!("FXMAC is processing when calling FXmacSetOptions function"); + } else { + + /* Many of these options will change the NET_CONFIG registers. + * To reduce the amount of IO to the device, group these options here + * and change them all at once. + */ + + /* Grab current register contents */ + reg_netcfg = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + + reg_new_netcfg = reg_netcfg; + + /* + * It is configured to max 1536. + */ + if (options & FXMAC_FRAME1536_OPTION) != 0 + { + reg_new_netcfg |= FXMAC_NWCFG_1536RXEN_MASK; + } + + /* Turn on VLAN packet only, only VLAN tagged will be accepted */ + if (options & FXMAC_VLAN_OPTION) != 0 + { + reg_new_netcfg |= FXMAC_NWCFG_NVLANDISC_MASK; + } + + /* Turn on FCS stripping on receive packets */ + if (options & FXMAC_FCS_STRIP_OPTION) != 0 + { + reg_new_netcfg |= FXMAC_NWCFG_FCS_REMOVE_MASK; + } + + /* Turn on length/type field checking on receive packets */ + if (options & FXMAC_LENTYPE_ERR_OPTION) != 0 + { + reg_new_netcfg |= FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK; + } + + /* Turn on flow control */ + if (options & FXMAC_FLOW_CONTROL_OPTION) != 0 + { + reg_new_netcfg |= FXMAC_NWCFG_PAUSE_ENABLE_MASK; + } + + /* Turn on promiscuous frame filtering (all frames are received) */ + if (options & FXMAC_PROMISC_OPTION) != 0 + { + reg_new_netcfg |= FXMAC_NWCFG_COPYALLEN_MASK; + } + + /* Allow broadcast address reception */ + if (options & FXMAC_BROADCAST_OPTION) != 0 + { + reg_new_netcfg &= !(FXMAC_NWCFG_BCASTDI_MASK as u32); + } + + /* Allow multicast address filtering */ + if (options & FXMAC_MULTICAST_OPTION) != 0 + { + reg_new_netcfg |= FXMAC_NWCFG_MCASTHASHEN_MASK; + } + + if (options & FXMAC_UNICAST_OPTION) != 0 + { + reg_new_netcfg |= FXMAC_NWCFG_UCASTHASHEN_MASK; + } + + if options & FXMAC_TAIL_PTR_OPTION + { + write_reg((FXMAC_IOBASE + FXMAC_TAIL_ENABLE) as *mut u32, 0x80000001); + } + + + /* enable RX checksum offload */ + if (options & FXMAC_RX_CHKSUM_ENABLE_OPTION) != 0 + { + reg_new_netcfg |= FXMAC_NWCFG_RXCHKSUMEN_MASK; + } + + /* Enable jumbo frames */ + if (options & FXMAC_JUMBO_ENABLE_OPTION) != 0 + { + max_mtu_size = FXMAC_MTU_JUMBO; + max_frame_size = FXMAC_MAX_FRAME_SIZE_JUMBO; + + reg_new_netcfg |= FXMAC_NWCFG_JUMBO_MASK; + + write_reg((FXMAC_IOBASE + FXMAC_JUMBOMAXLEN_OFFSET) as *mut u32, FXMAC_MAX_FRAME_SIZE_JUMBO); + + write_reg((FXMAC_IOBASE + FXMAC_TXQSEGALLOC_QLOWER_OFFSET) as *mut u32, FXMAC_TXQSEGALLOC_QLOWER_JUMBO_MASK); + + if queue_num == 0 { + let mut rx_buf_size: u32 = 0; + reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); + + reg &= !FXMAC_DMACR_RXBUF_MASK; + + rx_buf_size = max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if (max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { 1 } else { 0 }; + + reg |= (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT) & FXMAC_DMACR_RXBUF_MASK; + write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); + } else if queue_num < max_queue_num { + let mut rx_buf_size: u32 = 0; + rx_buf_size = max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if (max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { 1 } else { 0 }; + + write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue_num)) as *mut u32, rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK); + } + } + + if (options & FXMAC_SGMII_ENABLE_OPTION) != 0 + { + reg_new_netcfg |= (FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK | + FXMAC_NWCFG_PCSSEL_MASK); + } + + if (options & FXMAC_LOOPBACK_NO_MII_OPTION) != 0 + { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg |= FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + } + + if (options & FXMAC_LOOPBACK_USXGMII_OPTION) != 0 + { + write_reg((FXMAC_IOBASE + FXMAC_TEST_CONTROL_OFFSET) as *mut u32, 2); + } + + /* Officially change the NET_CONFIG registers if it needs to be + * modified. + */ + if (reg_netcfg != reg_new_netcfg) + { + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, reg_new_netcfg); + } + + /* Enable TX checksum offload */ + if (options & FXMAC_TX_CHKSUM_ENABLE_OPTION) != 0 + { + reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); + reg |= FXMAC_DMACR_TCPCKSUM_MASK; + write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); + } + + /* Enable transmitter */ + if (options & FXMAC_TRANSMITTER_ENABLE_OPTION) != 0 + { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg |= FXMAC_NWCTRL_TXEN_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + + } + + /* Enable receiver */ + if (options & FXMAC_RECEIVER_ENABLE_OPTION) != 0 + { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg |= FXMAC_NWCTRL_RXEN_MASK; + + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + + } + + /* The remaining options not handled here are managed elsewhere in the + * driver. No register modifications are needed at this time. Reflecting + * the option in instance_p->options is good enough for now. + */ + + /* Set options word to its new value */ + options |= options; + + status = FT_SUCCESS; + } + + status + } + +/// Clear options for the driver/device +fn FXmacClearOptions(max_mtu_size: mut u32, max_frame_size: mut u32, max_queue_num: u32, options: mut u32, queue_num: u32) -> u32 + { + let mut reg: u32 = 0; /* Generic */ + let mut reg_net_cfg: u32 = 0; /* Reflects original contents of NET_CONFIG */ + let mut reg_new_net_cfg: u32 = 0; /* Reflects new contents of NET_CONFIG */ + let mut status: u32 = 0; + + let is_started = 0; + /* Be sure device has been stopped */ + if (is_started == FT_COMPONENT_IS_STARTED) + { + status = FXMAC_ERR_MAC_IS_PROCESSING; + error!("FXMAC is processing when calling FXmacClearOptions function"); + + } else { + /* Many of these options will change the NET_CONFIG registers. + * To reduce the amount of IO to the device, group these options here + * and change them all at once. + */ + /* Grab current register contents */ + reg_net_cfg = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + reg_new_net_cfg = reg_net_cfg; + /* There is only RX configuration!? + * It is configured in two different length, up to 1536 and 10240 bytes + */ + if (options & FXMAC_FRAME1536_OPTION) != 0 + { + reg_new_net_cfg &= !(FXMAC_NWCFG_1536RXEN_MASK as u32); + } + + /* Turn off VLAN packet only */ + if (options & FXMAC_VLAN_OPTION) != 0 + { + reg_new_net_cfg &= !(FXMAC_NWCFG_NVLANDISC_MASK as u32); + } + + /* Turn off FCS stripping on receive packets */ + if (options & FXMAC_FCS_STRIP_OPTION) != 0 + { + reg_new_net_cfg &= !(FXMAC_NWCFG_FCS_REMOVE_MASK as u32); + } + + /* Turn off length/type field checking on receive packets */ + if (options & FXMAC_LENTYPE_ERR_OPTION) != 0 + { + reg_new_net_cfg &= !(FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK as u32); + } + + /* Turn off flow control */ + if (options & FXMAC_FLOW_CONTROL_OPTION) != 0 + { + reg_new_net_cfg &= !(FXMAC_NWCFG_PAUSE_ENABLE_MASK as u32); + } + + /* Turn off promiscuous frame filtering (all frames are received) */ + if (options & FXMAC_PROMISC_OPTION) != 0 + { + reg_new_net_cfg &= !(FXMAC_NWCFG_COPYALLEN_MASK as u32); + } + + /* Disallow broadcast address filtering => broadcast reception */ + if (options & FXMAC_BROADCAST_OPTION) != 0 + { + reg_new_net_cfg |= FXMAC_NWCFG_BCASTDI_MASK; + } + + /* Disallow unicast address filtering */ + if (options & FXMAC_UNICAST_OPTION) != 0 + { + reg_new_net_cfg &= !(FXMAC_NWCFG_UCASTHASHEN_MASK as u32); + } + + /* Disallow multicast address filtering */ + if (options & FXMAC_MULTICAST_OPTION) != 0 + { + reg_new_net_cfg &= !(FXMAC_NWCFG_MCASTHASHEN_MASK as u32); + } + + if (options & FXMAC_TAIL_PTR_OPTION) != 0 + { + write_reg((FXMAC_IOBASE + FXMAC_TAIL_ENABLE) as *mut u32, 0); + } + + /* Disable RX checksum offload */ + if (options & FXMAC_RX_CHKSUM_ENABLE_OPTION) != 0 + { + reg_new_net_cfg &= !(FXMAC_NWCFG_RXCHKSUMEN_MASK as u32); + } + + /* Disable jumbo frames */ + if (options & FXMAC_JUMBO_ENABLE_OPTION) != 0 /* 恢复之前buffer 容量 */ + { + max_mtu_size = FXMAC_MTU; + max_frame_size = FXMAC_MAX_FRAME_SIZE; + + reg_new_net_cfg &= !(FXMAC_NWCFG_JUMBO_MASK as u32); + + reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); + + reg &= !FXMAC_DMACR_RXBUF_MASK; + + if queue_num == 0 + { + u32 rx_buf_size = 0; + + reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); + reg &= !FXMAC_DMACR_RXBUF_MASK; + + rx_buf_size = max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if max_frame_size % FXMAC_RX_BUF_UNIT != 0 {1} else {0}; + + reg |= (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT) & FXMAC_DMACR_RXBUF_MASK; + + write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); + } + else if (queue_num < max_queue_num) + { + let rx_buf_size: u32 = 0; + rx_buf_size = max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if (max_frame_size % FXMAC_RX_BUF_UNIT) != 0 {1} else {0}; + + write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue_num)) as *mut u32, rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK); + } + } + + if (options & FXMAC_SGMII_ENABLE_OPTION) != 0 + { + reg_new_net_cfg &= !((FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK | + FXMAC_NWCFG_PCSSEL_MASK) as u32); + } + + if (options & FXMAC_LOOPBACK_NO_MII_OPTION) != 0 + { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg &= !FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + } + + if (options & FXMAC_LOOPBACK_USXGMII_OPTION) != 0 + { + write_reg((FXMAC_IOBASE + FXMAC_TEST_CONTROL_OFFSET) as *mut u32, + read_reg((FXMAC_IOBASE + FXMAC_TEST_CONTROL_OFFSET) as *const u32) & !2 ); + } + + /* Officially change the NET_CONFIG registers if it needs to be + * modified. + */ + if reg_net_cfg != reg_new_net_cfg + { + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, reg_new_net_cfg); + } + + /* Disable TX checksum offload */ + if (options & FXMAC_TX_CHKSUM_ENABLE_OPTION) != 0 + { + reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); + reg &= !FXMAC_DMACR_TCPCKSUM_MASK; + write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); + } + + /* Disable transmitter */ + if (options & FXMAC_TRANSMITTER_ENABLE_OPTION) != 0 + { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg &= !FXMAC_NWCTRL_TXEN_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + } + + /* Disable receiver */ + if (options & FXMAC_RECEIVER_ENABLE_OPTION) != 0 + { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg &= !FXMAC_NWCTRL_RXEN_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + } + + /* The remaining options not handled here are managed elsewhere in the + * driver. No register modifications are needed at this time. Reflecting + * option in instance_p->options is good enough for now. + */ + + /* Set options word to its new value */ + options &= !options; + + status = FT_SUCCESS; + } + status + } + +/// Clear the Hash registers for the mac address pointed by address_ptr. +fn FXmacClearHash() { + write_reg((FXMAC_IOBASE + FXMAC_HASHL_OFFSET) as *mut u32, 0); + + /* write bits [63:32] in TOP */ + write_reg((FXMAC_IOBASE + FXMAC_HASHH_OFFSET) as *mut u32, 0); +} + +/// Set the MAC address for this driver/device. The address is a 48-bit value. +/// The device must be stopped before calling this function. +fn FXmacSetMacAddress(address_ptr: &[u8; 6], index: u8) -> u32 { + let mut mac_addr: u32 = 0; + let aptr = address_ptr; + let index_loc: u8 = index; + let mut status: u32 = 0; + assert!((index_loc < FXMAC_MAX_MAC_ADDR as u8), "index of Mac Address exceed {}", FXMAC_MAX_MAC_ADDR); + + let is_started = 0; + /* Be sure device has been stopped */ + if is_started == (u32)FT_COMPONENT_IS_STARTED + { + status = FXMAC_ERR_MAC_IS_PROCESSING; + error!("FXMAC is processing when calling FXmacSetMacAddress function"); + } else { + /* Set the MAC bits [31:0] in BOT */ + mac_addr = aptr[0]; + mac_addr |= aptr[1] as u32 << 8; + mac_addr |= aptr[2] as u32 << 16; + mac_addr |= aptr[3] as u32 << 24; + write_reg((FXMAC_IOBASE + FXMAC_GEM_SA1B + (index_loc * 8) as u32) as *mut u32, mac_addr); + + /* There are reserved bits in TOP so don't affect them */ + mac_addr = read_reg((FXMAC_IOBASE + FXMAC_GEM_SA1T + (index_loc * 8) as u32) as *const u32); + mac_addr &= !FXMAC_GEM_SAB_MASK; + + /* Set MAC bits [47:32] in TOP */ + mac_addr |= aptr[4] as u32; + mac_addr |= aptr[5] as u32 << 8; + + write_reg((FXMAC_IOBASE + FXMAC_GEM_SA1T + (index_loc * 8) as u32) as *mut u32, mac_addr); + + status = FT_SUCCESS; + } + + status +} + +/// Set the Type ID match for this driver/device. The register is a 32-bit value. +/// The device must be stopped before calling this function. +fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { + let status: u32 = 0; + assert!((index < FXMAC_MAX_TYPE_ID as u8), "index of Type ID exceed {}", FXMAC_MAX_TYPE_ID); + + let is_started = 0; + /* Be sure device has been stopped */ + if is_started == FT_COMPONENT_IS_STARTED + { + status = FXMAC_ERR_MAC_IS_PROCESSING; + error!("FXMAC is processing when calling FXmacSetTypeIdCheck function"); + } else { + + /* Set the ID bits in MATCHx register */ + write_reg((FXMAC_IOBASE + FXMAC_MATCH1_OFFSET + (index * 4) as u32) as *mut u32, id_check); + + status = FT_SUCCESS; + } + + status; +} + +/// FXmacSelectClk +/// Determine the driver clock configuration based on the media independent interface +/// FXMAC_CLK_TYPE_0 + fn FXmacSelectClk(instance_p: &mut FXmac) + { + let speed: u32 = instance_p.config.speed; + let FXMAC_WRITEREG32 = |base_address: u64, offset: u64, reg_value: u32| write_reg((base_address + offset) as *mut u32, reg_value); + + assert!((speed == FXMAC_SPEED_10) || (speed == FXMAC_SPEED_100) || (speed == FXMAC_SPEED_1000) || (speed == FXMAC_SPEED_2500) || (speed == FXMAC_SPEED_10000)); + + if (instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_USXGMII) || (instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_XGMII) + { + if speed == FXMAC_SPEED_10000 + { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x1); /*0x1c0c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ + } + else if speed == FXMAC_SPEED_5000 + { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x8); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); /*0x1c0c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0); /*0x1c10*/ + } + } + else if instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_5GBASER + { + if speed == FXMAC_SPEED_5000 + { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x8); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); /*0x1c0c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x0); /*0x1c10*/ + } + } + else if instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_2500BASEX + { + if speed == FXMAC_SPEED_25000 + { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x1); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); /*0x1c0c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ + } + } + else if instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_SGMII + { + info!("FXMAC_PHY_INTERFACE_MODE_SGMII init"); + if speed == FXMAC_SPEED_2500 + { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ + } + else if speed == FXMAC_SPEED_1000 + { + info!("sgmii FXMAC_SPEED_1000"); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x8); /*0x1c0c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x0); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ + } + else if (speed == FXMAC_SPEED_100) || (speed == FXMAC_SPEED_10) + { + info!("sgmii FXMAC_SPEED_{}", speed); + + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x8); /*0x1c0c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x0); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x1); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x1); /*0x1c7c*/ + } + } + else if instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_RGMII + { + info!("FXMAC_PHY_INTERFACE_MODE_RGMII init"); + if speed == FXMAC_SPEED_1000 + { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_MII_SELECT, 0x1); /*0x1c18*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SEL_MII_ON_RGMII, 0x0); /*0x1c1c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, + 0x0); /*0x1c38*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL0, 0x1); /*0x1c80*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL1, 0x0); /*0x1c84*/ + } + else if speed == FXMAC_SPEED_100 + { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_MII_SELECT, 0x1); /*0x1c18*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SEL_MII_ON_RGMII, 0x0); /*0x1c1c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, + 0x0); /*0x1c38*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL0, 0x0); /*0x1c80*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL1, 0x0); /*0x1c84*/ + } + else + { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_MII_SELECT, 0x1); /*0x1c18*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SEL_MII_ON_RGMII, 0x0); /*0x1c1c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, + 0x1); /*0x1c38*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL0, 0x0); /*0x1c80*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL1, 0x0); /*0x1c84*/ + } + } + else if instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_RMII + { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + } + + FXmacHighSpeedConfiguration(instance_p, speed); + } + + fn FXmacHighSpeedConfiguration(instance_p: &mut FXmac, speed: u32) +{ + let reg_value: mut u32 = 0; + let set_speed: mut i32 = 0; + match speed + { + FXMAC_SPEED_25000 => { + set_speed = 2; + } + FXMAC_SPEED_10000 => { + set_speed = 4; + } + FXMAC_SPEED_5000 => { + set_speed = 3; + } + FXMAC_SPEED_2500 => { + set_speed = 2; + } + FXMAC_SPEED_1000 => { + set_speed = 1; + } + _ => { + set_speed = 0; + } + } + + /*GEM_HSMAC(0x0050) provide rate to the external*/ + reg_value = read_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC) as *const u32); + reg_value &= !FXMAC_GEM_HSMACSPEED_MASK; + reg_value |= (set_speed) & FXMAC_GEM_HSMACSPEED_MASK; + write_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC) as *mut u32, reg_value); + + reg_value = read_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC) as *const u32); + + info!("FXMAC_GEM_HSMAC is {:#x}", reg_value); +} + + +/// FXmacInitInterface +/// Initialize the MAC controller configuration based on the PHY interface type + fn FXmacInitInterface(instance_p: &mut FXmac) + { + let mut config: u32 = 0, + let mut control: u32 = 0; + + info!("FXmacInitInterface, PHY MODE:{}", instance_p.config.interface); + + if (instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_XGMII ) + { + config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + config &= !FXMAC_NWCFG_PCSSEL_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); + + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control |= FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + + instance_p.config.duplex = 1; + } + else if (instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_USXGMII || instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_5GBASER) + { + info!("usx interface is {}",instance_p.config.interface); + /* network_config */ + instance_p.config.duplex = 1; + config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + config |= FXMAC_NWCFG_PCSSEL_MASK; + config &= !FXMAC_NWCFG_100_MASK; + config &= !FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; + if (instance_p.config.duplex == 1) + { + info!("is duplex"); + config |= FXMAC_NWCFG_FDEN_MASK; + } + + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); + + /* network_control */ + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control |= FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + + + /* High speed PCS control register */ + control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); + + if (instance_p.config.speed == FXMAC_SPEED_10000) + { + info!("is 10G"); + control |= FXMAC_GEM_USX_HS_MAC_SPEED_10G; + control |= FXMAC_GEM_USX_SERDES_RATE_10G; + } + else if (instance_p.config.speed == FXMAC_SPEED_25000) + { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_2_5G; + } + else if (instance_p.config.speed == FXMAC_SPEED_1000) + { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_1G; + } + else if (instance_p.config.speed == FXMAC_SPEED_100) + { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_100M; + } + else if(instance_p.config.speed == FXMAC_SPEED_5000) + { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_5G; + control |= FXMAC_GEM_USX_SERDES_RATE_5G; + } + + control &= !(FXMAC_GEM_USX_TX_SCR_BYPASS | FXMAC_GEM_USX_RX_SCR_BYPASS); + control |= FXMAC_GEM_USX_RX_SYNC_RESET; + write_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, control); + + control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); + control &= !FXMAC_GEM_USX_RX_SYNC_RESET; + control |= FXMAC_GEM_USX_TX_DATAPATH_EN; + control |= FXMAC_GEM_USX_SIGNAL_OK; + + write_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, control); + + } + else if(instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_2500BASEX) + { + /* network_config */ + instance_p.config.duplex = 1; + config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + config |= FXMAC_NWCFG_PCSSEL_MASK | FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; + config &= !FXMAC_NWCFG_100_MASK; + + if (instance_p.config.duplex == 1) + { + config |= FXMAC_NWCFG_FDEN_MASK; + } + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); + + /* network_control */ + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control &= !FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; + control |= FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; /* Use high speed MAC */ + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + + /* High speed PCS control register */ + control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); + + if (instance_p.config.speed == FXMAC_SPEED_25000) + { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_2_5G; + } + + control &= !(FXMAC_GEM_USX_TX_SCR_BYPASS | FXMAC_GEM_USX_RX_SCR_BYPASS); + control |= FXMAC_GEM_USX_RX_SYNC_RESET; + write_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, control); + + control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); + control &= !FXMAC_GEM_USX_RX_SYNC_RESET; + control |= FXMAC_GEM_USX_TX_DATAPATH_EN; + control |= FXMAC_GEM_USX_SIGNAL_OK; + + write_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, control); + + } + else if (instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_SGMII) + { + config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + config |= FXMAC_NWCFG_PCSSEL_MASK | FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; + + config &= !(FXMAC_NWCFG_100_MASK | FXMAC_NWCFG_FDEN_MASK|FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK); + + if instance_p->moudle_id >= 2 + { + config &= !FXMAC_NWCFG_1000_MASK; + } + + if instance_p.config.duplex != 0 + { + config |= FXMAC_NWCFG_FDEN_MASK; + } + + if instance_p.config.speed == FXMAC_SPEED_100 + { + config |= FXMAC_NWCFG_100_MASK; + } + else if instance_p.config.speed == FXMAC_SPEED_1000 + { + config |= FXMAC_NWCFG_1000_MASK; + } + + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); + + if instance_p.config.speed == FXMAC_SPEED_2500 + { + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control |= FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + } + else + { + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control &= !FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + } + + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control &= !FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + + control = read_reg((FXMAC_IOBASE + FXMAC_PCS_CONTROL_OFFSET) as *const u32); + control |= FXMAC_PCS_CONTROL_ENABLE_AUTO_NEG; + write_reg((FXMAC_IOBASE + FXMAC_PCS_CONTROL_OFFSET) as *mut u32, control); + } + else + { + config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + + info!("select rgmii"); + + config &= !FXMAC_NWCFG_PCSSEL_MASK; + config &= !(FXMAC_NWCFG_100_MASK | FXMAC_NWCFG_FDEN_MASK); + + if instance_p->moudle_id >= 2 + { + config &= !FXMAC_NWCFG_1000_MASK; + } + + if instance_p.config.duplex != 0 + { + config |= FXMAC_NWCFG_FDEN_MASK; + } + + if instance_p.config.speed == FXMAC_SPEED_100 + { + config |= FXMAC_NWCFG_100_MASK; + } + else if instance_p.config.speed == FXMAC_SPEED_1000 + { + config |= FXMAC_NWCFG_1000_MASK; + } + + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); + + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control &= !FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + } + } diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs new file mode 100644 index 000000000..ca1478e6a --- /dev/null +++ b/src/fxmac_dma.rs @@ -0,0 +1,202 @@ + + + +FError FXmacInitDma(FXmacLwipPort *instance_p) +{ + FXmacBd bdtemplate; + FXmacBdRing *rxringptr, *txringptr; + FXmacBd *rxbd; + struct pbuf *p; + FError status; + int i; + u32 bdindex = 0; + u32 *temp; + + FXMAC_LWIP_PORT_XMAC_PRINT_I("%s,\r\n", __func__); + + /* + * The BDs need to be allocated in uncached memory. Hence the 1 MB + * address range allocated for Bd_Space is made uncached + * by setting appropriate attributes in the translation table. + * The Bd_Space is aligned to 1MB and has a size of 1 MB. This ensures + * a reserved uncached area used only for BDs. + */ + + rxringptr = &FXMAC_GET_RXRING(instance_p->instance); + txringptr = &FXMAC_GET_TXRING(instance_p->instance); + FXMAC_LWIP_PORT_XMAC_PRINT_I("rxringptr: 0x%08x\r\n", rxringptr); + FXMAC_LWIP_PORT_XMAC_PRINT_I("txringptr: 0x%08x\r\n", txringptr); + + FXMAC_LWIP_PORT_XMAC_PRINT_I("rx_bdspace: %p \r\n", instance_p->buffer.rx_bdspace); + FXMAC_LWIP_PORT_XMAC_PRINT_I("tx_bdspace: %p \r\n", instance_p->buffer.tx_bdspace); + + /* Setup RxBD space. */ + FXMAC_BD_CLEAR(&bdtemplate); + + /* Create the RxBD ring */ + status = FXmacBdRingCreate(rxringptr, (uintptr)instance_p->buffer.rx_bdspace, + (uintptr)instance_p->buffer.rx_bdspace, BD_ALIGNMENT, + FXMAX_RX_PBUFS_LENGTH); + + if (status != FT_SUCCESS) + { + FXMAC_LWIP_PORT_XMAC_PRINT_E("Error setting up RxBD space\r\n"); + return ERR_IF; + } + + status = FXmacBdRingClone(rxringptr, &bdtemplate, FXMAC_RECV); + if (status != FT_SUCCESS) + { + FXMAC_LWIP_PORT_XMAC_PRINT_E("Error initializing RxBD space\r\n"); + return ERR_IF; + } + + FXMAC_BD_CLEAR(&bdtemplate); + FXMAC_BD_SET_STATUS(&bdtemplate, FXMAC_TXBUF_USED_MASK); + + /* Create the TxBD ring */ + status = FXmacBdRingCreate(txringptr, (uintptr)instance_p->buffer.tx_bdspace, + (uintptr)instance_p->buffer.tx_bdspace, BD_ALIGNMENT, + FXMAX_TX_PBUFS_LENGTH); + + if (status != FT_SUCCESS) + { + return ERR_IF; + } + + /* We reuse the bd template, as the same one will work for both rx and tx. */ + status = FXmacBdRingClone(txringptr, &bdtemplate, FXMAC_SEND); + if (status != FT_SUCCESS) + { + return ERR_IF; + } + + /* + * Allocate RX descriptors, 1 RxBD at a time. + */ + FXMAC_LWIP_PORT_XMAC_PRINT_I("Allocate RX descriptors, 1 RxBD at a time."); + for (i = 0; i < FXMAX_RX_PBUFS_LENGTH; i++) + { + if (instance_p->feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) + { + p = pbuf_alloc(PBUF_RAW, FXMAC_MAX_FRAME_SIZE_JUMBO, PBUF_RAM); + } + else + { + p = pbuf_alloc(PBUF_RAW, FXMAC_MAX_FRAME_SIZE, PBUF_POOL); + } + + if (!p) + { +#if LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif + FXMAC_LWIP_PORT_XMAC_PRINT_E("unable to alloc pbuf in InitDma\r\n"); + return ERR_IF; + } + status = FXmacBdRingAlloc(rxringptr, 1, &rxbd); + if (status != FT_SUCCESS) + { + FXMAC_LWIP_PORT_XMAC_PRINT_E("InitDma: Error allocating RxBD\r\n"); + pbuf_free(p); + return ERR_IF; + } + /* Enqueue to HW */ + status = FXmacBdRingToHw(rxringptr, 1, rxbd); + if (status != FT_SUCCESS) + { + FXMAC_LWIP_PORT_XMAC_PRINT_E("Error: committing RxBD to HW\r\n"); + pbuf_free(p); + FXmacBdRingUnAlloc(rxringptr, 1, rxbd); + return ERR_IF; + } + + bdindex = FXMAC_BD_TO_INDEX(rxringptr, rxbd); + temp = (u32 *)rxbd; + *temp = 0; + if (bdindex == (FXMAX_RX_PBUFS_LENGTH - 1)) + { + *temp = 0x00000002; /* Marks last descriptor in receive buffer descriptor list */ + } + temp++; + *temp = 0; /* Clear word 1 in descriptor */ + DSB(); + + if (instance_p->feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) + { + FCacheDCacheInvalidateRange((uintptr)p->payload, (uintptr)FXMAC_MAX_FRAME_SIZE_JUMBO); + } + else + { + FCacheDCacheInvalidateRange((uintptr)p->payload, (uintptr)FXMAC_MAX_FRAME_SIZE); + } + FXMAC_BD_SET_ADDRESS_RX(rxbd, (uintptr)p->payload); + + instance_p->buffer.rx_pbufs_storage[bdindex] = (uintptr)p; + } + + FXmacSetQueuePtr(&(instance_p->instance), instance_p->instance.tx_bd_queue.bdring.phys_base_addr, 0, (u16)FXMAC_SEND); + FXmacSetQueuePtr(&(instance_p->instance), instance_p->instance.rx_bd_queue.bdring.phys_base_addr, 0, (u16)FXMAC_RECV); + + if ((instance_p->instance).config.caps & FXMAC_CAPS_TAILPTR) + { + FXMAC_WRITEREG32((instance_p->instance).config.base_address, FXMAC_TAIL_QUEUE(0), BIT(31)|0); + } + + return 0; +} + +/* Reset Tx and Rx DMA pointers after FXmacStop */ +void ResetDma(FXmacLwipPort *instance_p) +{ + FXMAC_LWIP_PORT_XMAC_PRINT_I("%s,\r\n", __func__); + + FXmacBdRing *txringptr = &FXMAC_GET_TXRING(instance_p->instance); + FXmacBdRing *rxringptr = &FXMAC_GET_RXRING(instance_p->instance); + + FXmacBdringPtrReset(txringptr, instance_p->buffer.tx_bdspace); + FXmacBdringPtrReset(rxringptr, instance_p->buffer.rx_bdspace); + + FXmacSetQueuePtr(&(instance_p->instance), instance_p->instance.tx_bd_queue.bdring.phys_base_addr, 0, (u16)FXMAC_SEND); + FXmacSetQueuePtr(&(instance_p->instance), instance_p->instance.rx_bd_queue.bdring.phys_base_addr, 0, (u16)FXMAC_RECV); +} + + +static void FXmacSetupIsr(FXmacLwipPort *instance_p) +{ + FXMAC_LWIP_PORT_XMAC_PRINT_I("%s,\r\n", __func__); + + u32 cpu_id; + GetCpuId(&cpu_id); + InterruptSetTargetCpus(instance_p->instance.config.queue_irq_num[0], cpu_id); + /* Setup callbacks */ + FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_DMASEND, FXmacSendHandler, instance_p); + FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_DMARECV, FXmacRecvIsrHandler, instance_p); + FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_ERROR, FXmacErrorHandler, instance_p); + FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_LINKCHANGE, FXmacLinkChange, instance_p); + + InterruptSetPriority(instance_p->instance.config.queue_irq_num[0], IRQ_PRIORITY_VALUE_12); + // setup interrupt handler + InterruptInstall(instance_p->instance.config.queue_irq_num[0], FXmacLwipPortIntrHandler, &instance_p->instance, "fxmac"); + InterruptUmask(instance_p->instance.config.queue_irq_num[0]); +} + +void CleanDmaTxdescs(FXmacLwipPort *instance_p) +{ + FXmacBd bdtemplate; + FXmacBdRing *txringptr; + + FXMAC_LWIP_PORT_XMAC_PRINT_I("%s,\r\n", __func__); + + txringptr = &FXMAC_GET_TXRING((instance_p->instance)); + FXMAC_BD_CLEAR(&bdtemplate); + FXMAC_BD_SET_STATUS(&bdtemplate, FXMAC_TXBUF_USED_MASK); + + FXmacBdRingCreate(txringptr, (uintptr)instance_p->buffer.tx_bdspace, + (uintptr)instance_p->buffer.tx_bdspace, BD_ALIGNMENT, + sizeof(instance_p->buffer.tx_bdspace)); + + FXmacBdRingClone(txringptr, &bdtemplate, FXMAC_SEND); +} + diff --git a/src/fxmac_phy.rs b/src/fxmac_phy.rs new file mode 100644 index 000000000..a9b58d46c --- /dev/null +++ b/src/fxmac_phy.rs @@ -0,0 +1,411 @@ +/* + * Write data to the specified PHY register. The Ethernet driver does not + * require the device to be stopped before writing to the PHY. Although it is + * probably a good idea to stop the device, it is the responsibility of the + * application to deem this necessary. The MAC provides the driver with the + * ability to talk to a PHY that adheres to the Media Independent Interface + * (MII) as defined in the IEEE 802.3 standard. + * + * Prior to PHY access with this function, the user should have setup the MDIO + * clock with FXmacSetMdioDivisor(). + */ +fn FXmacPhyWrite(instance_p &mut FXmac, phy_address: u32, register_num: u32, phy_data: u16) -> u32 +{ + let mgtcr: mut u32 = 0; + let ipisr: mut u32 = 0; + let ip_write_temp: mut u32 = 0; + let status: mut u32 = 0; + + debug!("FXmacPhyWrite, phy_address={:#x}, register_num={}, phy_data={:#x}", phy_address, register_num, phy_data); + + /* Make sure no other PHY operation is currently in progress */ + if (!(read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32) & + FXMAC_NWSR_MDIOIDLE_MASK)) == 1 + { + status = FXMAC_ERR_PHY_BUSY; + error!("FXmacPhyRead error: PHY busy!"); + }else{ + /* Construct mgtcr mask for the operation */ + mgtcr = FXMAC_PHYMNTNC_OP_MASK | FXMAC_PHYMNTNC_OP_W_MASK | + (phy_address << FXMAC_PHYMNTNC_PHAD_SHFT_MSK) | + (register_num << FXMAC_PHYMNTNC_PREG_SHFT_MSK) | phy_data as u32; + + /* Write mgtcr and wait for completion */ + write_reg((instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *mut u32, mgtcr); + + loop{ + ipisr = read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32); + ip_write_temp = ipisr; + + if !(ip_write_temp & FXMAC_NWSR_MDIOIDLE_MASK) == 0 { + break; + } + } + + status = FT_SUCCESS; + } + + status +} + +fn FXmacPhyRead(instance_p &mut FXmac, phy_address: u32, register_num: u32, phydat_aptr: &mut u16) -> u32 +{ + let mgtcr: mut u32 = 0; + let ipisr: mut u32 = 0; + let IpReadTemp: mut u32 = 0; + let status: mut u32 = 0; + + /* Make sure no other PHY operation is currently in progress */ + if (!(read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32) & FXMAC_NWSR_MDIOIDLE_MASK)) == 1 + { + status = FXMAC_ERR_PHY_BUSY; + error!("FXmacPhyRead error: PHY busy!"); + }else{ + /* Construct mgtcr mask for the operation */ + mgtcr = FXMAC_PHYMNTNC_OP_MASK | FXMAC_PHYMNTNC_OP_R_MASK | + (phy_address << FXMAC_PHYMNTNC_PHAD_SHFT_MSK) | + (register_num << FXMAC_PHYMNTNC_PREG_SHFT_MSK); + + /* Write mgtcr and wait for completion */ + write_reg((instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *mut u32, mgtcr); + + loop{ + ipisr = read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32); + IpReadTemp = ipisr; + + if !(IpReadTemp & FXMAC_NWSR_MDIOIDLE_MASK) == 0 { + break; + } + } + + // Read data + phydat_aptr = read_reg((instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *const u32) as u16; + debug!("FXmacPhyRead, phy_address={:#x}, register_num={}, phydat_aptr={:#x}", phy_address, register_num, phydat_aptr); + + status = FT_SUCCESS; + } + + status +} + + +/// FXmacPhyInit +/// Setup the PHYs for proper speed setting. +pub fn FXmacPhyInit( + mut instance_p: &mut FXmac, + mut speed: u32, + mut duplex_mode: u32, + mut autonegotiation_en: u32, + mut reset_flag: u32, +) -> u32 { + info!("FXmacPhyInit, speed={}, duplex_mode={}, autonegotiation_en={}, reset_flag={}", + duplex_mode, + autonegotiation_en, + reset_flag, + ); + let mut ret: u32 = 0; + let mut phy_addr: u32 = 0; + if FXmacDetect(instance_p, &mut phy_addr) != 0 { + error!("Phy is not found."); + return FXMAC_PHY_IS_NOT_FOUND; + } + info!("Setting phy addr is {}", phy_addr); + instance_p.phy_address = phy_addr; + if reset_flag != 0 { + FXmacPhyReset(instance_p, phy_addr); + } + if autonegotiation_en != 0 { + ret = FXmacGetIeeePhySpeed(instance_p, phy_addr); + if ret != 0 { + return ret; + } + } else { + info!("Set the communication speed manually."); + assert!(speed != FXMAC_SPEED_1000, "The speed must be 100M or 10M!"); + + ret = FXmacConfigureIeeePhySpeed(instance_p, phy_addr, speed, duplex_mode); + if ret != 0 { + error!("Failed to manually set the phy."); + return ret; + } + } + instance_p.link_status = FXMAC_LINKUP; + + FT_SUCCESS +} + +fn FXmacDetect(instance_p: &mut FXmac, phy_addr_p: &mut u32) -> u32 +{ + let phy_addr: mut u32 = 0; + let phy_reg: mut u16 = 0; + let phy_id1_reg: mut u16 = 0; + let phy_id2_reg: mut u16 = 0; + + for phy_addr in 0..FXMAC_PHY_MAX_NUM + { + let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &mut phy_reg); + if (ret != FT_SUCCESS) + { + error!("Phy operation is busy."); + return ret; + } + info!("Phy status reg is {:#x}", phy_reg); + + if (phy_reg != 0xffff) + { + ret = FXmacPhyRead(instance_p, phy_addr, PHY_IDENTIFIER_1_REG, &phy_id1_reg); + ret |= FXmacPhyRead(instance_p, phy_addr, PHY_IDENTIFIER_2_REG, &phy_id2_reg); + info!("Phy id1 reg is {:#x}, Phy id2 reg is {:#x}", phy_id1_reg , phy_id2_reg); + + if ((ret == FT_SUCCESS) && (phy_id2_reg != 0) && (phy_id2_reg != 0xffff) && (phy_id1_reg != 0xffff)) + { + *phy_addr_p = phy_addr; + phy_addr_b = phy_addr; + info!("Phy addr is {:#x}", phy_addr); + return FT_SUCCESS; + } + } + } + + FXMAC_PHY_IS_NOT_FOUND +} + +/// FXmacPhyReset: Perform phy software reset + fn FXmacPhyReset(instance_p: &mut FXmac, phy_addr: u32) -> u32 + { + let control: mut u16 = 0; + + let ret: mut u32 = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + if (ret != FT_SUCCESS) + { + error!("FXmacPhyReset, read PHY_CONTROL_REG_OFFSET is error"); + return ret; + } + + control |= PHY_CONTROL_RESET_MASK; + + ret = FXmacPhyWrite(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, control); + if (ret != FT_SUCCESS) + { + error!("FXmacPhyReset, write PHY_CONTROL_REG_OFFSET is error"); + return ret; + } + + loop + { + ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + if (ret != FT_SUCCESS) + { + error!("FXmacPhyReset, read PHY_CONTROL_REG_OFFSET is error"); + return ret; + } + if (control & PHY_CONTROL_RESET_MASK) == 0 { + break; + } + } + + info!("Phy reset end."); + ret + } + + +fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 +{ + let temp: mut u16 = 0; + let temp2: mut u16 = 0; + let control: mut u16 = 0; + let status: mut u16 = 0; + let negotitation_timeout_cnt: mut u32 = 0; + + info!("Start phy auto negotiation."); + + let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + if (ret != FT_SUCCESS) + { + error!("FXmacGetIeeePhySpeed,read PHY_CONTROL_REG_OFFSET is error"); + return ret; + } + + control |= PHY_CONTROL_AUTONEGOTIATE_ENABLE; + control |= PHY_CONTROL_AUTONEGOTIATE_RESTART; + ret = FXmacPhyWrite(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, control); + if (ret != FT_SUCCESS) + { + error!("FXmacGetIeeePhySpeed,write PHY_CONTROL_REG_OFFSET is error"); + return ret; + } + + info!("Waiting for phy to complete auto negotiation."); + do{ + FDriverMdelay(50); + + ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &status); + if (ret != FT_SUCCESS) + { + error!("FXmacGetIeeePhySpeed,read PHY_STATUS_REG_OFFSET is error"); + return ret; + } + + + if (negotitation_timeout_cnt++ >= 0xff) + { + error!("Auto negotiation is error."); + return FXMAC_PHY_AUTO_AUTONEGOTIATION_FAILED; + } + + if (status & PHY_STATUS_AUTONEGOTIATE_COMPLETE) != 0 { + break + } + } + + info!("Auto negotiation complete."); + + ret = FXmacPhyRead(instance_p, phy_addr, PHY_SPECIFIC_STATUS_REG, &temp); + if (ret != FT_SUCCESS) + { + error!("FXmacGetIeeePhySpeed,read PHY_SPECIFIC_STATUS_REG is error"); + return ret; + } + + info!("Temp is {:#x}", temp); + ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &temp2); + if (ret != FT_SUCCESS) + { + error!("FXmacGetIeeePhySpeed,read PHY_STATUS_REG_OFFSET is error"); + return ret; + } + + info!("Temp2 is {:#x}", temp2); + + if (temp & (1 << 13)) != 0 + { + info!("Duplex is full."); + instance_p.config.duplex = 1; + } + else + { + info!("Duplex is half."); + instance_p.config.duplex = 0; + } + + if (temp & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_1000M + { + info!("Speed is 1000M."); + instance_p.config.speed = 1000; + } + else if (temp & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_100M + { + info!("Speed is 100M."); + instance_p.config.speed = 100; + } + else + { + info!("Speed is 10M."); + instance_p.config.speed = 10; + } + + FT_SUCCESS +} + +fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: u32, duplex_mode: u32) -> u32 +{ + let control: mut u16 = 0;; + let autonereg: mut u16 = 0;; + let specific_reg: mut u16 = 0; + + info("Manual setting, phy_addr is {:#x},speed {}, duplex_mode is {}.", phy_addr, speed, duplex_mode); + + let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_AUTONEGO_ADVERTISE_REG, &autonereg); + if (ret != FT_SUCCESS) + { + error!("FXmacConfigureIeeePhySpeed, read PHY_AUTONEGO_ADVERTISE_REG is error."); + return ret; + } + + autonereg |= PHY_AUTOADVERTISE_ASYMMETRIC_PAUSE_MASK; + autonereg |= PHY_AUTOADVERTISE_PAUSE_MASK; + ret = FXmacPhyWrite(instance_p, phy_addr, PHY_AUTONEGO_ADVERTISE_REG, autonereg); + if (ret != FT_SUCCESS) + { + error!("FXmacConfigureIeeePhySpeed, write PHY_AUTONEGO_ADVERTISE_REG is error."); + return ret; + } + + ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + if (ret != FT_SUCCESS) + { + error!("FXmacConfigureIeeePhySpeed, read PHY_AUTONEGO_ADVERTISE_REG is error."); + return ret; + } + info!("PHY_CONTROL_REG_OFFSET is {:#x}.", control); + + control &= !PHY_CONTROL_LINKSPEED_1000M; + control &= !PHY_CONTROL_LINKSPEED_100M; + control &= !PHY_CONTROL_LINKSPEED_10M; + + if speed == 100 + { + control |= PHY_CONTROL_LINKSPEED_100M; + } + else if speed == 10 + { + control |= PHY_CONTROL_LINKSPEED_10M; + } + + if duplex_mode == 1 + { + control |= PHY_CONTROL_FULL_DUPLEX_MASK; + } + else + { + control &= !PHY_CONTROL_FULL_DUPLEX_MASK; + } + + /* disable auto-negotiation */ + control &= !PHY_CONTROL_AUTONEGOTIATE_ENABLE; + control &= !PHY_CONTROL_AUTONEGOTIATE_RESTART; + + ret = FXmacPhyWrite(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, control); /* Technology Ability Field */ + if (ret != FT_SUCCESS) + { + error!("FXmacConfigureIeeePhySpeed, write PHY_AUTONEGO_ADVERTISE_REG is error."); + return ret; + } + + FDriverMdelay(1500); + + info!("Manual selection completed."); + + ret = FXmacPhyRead(instance_p, phy_addr, PHY_SPECIFIC_STATUS_REG, &specific_reg); + if (ret != FT_SUCCESS) + { + error!("FXmacConfigureIeeePhySpeed, read PHY_SPECIFIC_STATUS_REG is error."); + return ret; + } + + info!("Specific reg is 0x%x.", specific_reg); + + if (specific_reg & (1 << 13)) != 0 + { + info!("Duplex is full."); + instance_p.config.duplex = 1; + } + else + { + info!("Duplex is half."); + instance_p.config.duplex = 0; + } + + if (specific_reg & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_100M + { + info!("Speed is 100M."); + instance_p.config.speed = 100; + } + else + { + info!("Speed is 10M."); + instance_p.config.speed = 10; + } + + FT_SUCCESS +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..10aa79ccf --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,23 @@ +#![no_std] +#![feature(linkage)] +#![allow(dead_code)] +#![allow(unused)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +extern crate alloc; + +mod mii_const; +mod macb_const; + +mod fxmac_phy; +mod fxmac_dma; +pub mod fxmac; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + } +} diff --git a/src/macb_const.rs b/src/macb_const.rs new file mode 100644 index 000000000..454c8b8a4 --- /dev/null +++ b/src/macb_const.rs @@ -0,0 +1,894 @@ +//cadence/macb.h +//Atmel MACB Ethernet Controller driver + +pub(crate) const MACB_GREGS_NBR: u32 = 16; +pub(crate) const MACB_GREGS_VERSION: u32 = 2; +pub(crate) const MACB_MAX_QUEUES: u32 = 8; + +/* MACB register offsets */ +pub(crate) const MACB_NCR: u32 = 0x0000; /* Network Control */ +pub(crate) const MACB_NCFGR: u32 = 0x0004; /* Network Config */ +pub(crate) const MACB_NSR: u32 = 0x0008; /* Network Status */ +pub(crate) const MACB_TAR: u32 = 0x000c; /* AT91RM9200 only */ +pub(crate) const MACB_TCR: u32 = 0x0010; /* AT91RM9200 only */ +pub(crate) const MACB_TSR: u32 = 0x0014; /* Transmit Status */ +pub(crate) const MACB_RBQP: u32 = 0x0018; /* RX Q Base Address */ +pub(crate) const MACB_TBQP: u32 = 0x001c; /* TX Q Base Address */ +pub(crate) const MACB_RSR: u32 = 0x0020; /* Receive Status */ +pub(crate) const MACB_ISR: u32 = 0x0024; /* Interrupt Status */ +pub(crate) const MACB_IER: u32 = 0x0028; /* Interrupt Enable */ +pub(crate) const MACB_IDR: u32 = 0x002c; /* Interrupt Disable */ +pub(crate) const MACB_IMR: u32 = 0x0030; /* Interrupt Mask */ +pub(crate) const MACB_MAN: u32 = 0x0034; /* PHY Maintenance */ +pub(crate) const MACB_PTR: u32 = 0x0038; +pub(crate) const MACB_PFR: u32 = 0x003c; +pub(crate) const MACB_FTO: u32 = 0x0040; +pub(crate) const MACB_SCF: u32 = 0x0044; +pub(crate) const MACB_MCF: u32 = 0x0048; +pub(crate) const MACB_FRO: u32 = 0x004c; +pub(crate) const MACB_FCSE: u32 = 0x0050; +pub(crate) const MACB_ALE: u32 = 0x0054; +pub(crate) const MACB_DTF: u32 = 0x0058; +pub(crate) const MACB_LCOL: u32 = 0x005c; +pub(crate) const MACB_EXCOL: u32 = 0x0060; +pub(crate) const MACB_TUND: u32 = 0x0064; +pub(crate) const MACB_CSE: u32 = 0x0068; +pub(crate) const MACB_RRE: u32 = 0x006c; +pub(crate) const MACB_ROVR: u32 = 0x0070; +pub(crate) const MACB_RSE: u32 = 0x0074; +pub(crate) const MACB_ELE: u32 = 0x0078; +pub(crate) const MACB_RJA: u32 = 0x007c; +pub(crate) const MACB_USF: u32 = 0x0080; +pub(crate) const MACB_STE: u32 = 0x0084; +pub(crate) const MACB_RLE: u32 = 0x0088; +pub(crate) const MACB_TPF: u32 = 0x008c; +pub(crate) const MACB_HRB: u32 = 0x0090; +pub(crate) const MACB_HRT: u32 = 0x0094; +pub(crate) const MACB_SA1B: u32 = 0x0098; +pub(crate) const MACB_SA1T: u32 = 0x009c; +pub(crate) const MACB_SA2B: u32 = 0x00a0; +pub(crate) const MACB_SA2T: u32 = 0x00a4; +pub(crate) const MACB_SA3B: u32 = 0x00a8; +pub(crate) const MACB_SA3T: u32 = 0x00ac; +pub(crate) const MACB_SA4B: u32 = 0x00b0; +pub(crate) const MACB_SA4T: u32 = 0x00b4; +pub(crate) const MACB_TID: u32 = 0x00b8; +pub(crate) const MACB_TPQ: u32 = 0x00bc; +pub(crate) const MACB_USRIO: u32 = 0x00c0; +pub(crate) const MACB_WOL: u32 = 0x00c4; +pub(crate) const MACB_MID: u32 = 0x00fc; +pub(crate) const MACB_TBQPH: u32 = 0x04C8; +pub(crate) const MACB_RBQPH: u32 = 0x04D4; + +/* GEM register offsets. */ +pub(crate) const GEM_NCR: u32 = 0x0000; /* Network Control */ +pub(crate) const GEM_NCFGR: u32 = 0x0004; /* Network Config */ +pub(crate) const GEM_USRIO: u32 = 0x000c; /* User IO */ +pub(crate) const GEM_DMACFG: u32 = 0x0010; /* DMA Configuration */ +pub(crate) const GEM_JML: u32 = 0x0048; /* Jumbo Max Length */ +pub(crate) const GEM_HS_MAC_CONFIG: u32 = 0x0050; /* GEM high speed config */ +pub(crate) const GEM_HRB: u32 = 0x0080; /* Hash Bottom */ +pub(crate) const GEM_HRT: u32 = 0x0084; /* Hash Top */ +pub(crate) const GEM_SA1B: u32 = 0x0088; /* Specific1 Bottom */ +pub(crate) const GEM_SA1T: u32 = 0x008C; /* Specific1 Top */ +pub(crate) const GEM_SA2B: u32 = 0x0090; /* Specific2 Bottom */ +pub(crate) const GEM_SA2T: u32 = 0x0094; /* Specific2 Top */ +pub(crate) const GEM_SA3B: u32 = 0x0098; /* Specific3 Bottom */ +pub(crate) const GEM_SA3T: u32 = 0x009C; /* Specific3 Top */ +pub(crate) const GEM_SA4B: u32 = 0x00A0; /* Specific4 Bottom */ +pub(crate) const GEM_SA4T: u32 = 0x00A4; /* Specific4 Top */ +pub(crate) const GEM_WOL: u32 = 0x00b8; /* Wake on LAN */ +pub(crate) const GEM_EFTSH: u32 = 0x00e8; /* PTP Event Frame Transmitted Seconds Register 47:32 */ +pub(crate) const GEM_EFRSH: u32 = 0x00ec; /* PTP Event Frame Received Seconds Register 47:32 */ +pub(crate) const GEM_PEFTSH: u32 = 0x00f0; /* PTP Peer Event Frame Transmitted Seconds Register 47:32 */ +pub(crate) const GEM_PEFRSH: u32 = 0x00f4; /* PTP Peer Event Frame Received Seconds Register 47:32 */ +pub(crate) const GEM_OTX: u32 = 0x0100; /* Octets transmitted */ +pub(crate) const GEM_OCTTXL: u32 = 0x0100; /* Octets transmitted [31:0] */ +pub(crate) const GEM_OCTTXH: u32 = 0x0104; /* Octets transmitted [47:32] */ +pub(crate) const GEM_TXCNT: u32 = 0x0108; /* Frames Transmitted counter */ +pub(crate) const GEM_TXBCCNT: u32 = 0x010c; /* Broadcast Frames counter */ +pub(crate) const GEM_TXMCCNT: u32 = 0x0110; /* Multicast Frames counter */ +pub(crate) const GEM_TXPAUSECNT: u32 = 0x0114; /* Pause Frames Transmitted Counter */ +pub(crate) const GEM_TX64CNT: u32 = 0x0118; /* 64 byte Frames TX counter */ +pub(crate) const GEM_TX65CNT: u32 = 0x011c; /* 65-127 byte Frames TX counter */ +pub(crate) const GEM_TX128CNT: u32 = 0x0120; /* 128-255 byte Frames TX counter */ +pub(crate) const GEM_TX256CNT: u32 = 0x0124; /* 256-511 byte Frames TX counter */ +pub(crate) const GEM_TX512CNT: u32 = 0x0128; /* 512-1023 byte Frames TX counter */ +pub(crate) const GEM_TX1024CNT: u32 = 0x012c; /* 1024-1518 byte Frames TX counter */ +pub(crate) const GEM_TX1519CNT: u32 = 0x0130; /* 1519+ byte Frames TX counter */ +pub(crate) const GEM_TXURUNCNT: u32 = 0x0134; /* TX under run error counter */ +pub(crate) const GEM_SNGLCOLLCNT: u32 = 0x0138; /* Single Collision Frame Counter */ +pub(crate) const GEM_MULTICOLLCNT: u32 = 0x013c; /* Multiple Collision Frame Counter */ +pub(crate) const GEM_EXCESSCOLLCNT: u32 = 0x0140; /* Excessive Collision Frame Counter */ +pub(crate) const GEM_LATECOLLCNT: u32 = 0x0144; /* Late Collision Frame Counter */ +pub(crate) const GEM_TXDEFERCNT: u32 = 0x0148; /* Deferred Transmission Frame Counter */ +pub(crate) const GEM_TXCSENSECNT: u32 = 0x014c; /* Carrier Sense Error Counter */ +pub(crate) const GEM_ORX: u32 = 0x0150; /* Octets received */ +pub(crate) const GEM_OCTRXL: u32 = 0x0150; /* Octets received [31:0] */ +pub(crate) const GEM_OCTRXH: u32 = 0x0154; /* Octets received [47:32] */ +pub(crate) const GEM_RXCNT: u32 = 0x0158; /* Frames Received Counter */ +pub(crate) const GEM_RXBROADCNT: u32 = 0x015c; /* Broadcast Frames Received Counter */ +pub(crate) const GEM_RXMULTICNT: u32 = 0x0160; /* Multicast Frames Received Counter */ +pub(crate) const GEM_RXPAUSECNT: u32 = 0x0164; /* Pause Frames Received Counter */ +pub(crate) const GEM_RX64CNT: u32 = 0x0168; /* 64 byte Frames RX Counter */ +pub(crate) const GEM_RX65CNT: u32 = 0x016c; /* 65-127 byte Frames RX Counter */ +pub(crate) const GEM_RX128CNT: u32 = 0x0170; /* 128-255 byte Frames RX Counter */ +pub(crate) const GEM_RX256CNT: u32 = 0x0174; /* 256-511 byte Frames RX Counter */ +pub(crate) const GEM_RX512CNT: u32 = 0x0178; /* 512-1023 byte Frames RX Counter */ +pub(crate) const GEM_RX1024CNT: u32 = 0x017c; /* 1024-1518 byte Frames RX Counter */ +pub(crate) const GEM_RX1519CNT: u32 = 0x0180; /* 1519+ byte Frames RX Counter */ +pub(crate) const GEM_RXUNDRCNT: u32 = 0x0184; /* Undersize Frames Received Counter */ +pub(crate) const GEM_RXOVRCNT: u32 = 0x0188; /* Oversize Frames Received Counter */ +pub(crate) const GEM_RXJABCNT: u32 = 0x018c; /* Jabbers Received Counter */ +pub(crate) const GEM_RXFCSCNT: u32 = 0x0190; /* Frame Check Sequence Error Counter */ +pub(crate) const GEM_RXLENGTHCNT: u32 = 0x0194; /* Length Field Error Counter */ +pub(crate) const GEM_RXSYMBCNT: u32 = 0x0198; /* Symbol Error Counter */ +pub(crate) const GEM_RXALIGNCNT: u32 = 0x019c; /* Alignment Error Counter */ +pub(crate) const GEM_RXRESERRCNT: u32 = 0x01a0; /* Receive Resource Error Counter */ +pub(crate) const GEM_RXORCNT: u32 = 0x01a4; /* Receive Overrun Counter */ +pub(crate) const GEM_RXIPCCNT: u32 = 0x01a8; /* IP header Checksum Error Counter */ +pub(crate) const GEM_RXTCPCCNT: u32 = 0x01ac; /* TCP Checksum Error Counter */ +pub(crate) const GEM_RXUDPCCNT: u32 = 0x01b0; /* UDP Checksum Error Counter */ +pub(crate) const GEM_TISUBN: u32 = 0x01bc; /* 1588 Timer Increment Sub-ns */ +pub(crate) const GEM_TSH: u32 = 0x01c0; /* 1588 Timer Seconds High */ +pub(crate) const GEM_TSL: u32 = 0x01d0; /* 1588 Timer Seconds Low */ +pub(crate) const GEM_TN: u32 = 0x01d4; /* 1588 Timer Nanoseconds */ +pub(crate) const GEM_TA: u32 = 0x01d8; /* 1588 Timer Adjust */ +pub(crate) const GEM_TI: u32 = 0x01dc; /* 1588 Timer Increment */ +pub(crate) const GEM_EFTSL: u32 = 0x01e0; /* PTP Event Frame Tx Seconds Low */ +pub(crate) const GEM_EFTN: u32 = 0x01e4; /* PTP Event Frame Tx Nanoseconds */ +pub(crate) const GEM_EFRSL: u32 = 0x01e8; /* PTP Event Frame Rx Seconds Low */ +pub(crate) const GEM_EFRN: u32 = 0x01ec; /* PTP Event Frame Rx Nanoseconds */ +pub(crate) const GEM_PEFTSL: u32 = 0x01f0; /* PTP Peer Event Frame Tx Secs Low */ +pub(crate) const GEM_PEFTN: u32 = 0x01f4; /* PTP Peer Event Frame Tx Ns */ +pub(crate) const GEM_PEFRSL: u32 = 0x01f8; /* PTP Peer Event Frame Rx Sec Low */ +pub(crate) const GEM_PEFRN: u32 = 0x01fc; /* PTP Peer Event Frame Rx Ns */ +pub(crate) const GEM_PCSCNTRL: u32 = 0x0200; /* PCS Control */ +pub(crate) const GEM_PCSSTS: u32 = 0x0204; /* PCS Status */ +pub(crate) const GEM_PCSPHYTOPID: u32 = 0x0208; /* PCS PHY Top ID */ +pub(crate) const GEM_PCSPHYBOTID: u32 = 0x020c; /* PCS PHY Bottom ID */ +pub(crate) const GEM_PCSANADV: u32 = 0x0210; /* PCS AN Advertisement */ +pub(crate) const GEM_PCSANLPBASE: u32 = 0x0214; /* PCS AN Link Partner Base */ +pub(crate) const GEM_PCSANEXP: u32 = 0x0218; /* PCS AN Expansion */ +pub(crate) const GEM_PCSANNPTX: u32 = 0x021c; /* PCS AN Next Page TX */ +pub(crate) const GEM_PCSANNPLP: u32 = 0x0220; /* PCS AN Next Page LP */ +pub(crate) const GEM_PCSANEXTSTS: u32 = 0x023c; /* PCS AN Extended Status */ +pub(crate) const GEM_DCFG1: u32 = 0x0280; /* Design Config 1 */ +pub(crate) const GEM_DCFG2: u32 = 0x0284; /* Design Config 2 */ +pub(crate) const GEM_DCFG3: u32 = 0x0288; /* Design Config 3 */ +pub(crate) const GEM_DCFG4: u32 = 0x028c; /* Design Config 4 */ +pub(crate) const GEM_DCFG5: u32 = 0x0290; /* Design Config 5 */ +pub(crate) const GEM_DCFG6: u32 = 0x0294; /* Design Config 6 */ +pub(crate) const GEM_DCFG7: u32 = 0x0298; /* Design Config 7 */ +pub(crate) const GEM_DCFG8: u32 = 0x029C; /* Design Config 8 */ +pub(crate) const GEM_DCFG10: u32 = 0x02A4; /* Design Config 10 */ +pub(crate) const GEM_DCFG12: u32 = 0x02AC; /* Design Config 12 */ +pub(crate) const GEM_USX_CONTROL: u32 = 0x0A80; /* High speed PCS control register */ +pub(crate) const GEM_USX_STATUS: u32 = 0x0A88; /* High speed PCS status register */ + +pub(crate) const GEM_TXBDCTRL: u32 = 0x04cc; /* TX Buffer Descriptor control register */ +pub(crate) const GEM_RXBDCTRL: u32 = 0x04d0; /* RX Buffer Descriptor control register */ + +/* Screener Type 2 match registers */ +pub(crate) const GEM_SCRT2: u32 = 0x540; + +/* EtherType registers */ +pub(crate) const GEM_ETHT: u32 = 0x06E0; + +/* Type 2 compare registers */ +pub(crate) const GEM_T2CMPW0: u32 = 0x0700; +pub(crate) const GEM_T2CMPW1: u32 = 0x0704; +//pub(crate) const T2CMP_OFST(t2idx) (t2idx * 2) + +/* +/* type 2 compare registers + * each location requires 3 compare regs + */ +pub(crate) const GEM_IP4SRC_CMP(idx) (idx * 3) +pub(crate) const GEM_IP4DST_CMP(idx) (idx * 3 + 1) +pub(crate) const GEM_PORT_CMP(idx) (idx * 3 + 2) + +/* Which screening type 2 EtherType register will be used (0 - 7) */ +pub(crate) const SCRT2_ETHT 0 + +pub(crate) const GEM_ISR(hw_q) (0x0400 + ((hw_q) << 2)) +pub(crate) const GEM_TBQP(hw_q) (0x0440 + ((hw_q) << 2)) +pub(crate) const GEM_TBQPH(hw_q) (0x04C8) +pub(crate) const GEM_RBQP(hw_q) (0x0480 + ((hw_q) << 2)) +pub(crate) const GEM_RBQS(hw_q) (0x04A0 + ((hw_q) << 2)) +pub(crate) const GEM_RBQPH(hw_q) (0x04D4) +pub(crate) const GEM_IER(hw_q) (0x0600 + ((hw_q) << 2)) +pub(crate) const GEM_IDR(hw_q) (0x0620 + ((hw_q) << 2)) +pub(crate) const GEM_IMR(hw_q) (0x0640 + ((hw_q) << 2)) + +*/ + +/* Bitfields in NCR */ +pub(crate) const MACB_LB_OFFSET: u32 = 0; /* reserved */ +pub(crate) const MACB_LB_SIZE: u32 = 1; +pub(crate) const MACB_LLB_OFFSET: u32 = 1; /* Loop back local */ +pub(crate) const MACB_LLB_SIZE: u32 = 1; +pub(crate) const MACB_RE_OFFSET: u32 = 2; /* Receive enable */ +pub(crate) const MACB_RE_SIZE: u32 = 1; +pub(crate) const MACB_TE_OFFSET: u32 = 3; /* Transmit enable */ +pub(crate) const MACB_TE_SIZE: u32 = 1; +pub(crate) const MACB_MPE_OFFSET: u32 = 4; /* Management port enable */ +pub(crate) const MACB_MPE_SIZE: u32 = 1; +pub(crate) const MACB_CLRSTAT_OFFSET: u32 = 5; /* Clear stats regs */ +pub(crate) const MACB_CLRSTAT_SIZE: u32 = 1; +pub(crate) const MACB_INCSTAT_OFFSET: u32 = 6; /* Incremental stats regs */ +pub(crate) const MACB_INCSTAT_SIZE: u32 = 1; +pub(crate) const MACB_WESTAT_OFFSET: u32 = 7; /* Write enable stats regs */ +pub(crate) const MACB_WESTAT_SIZE: u32 = 1; +pub(crate) const MACB_BP_OFFSET: u32 = 8; /* Back pressure */ +pub(crate) const MACB_BP_SIZE: u32 = 1; +pub(crate) const MACB_TSTART_OFFSET: u32 = 9; /* Start transmission */ +pub(crate) const MACB_TSTART_SIZE: u32 = 1; +pub(crate) const MACB_THALT_OFFSET: u32 = 10; /* Transmit halt */ +pub(crate) const MACB_THALT_SIZE: u32 = 1; +pub(crate) const MACB_NCR_TPF_OFFSET: u32 = 11; /* Transmit pause frame */ +pub(crate) const MACB_NCR_TPF_SIZE: u32 = 1; +pub(crate) const MACB_TZQ_OFFSET: u32 = 12; /* Transmit zero quantum pause frame */ +pub(crate) const MACB_TZQ_SIZE: u32 = 1; +pub(crate) const MACB_SRTSM_OFFSET: u32 = 15; /* Store Receive Timestamp to Memory */ +pub(crate) const MACB_OSSMODE_OFFSET: u32 = 24; /* Enable One Step Synchro Mode */ +pub(crate) const MACB_OSSMODE_SIZE: u32 = 1; +pub(crate) const MACB_MIIONRGMII_OFFSET: u32 = 28; /* MII Usage on RGMII Interface */ +pub(crate) const MACB_MIIONRGMII_SIZE: u32 = 1; + +/* Bitfields in NCFGR */ +pub(crate) const MACB_SPD_OFFSET: u32 = 0; /* Speed */ +pub(crate) const MACB_SPD_SIZE: u32 = 1; +pub(crate) const MACB_FD_OFFSET: u32 = 1; /* Full duplex */ +pub(crate) const MACB_FD_SIZE: u32 = 1; +pub(crate) const MACB_BIT_RATE_OFFSET: u32 = 2; /* Discard non-VLAN frames */ +pub(crate) const MACB_BIT_RATE_SIZE: u32 = 1; +pub(crate) const MACB_JFRAME_OFFSET: u32 = 3; /* reserved */ +pub(crate) const MACB_JFRAME_SIZE: u32 = 1; +pub(crate) const MACB_CAF_OFFSET: u32 = 4; /* Copy all frames */ +pub(crate) const MACB_CAF_SIZE: u32 = 1; +pub(crate) const MACB_NBC_OFFSET: u32 = 5; /* No broadcast */ +pub(crate) const MACB_NBC_SIZE: u32 = 1; +pub(crate) const MACB_NCFGR_MTI_OFFSET: u32 = 6; /* Multicast hash enable */ +pub(crate) const MACB_NCFGR_MTI_SIZE: u32 = 1; +pub(crate) const MACB_UNI_OFFSET: u32 = 7; /* Unicast hash enable */ +pub(crate) const MACB_UNI_SIZE: u32 = 1; +pub(crate) const MACB_BIG_OFFSET: u32 = 8; /* Receive 1536 byte frames */ +pub(crate) const MACB_BIG_SIZE: u32 = 1; +pub(crate) const MACB_EAE_OFFSET: u32 = 9; /* External address match enable */ +pub(crate) const MACB_EAE_SIZE: u32 = 1; +pub(crate) const MACB_CLK_OFFSET: u32 = 10; +pub(crate) const MACB_CLK_SIZE: u32 = 2; +pub(crate) const MACB_RTY_OFFSET: u32 = 12; /* Retry test */ +pub(crate) const MACB_RTY_SIZE: u32 = 1; +pub(crate) const MACB_PAE_OFFSET: u32 = 13; /* Pause enable */ +pub(crate) const MACB_PAE_SIZE: u32 = 1; +pub(crate) const MACB_RM9200_RMII_OFFSET: u32 = 13; /* AT91RM9200 only */ +pub(crate) const MACB_RM9200_RMII_SIZE: u32 = 1; /* AT91RM9200 only */ +pub(crate) const MACB_RBOF_OFFSET: u32 = 14; /* Receive buffer offset */ +pub(crate) const MACB_RBOF_SIZE: u32 = 2; +pub(crate) const MACB_RLCE_OFFSET: u32 = 16; /* Length field error frame discard */ +pub(crate) const MACB_RLCE_SIZE: u32 = 1; +pub(crate) const MACB_DRFCS_OFFSET: u32 = 17; /* FCS remove */ +pub(crate) const MACB_DRFCS_SIZE: u32 = 1; +pub(crate) const MACB_EFRHD_OFFSET: u32 = 18; +pub(crate) const MACB_EFRHD_SIZE: u32 = 1; +pub(crate) const MACB_IRXFCS_OFFSET: u32 = 19; +pub(crate) const MACB_IRXFCS_SIZE: u32 = 1; + +/* GEM specific NCR bitfields. */ +pub(crate) const GEM_ENABLE_HS_MAC_OFFSET: u32 = 31; +pub(crate) const GEM_ENABLE_HS_MAC_SIZE: u32 = 1; + +/* GEM specific NCFGR bitfields. */ +pub(crate) const GEM_FD_OFFSET: u32 = 1; /* Full duplex */ +pub(crate) const GEM_FD_SIZE: u32 = 1; +pub(crate) const GEM_GBE_OFFSET: u32 = 10; /* Gigabit mode enable */ +pub(crate) const GEM_GBE_SIZE: u32 = 1; +pub(crate) const GEM_PCSSEL_OFFSET: u32 = 11; +pub(crate) const GEM_PCSSEL_SIZE: u32 = 1; +pub(crate) const GEM_PAE_OFFSET: u32 = 13; /* Pause enable */ +pub(crate) const GEM_PAE_SIZE: u32 = 1; +pub(crate) const GEM_CLK_OFFSET: u32 = 18; /* MDC clock division */ +pub(crate) const GEM_CLK_SIZE: u32 = 3; +pub(crate) const GEM_DBW_OFFSET: u32 = 21; /* Data bus width */ +pub(crate) const GEM_DBW_SIZE: u32 = 2; +pub(crate) const GEM_RXCOEN_OFFSET: u32 = 24; +pub(crate) const GEM_RXCOEN_SIZE: u32 = 1; +pub(crate) const GEM_SGMIIEN_OFFSET: u32 = 27; +pub(crate) const GEM_SGMIIEN_SIZE: u32 = 1; + +/* Constants for data bus width. */ +pub(crate) const GEM_DBW32: u32 = 0; /* 32 bit AMBA AHB data bus width */ +pub(crate) const GEM_DBW64: u32 = 1; /* 64 bit AMBA AHB data bus width */ +pub(crate) const GEM_DBW128: u32 = 2; /* 128 bit AMBA AHB data bus width */ + +/* Bitfields in DMACFG. */ +pub(crate) const GEM_FBLDO_OFFSET: u32 = 0; /* fixed burst length for DMA */ +pub(crate) const GEM_FBLDO_SIZE: u32 = 5; +pub(crate) const GEM_ENDIA_DESC_OFFSET: u32 = 6; /* endian swap mode for management descriptor access */ +pub(crate) const GEM_ENDIA_DESC_SIZE: u32 = 1; +pub(crate) const GEM_ENDIA_PKT_OFFSET: u32 = 7; /* endian swap mode for packet data access */ +pub(crate) const GEM_ENDIA_PKT_SIZE: u32 = 1; +pub(crate) const GEM_RXBMS_OFFSET: u32 = 8; /* RX packet buffer memory size select */ +pub(crate) const GEM_RXBMS_SIZE: u32 = 2; +pub(crate) const GEM_TXPBMS_OFFSET: u32 = 10; /* TX packet buffer memory size select */ +pub(crate) const GEM_TXPBMS_SIZE: u32 = 1; +pub(crate) const GEM_TXCOEN_OFFSET: u32 = 11; /* TX IP/TCP/UDP checksum gen offload */ +pub(crate) const GEM_TXCOEN_SIZE: u32 = 1; +pub(crate) const GEM_RXBS_OFFSET: u32 = 16; /* DMA receive buffer size */ +pub(crate) const GEM_RXBS_SIZE: u32 = 8; +pub(crate) const GEM_DDRP_OFFSET: u32 = 24; /* disc_when_no_ahb */ +pub(crate) const GEM_DDRP_SIZE: u32 = 1; +pub(crate) const GEM_RXEXT_OFFSET: u32 = 28; /* RX extended Buffer Descriptor mode */ +pub(crate) const GEM_RXEXT_SIZE: u32 = 1; +pub(crate) const GEM_TXEXT_OFFSET: u32 = 29; /* TX extended Buffer Descriptor mode */ +pub(crate) const GEM_TXEXT_SIZE: u32 = 1; +pub(crate) const GEM_ADDR64_OFFSET: u32 = 30; /* Address bus width - 64b or 32b */ +pub(crate) const GEM_ADDR64_SIZE: u32 = 1; + +/* Bitfields in NSR */ +pub(crate) const MACB_NSR_LINK_OFFSET: u32 = 0; /* pcs_link_state */ +pub(crate) const MACB_NSR_LINK_SIZE: u32 = 1; +pub(crate) const MACB_MDIO_OFFSET: u32 = 1; /* status of the mdio_in pin */ +pub(crate) const MACB_MDIO_SIZE: u32 = 1; +pub(crate) const MACB_IDLE_OFFSET: u32 = 2; /* The PHY management logic is idle */ +pub(crate) const MACB_IDLE_SIZE: u32 = 1; + +/* Bitfields in TSR */ +pub(crate) const MACB_UBR_OFFSET: u32 = 0; /* Used bit read */ +pub(crate) const MACB_UBR_SIZE: u32 = 1; +pub(crate) const MACB_COL_OFFSET: u32 = 1; /* Collision occurred */ +pub(crate) const MACB_COL_SIZE: u32 = 1; +pub(crate) const MACB_TSR_RLE_OFFSET: u32 = 2; /* Retry limit exceeded */ +pub(crate) const MACB_TSR_RLE_SIZE: u32 = 1; +pub(crate) const MACB_TGO_OFFSET: u32 = 3; /* Transmit go */ +pub(crate) const MACB_TGO_SIZE: u32 = 1; +pub(crate) const MACB_BEX_OFFSET: u32 = 4; /* TX frame corruption due to AHB error */ +pub(crate) const MACB_BEX_SIZE: u32 = 1; +pub(crate) const MACB_RM9200_BNQ_OFFSET: u32 = 4; /* AT91RM9200 only */ +pub(crate) const MACB_RM9200_BNQ_SIZE: u32 = 1; /* AT91RM9200 only */ +pub(crate) const MACB_COMP_OFFSET: u32 = 5; /* Trnasmit complete */ +pub(crate) const MACB_COMP_SIZE: u32 = 1; +pub(crate) const MACB_UND_OFFSET: u32 = 6; /* Trnasmit under run */ +pub(crate) const MACB_UND_SIZE: u32 = 1; + +/* Bitfields in RSR */ +pub(crate) const MACB_BNA_OFFSET: u32 = 0; /* Buffer not available */ +pub(crate) const MACB_BNA_SIZE: u32 = 1; +pub(crate) const MACB_REC_OFFSET: u32 = 1; /* Frame received */ +pub(crate) const MACB_REC_SIZE: u32 = 1; +pub(crate) const MACB_OVR_OFFSET: u32 = 2; /* Receive overrun */ +pub(crate) const MACB_OVR_SIZE: u32 = 1; + +/* Bitfields in ISR/IER/IDR/IMR */ +pub(crate) const MACB_MFD_OFFSET: u32 = 0; /* Management frame sent */ +pub(crate) const MACB_MFD_SIZE: u32 = 1; +pub(crate) const MACB_RCOMP_OFFSET: u32 = 1; /* Receive complete */ +pub(crate) const MACB_RCOMP_SIZE: u32 = 1; +pub(crate) const MACB_RXUBR_OFFSET: u32 = 2; /* RX used bit read */ +pub(crate) const MACB_RXUBR_SIZE: u32 = 1; +pub(crate) const MACB_TXUBR_OFFSET: u32 = 3; /* TX used bit read */ +pub(crate) const MACB_TXUBR_SIZE: u32 = 1; +pub(crate) const MACB_ISR_TUND_OFFSET: u32 = 4; /* Enable TX buffer under run interrupt */ +pub(crate) const MACB_ISR_TUND_SIZE: u32 = 1; +pub(crate) const MACB_ISR_RLE_OFFSET: u32 = 5; /* EN retry exceeded/late coll interrupt */ +pub(crate) const MACB_ISR_RLE_SIZE: u32 = 1; +pub(crate) const MACB_TXERR_OFFSET: u32 = 6; /* EN TX frame corrupt from error interrupt */ +pub(crate) const MACB_TXERR_SIZE: u32 = 1; +pub(crate) const MACB_RM9200_TBRE_OFFSET: u32 = 6; /* EN may send new frame interrupt (RM9200) */ +pub(crate) const MACB_RM9200_TBRE_SIZE: u32 = 1; +pub(crate) const MACB_TCOMP_OFFSET: u32 = 7; /* Enable transmit complete interrupt */ +pub(crate) const MACB_TCOMP_SIZE: u32 = 1; +pub(crate) const MACB_ISR_LINK_OFFSET: u32 = 9; /* Enable link change interrupt */ +pub(crate) const MACB_ISR_LINK_SIZE: u32 = 1; +pub(crate) const MACB_ISR_ROVR_OFFSET: u32 = 10; /* Enable receive overrun interrupt */ +pub(crate) const MACB_ISR_ROVR_SIZE: u32 = 1; +pub(crate) const MACB_HRESP_OFFSET: u32 = 11; /* Enable hrsep not OK interrupt */ +pub(crate) const MACB_HRESP_SIZE: u32 = 1; +pub(crate) const MACB_PFR_OFFSET: u32 = 12; /* Enable pause frame w/ quantum interrupt */ +pub(crate) const MACB_PFR_SIZE: u32 = 1; +pub(crate) const MACB_PTZ_OFFSET: u32 = 13; /* Enable pause time zero interrupt */ +pub(crate) const MACB_PTZ_SIZE: u32 = 1; +pub(crate) const MACB_WOL_OFFSET: u32 = 14; /* Enable wake-on-lan interrupt */ +pub(crate) const MACB_WOL_SIZE: u32 = 1; +pub(crate) const MACB_DRQFR_OFFSET: u32 = 18; /* PTP Delay Request Frame Received */ +pub(crate) const MACB_DRQFR_SIZE: u32 = 1; +pub(crate) const MACB_SFR_OFFSET: u32 = 19; /* PTP Sync Frame Received */ +pub(crate) const MACB_SFR_SIZE: u32 = 1; +pub(crate) const MACB_DRQFT_OFFSET: u32 = 20; /* PTP Delay Request Frame Transmitted */ +pub(crate) const MACB_DRQFT_SIZE: u32 = 1; +pub(crate) const MACB_SFT_OFFSET: u32 = 21; /* PTP Sync Frame Transmitted */ +pub(crate) const MACB_SFT_SIZE: u32 = 1; +pub(crate) const MACB_PDRQFR_OFFSET: u32 = 22; /* PDelay Request Frame Received */ +pub(crate) const MACB_PDRQFR_SIZE: u32 = 1; +pub(crate) const MACB_PDRSFR_OFFSET: u32 = 23; /* PDelay Response Frame Received */ +pub(crate) const MACB_PDRSFR_SIZE: u32 = 1; +pub(crate) const MACB_PDRQFT_OFFSET: u32 = 24; /* PDelay Request Frame Transmitted */ +pub(crate) const MACB_PDRQFT_SIZE: u32 = 1; +pub(crate) const MACB_PDRSFT_OFFSET: u32 = 25; /* PDelay Response Frame Transmitted */ +pub(crate) const MACB_PDRSFT_SIZE: u32 = 1; +pub(crate) const MACB_SRI_OFFSET: u32 = 26; /* TSU Seconds Register Increment */ +pub(crate) const MACB_SRI_SIZE: u32 = 1; +pub(crate) const GEM_WOL_OFFSET: u32 = 28; /* Enable wake-on-lan interrupt */ +pub(crate) const GEM_WOL_SIZE: u32 = 1; + +/* Timer increment fields */ +pub(crate) const MACB_TI_CNS_OFFSET: u32 = 0; +pub(crate) const MACB_TI_CNS_SIZE: u32 = 8; +pub(crate) const MACB_TI_ACNS_OFFSET: u32 = 8; +pub(crate) const MACB_TI_ACNS_SIZE: u32 = 8; +pub(crate) const MACB_TI_NIT_OFFSET: u32 = 16; +pub(crate) const MACB_TI_NIT_SIZE: u32 = 8; + +/* Bitfields in MAN */ +pub(crate) const MACB_DATA_OFFSET: u32 = 0; /* data */ +pub(crate) const MACB_DATA_SIZE: u32 = 16; +pub(crate) const MACB_CODE_OFFSET: u32 = 16; /* Must be written to 10 */ +pub(crate) const MACB_CODE_SIZE: u32 = 2; +pub(crate) const MACB_REGA_OFFSET: u32 = 18; /* Register address */ +pub(crate) const MACB_REGA_SIZE: u32 = 5; +pub(crate) const MACB_PHYA_OFFSET: u32 = 23; /* PHY address */ +pub(crate) const MACB_PHYA_SIZE: u32 = 5; +pub(crate) const MACB_RW_OFFSET: u32 = 28; /* Operation. 10 is read. 01 is write. */ +pub(crate) const MACB_RW_SIZE: u32 = 2; +pub(crate) const MACB_SOF_OFFSET: u32 = 30; /* Must be written to 1 for Clause 22 */ +pub(crate) const MACB_SOF_SIZE: u32 = 2; + +/* Bitfields in USRIO (AVR32) */ +pub(crate) const MACB_MII_OFFSET: u32 = 0; +pub(crate) const MACB_MII_SIZE: u32 = 1; +pub(crate) const MACB_EAM_OFFSET: u32 = 1; +pub(crate) const MACB_EAM_SIZE: u32 = 1; +pub(crate) const MACB_TX_PAUSE_OFFSET: u32 = 2; +pub(crate) const MACB_TX_PAUSE_SIZE: u32 = 1; +pub(crate) const MACB_TX_PAUSE_ZERO_OFFSET: u32 = 3; +pub(crate) const MACB_TX_PAUSE_ZERO_SIZE: u32 = 1; + +/* Bitfields in USRIO (AT91) */ +pub(crate) const MACB_RMII_OFFSET: u32 = 0; +pub(crate) const MACB_RMII_SIZE: u32 = 1; +pub(crate) const GEM_RGMII_OFFSET: u32 = 0; /* GEM gigabit mode */ +pub(crate) const GEM_RGMII_SIZE: u32 = 1; +pub(crate) const MACB_CLKEN_OFFSET: u32 = 1; +pub(crate) const MACB_CLKEN_SIZE: u32 = 1; + +/* Bitfields in WOL */ +pub(crate) const MACB_IP_OFFSET: u32 = 0; +pub(crate) const MACB_IP_SIZE: u32 = 16; +pub(crate) const MACB_MAG_OFFSET: u32 = 16; +pub(crate) const MACB_MAG_SIZE: u32 = 1; +pub(crate) const MACB_ARP_OFFSET: u32 = 17; +pub(crate) const MACB_ARP_SIZE: u32 = 1; +pub(crate) const MACB_SA1_OFFSET: u32 = 18; +pub(crate) const MACB_SA1_SIZE: u32 = 1; +pub(crate) const MACB_WOL_MTI_OFFSET: u32 = 19; +pub(crate) const MACB_WOL_MTI_SIZE: u32 = 1; + +/* Bitfields in MID */ +pub(crate) const MACB_IDNUM_OFFSET: u32 = 16; +pub(crate) const MACB_IDNUM_SIZE: u32 = 12; +pub(crate) const MACB_REV_OFFSET: u32 = 0; +pub(crate) const MACB_REV_SIZE: u32 = 16; + +/* Bitfield in HS_MAC_CONFIG */ +pub(crate) const GEM_HS_MAC_SPEED_OFFSET: u32 = 0; +pub(crate) const GEM_HS_MAC_SPEED_SIZE: u32 = 3; + +/* Bitfields in PCSCNTRL */ +pub(crate) const GEM_PCSAUTONEG_OFFSET: u32 = 12; +pub(crate) const GEM_PCSAUTONEG_SIZE: u32 = 1; + +/* Bitfields in DCFG1. */ +pub(crate) const GEM_IRQCOR_OFFSET: u32 = 23; +pub(crate) const GEM_IRQCOR_SIZE: u32 = 1; +pub(crate) const GEM_DBWDEF_OFFSET: u32 = 25; +pub(crate) const GEM_DBWDEF_SIZE: u32 = 3; +pub(crate) const GEM_NO_PCS_OFFSET: u32 = 0; +pub(crate) const GEM_NO_PCS_SIZE: u32 = 1; + +/* Bitfields in DCFG2. */ +pub(crate) const GEM_RX_PKT_BUFF_OFFSET: u32 = 20; +pub(crate) const GEM_RX_PKT_BUFF_SIZE: u32 = 1; +pub(crate) const GEM_TX_PKT_BUFF_OFFSET: u32 = 21; +pub(crate) const GEM_TX_PKT_BUFF_SIZE: u32 = 1; + +/* Bitfields in DCFG5. */ +pub(crate) const GEM_TSU_OFFSET: u32 = 8; +pub(crate) const GEM_TSU_SIZE: u32 = 1; + +/* Bitfields in DCFG6. */ +pub(crate) const GEM_PBUF_LSO_OFFSET: u32 = 27; +pub(crate) const GEM_PBUF_LSO_SIZE: u32 = 1; +pub(crate) const GEM_DAW64_OFFSET: u32 = 23; +pub(crate) const GEM_DAW64_SIZE: u32 = 1; + +/* Bitfields in DCFG8. */ +pub(crate) const GEM_T1SCR_OFFSET: u32 = 24; +pub(crate) const GEM_T1SCR_SIZE: u32 = 8; +pub(crate) const GEM_T2SCR_OFFSET: u32 = 16; +pub(crate) const GEM_T2SCR_SIZE: u32 = 8; +pub(crate) const GEM_SCR2ETH_OFFSET: u32 = 8; +pub(crate) const GEM_SCR2ETH_SIZE: u32 = 8; +pub(crate) const GEM_SCR2CMP_OFFSET: u32 = 0; +pub(crate) const GEM_SCR2CMP_SIZE: u32 = 8; + +/* Bitfields in DCFG10 */ +pub(crate) const GEM_TXBD_RDBUFF_OFFSET: u32 = 12; +pub(crate) const GEM_TXBD_RDBUFF_SIZE: u32 = 4; +pub(crate) const GEM_RXBD_RDBUFF_OFFSET: u32 = 8; +pub(crate) const GEM_RXBD_RDBUFF_SIZE: u32 = 4; + +/* Bitfields in DCFG12. */ +pub(crate) const GEM_HIGH_SPEED_OFFSET: u32 = 26; +pub(crate) const GEM_HIGH_SPEED_SIZE: u32 = 1; + +/* Bitfields in USX_CONTROL. */ +pub(crate) const GEM_USX_CTRL_SPEED_OFFSET: u32 = 14; +pub(crate) const GEM_USX_CTRL_SPEED_SIZE: u32 = 3; +pub(crate) const GEM_SERDES_RATE_OFFSET: u32 = 12; +pub(crate) const GEM_SERDES_RATE_SIZE: u32 = 2; +pub(crate) const GEM_RX_SCR_BYPASS_OFFSET: u32 = 9; +pub(crate) const GEM_RX_SCR_BYPASS_SIZE: u32 = 1; +pub(crate) const GEM_TX_SCR_BYPASS_OFFSET: u32 = 8; +pub(crate) const GEM_TX_SCR_BYPASS_SIZE: u32 = 1; +pub(crate) const GEM_TX_EN_OFFSET: u32 = 1; +pub(crate) const GEM_TX_EN_SIZE: u32 = 1; +pub(crate) const GEM_SIGNAL_OK_OFFSET: u32 = 0; +pub(crate) const GEM_SIGNAL_OK_SIZE: u32 = 1; + +/* Bitfields in USX_STATUS. */ +pub(crate) const GEM_USX_BLOCK_LOCK_OFFSET: u32 = 0; +pub(crate) const GEM_USX_BLOCK_LOCK_SIZE: u32 = 1; + +/* Bitfields in TISUBN */ +pub(crate) const GEM_SUBNSINCR_OFFSET: u32 = 0; +pub(crate) const GEM_SUBNSINCRL_OFFSET: u32 = 24; +pub(crate) const GEM_SUBNSINCRL_SIZE: u32 = 8; +pub(crate) const GEM_SUBNSINCRH_OFFSET: u32 = 0; +pub(crate) const GEM_SUBNSINCRH_SIZE: u32 = 16; +pub(crate) const GEM_SUBNSINCR_SIZE: u32 = 24; + +/* Bitfields in TI */ +pub(crate) const GEM_NSINCR_OFFSET: u32 = 0; +pub(crate) const GEM_NSINCR_SIZE: u32 = 8; + +/* Bitfields in TSH */ +pub(crate) const GEM_TSH_OFFSET: u32 = 0; /* TSU timer value (s). MSB [47:32] of seconds timer count */ +pub(crate) const GEM_TSH_SIZE: u32 = 16; + +/* Bitfields in TSL */ +pub(crate) const GEM_TSL_OFFSET: u32 = 0; /* TSU timer value (s). LSB [31:0] of seconds timer count */ +pub(crate) const GEM_TSL_SIZE: u32 = 32; + +/* Bitfields in TN */ +pub(crate) const GEM_TN_OFFSET: u32 = 0; /* TSU timer value (ns) */ +pub(crate) const GEM_TN_SIZE: u32 = 30; + +/* Bitfields in TXBDCTRL */ +pub(crate) const GEM_TXTSMODE_OFFSET: u32 = 4; /* TX Descriptor Timestamp Insertion mode */ +pub(crate) const GEM_TXTSMODE_SIZE: u32 = 2; + +/* Bitfields in RXBDCTRL */ +pub(crate) const GEM_RXTSMODE_OFFSET: u32 = 4; /* RX Descriptor Timestamp Insertion mode */ +pub(crate) const GEM_RXTSMODE_SIZE: u32 = 2; + +/* Bitfields in SCRT2 */ +pub(crate) const GEM_QUEUE_OFFSET: u32 = 0; /* Queue Number */ +pub(crate) const GEM_QUEUE_SIZE: u32 = 4; +pub(crate) const GEM_VLANPR_OFFSET: u32 = 4; /* VLAN Priority */ +pub(crate) const GEM_VLANPR_SIZE: u32 = 3; +pub(crate) const GEM_VLANEN_OFFSET: u32 = 8; /* VLAN Enable */ +pub(crate) const GEM_VLANEN_SIZE: u32 = 1; +pub(crate) const GEM_ETHT2IDX_OFFSET: u32 = 9; /* Index to screener type 2 EtherType register */ +pub(crate) const GEM_ETHT2IDX_SIZE: u32 = 3; +pub(crate) const GEM_ETHTEN_OFFSET: u32 = 12; /* EtherType Enable */ +pub(crate) const GEM_ETHTEN_SIZE: u32 = 1; +pub(crate) const GEM_CMPA_OFFSET: u32 = 13; /* Compare A - Index to screener type 2 Compare register */ +pub(crate) const GEM_CMPA_SIZE: u32 = 5; +pub(crate) const GEM_CMPAEN_OFFSET: u32 = 18; /* Compare A Enable */ +pub(crate) const GEM_CMPAEN_SIZE: u32 = 1; +pub(crate) const GEM_CMPB_OFFSET: u32 = 19; /* Compare B - Index to screener type 2 Compare register */ +pub(crate) const GEM_CMPB_SIZE: u32 = 5; +pub(crate) const GEM_CMPBEN_OFFSET: u32 = 24; /* Compare B Enable */ +pub(crate) const GEM_CMPBEN_SIZE: u32 = 1; +pub(crate) const GEM_CMPC_OFFSET: u32 = 25; /* Compare C - Index to screener type 2 Compare register */ +pub(crate) const GEM_CMPC_SIZE: u32 = 5; +pub(crate) const GEM_CMPCEN_OFFSET: u32 = 30; /* Compare C Enable */ +pub(crate) const GEM_CMPCEN_SIZE: u32 = 1; + +/* Bitfields in ETHT */ +pub(crate) const GEM_ETHTCMP_OFFSET: u32 = 0; /* EtherType compare value */ +pub(crate) const GEM_ETHTCMP_SIZE: u32 = 16; + +/* Bitfields in T2CMPW0 */ +pub(crate) const GEM_T2CMP_OFFSET: u32 = 16; /* 0xFFFF0000 compare value */ +pub(crate) const GEM_T2CMP_SIZE: u32 = 16; +pub(crate) const GEM_T2MASK_OFFSET: u32 = 0; /* 0x0000FFFF compare value or mask */ +pub(crate) const GEM_T2MASK_SIZE: u32 = 16; + +/* Bitfields in T2CMPW1 */ +pub(crate) const GEM_T2DISMSK_OFFSET: u32 = 9; /* disable mask */ +pub(crate) const GEM_T2DISMSK_SIZE: u32 = 1; +pub(crate) const GEM_T2CMPOFST_OFFSET: u32 = 7; /* compare offset */ +pub(crate) const GEM_T2CMPOFST_SIZE: u32 = 2; +pub(crate) const GEM_T2OFST_OFFSET: u32 = 0; /* offset value */ +pub(crate) const GEM_T2OFST_SIZE: u32 = 7; + +/* Offset for screener type 2 compare values (T2CMPOFST). + * Note the offset is applied after the specified point, + * e.g. GEM_T2COMPOFST_ETYPE denotes the EtherType field, so an offset + * of 12 bytes from this would be the source IP address in an IP header + */ +pub(crate) const GEM_T2COMPOFST_SOF: u32 = 0; +pub(crate) const GEM_T2COMPOFST_ETYPE: u32 = 1; +pub(crate) const GEM_T2COMPOFST_IPHDR: u32 = 2; +pub(crate) const GEM_T2COMPOFST_TCPUDP: u32 = 3; + +/* offset from EtherType to IP address */ +pub(crate) const ETYPE_SRCIP_OFFSET: u32 = 12; +pub(crate) const ETYPE_DSTIP_OFFSET: u32 = 16; + +/* offset from IP header to port */ +pub(crate) const IPHDR_SRCPORT_OFFSET: u32 = 0; +pub(crate) const IPHDR_DSTPORT_OFFSET: u32 = 2; + +/* Transmit DMA buffer descriptor Word 1 */ +pub(crate) const GEM_DMA_TXVALID_OFFSET: u32 = 23; /* timestamp has been captured in the Buffer Descriptor */ +pub(crate) const GEM_DMA_TXVALID_SIZE: u32 = 1; + +/* Receive DMA buffer descriptor Word 0 */ +pub(crate) const GEM_DMA_RXVALID_OFFSET: u32 = 2; /* indicates a valid timestamp in the Buffer Descriptor */ +pub(crate) const GEM_DMA_RXVALID_SIZE: u32 = 1; + +/* DMA buffer descriptor Word 2 (32 bit addressing) or Word 4 (64 bit addressing) */ +pub(crate) const GEM_DMA_SECL_OFFSET: u32 = 30; /* Timestamp seconds[1:0] */ +pub(crate) const GEM_DMA_SECL_SIZE: u32 = 2; +pub(crate) const GEM_DMA_NSEC_OFFSET: u32 = 0; /* Timestamp nanosecs [29:0] */ +pub(crate) const GEM_DMA_NSEC_SIZE: u32 = 30; + +/* DMA buffer descriptor Word 3 (32 bit addressing) or Word 5 (64 bit addressing) */ + +/* New hardware supports 12 bit precision of timestamp in DMA buffer descriptor. + * Old hardware supports only 6 bit precision but it is enough for PTP. + * Less accuracy is used always instead of checking hardware version. + */ +/* +pub(crate) const GEM_DMA_SECH_OFFSET 0 /* Timestamp seconds[5:2] */ +pub(crate) const GEM_DMA_SECH_SIZE 4 +pub(crate) const GEM_DMA_SEC_WIDTH (GEM_DMA_SECH_SIZE + GEM_DMA_SECL_SIZE) +pub(crate) const GEM_DMA_SEC_TOP (1 << GEM_DMA_SEC_WIDTH) +pub(crate) const GEM_DMA_SEC_MASK (GEM_DMA_SEC_TOP - 1) +*/ + +/* Bitfields in ADJ */ +pub(crate) const GEM_ADDSUB_OFFSET: u32 = 31; +pub(crate) const GEM_ADDSUB_SIZE: u32 = 1; +/* Constants for CLK */ +pub(crate) const MACB_CLK_DIV8: u32 = 0; +pub(crate) const MACB_CLK_DIV16: u32 = 1; +pub(crate) const MACB_CLK_DIV32: u32 = 2; +pub(crate) const MACB_CLK_DIV64: u32 = 3; + +/* GEM specific constants for CLK. */ +pub(crate) const GEM_CLK_DIV8: u32 = 0; +pub(crate) const GEM_CLK_DIV16: u32 = 1; +pub(crate) const GEM_CLK_DIV32: u32 = 2; +pub(crate) const GEM_CLK_DIV48: u32 = 3; +pub(crate) const GEM_CLK_DIV64: u32 = 4; +pub(crate) const GEM_CLK_DIV96: u32 = 5; +pub(crate) const GEM_CLK_DIV128: u32 = 6; +pub(crate) const GEM_CLK_DIV224: u32 = 7; + +/* Constants for MAN register */ +pub(crate) const MACB_MAN_C22_SOF: u32 = 1; +pub(crate) const MACB_MAN_C22_WRITE: u32 = 1; +pub(crate) const MACB_MAN_C22_READ: u32 = 2; +pub(crate) const MACB_MAN_C22_CODE: u32 = 2; + +pub(crate) const MACB_MAN_C45_SOF: u32 = 0; +pub(crate) const MACB_MAN_C45_ADDR: u32 = 0; +pub(crate) const MACB_MAN_C45_WRITE: u32 = 1; +pub(crate) const MACB_MAN_C45_POST_READ_INCR: u32 = 2; +pub(crate) const MACB_MAN_C45_READ: u32 = 3; +pub(crate) const MACB_MAN_C45_CODE: u32 = 2; + +/* Capability mask bits */ +pub(crate) const MACB_CAPS_ISR_CLEAR_ON_WRITE: u32 = 0x00000001; +pub(crate) const MACB_CAPS_USRIO_HAS_CLKEN: u32 = 0x00000002; +pub(crate) const MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII: u32 = 0x00000004; +pub(crate) const MACB_CAPS_NO_GIGABIT_HALF: u32 = 0x00000008; +pub(crate) const MACB_CAPS_USRIO_DISABLED: u32 = 0x00000010; +pub(crate) const MACB_CAPS_JUMBO: u32 = 0x00000020; +pub(crate) const MACB_CAPS_GEM_HAS_PTP: u32 = 0x00000040; +pub(crate) const MACB_CAPS_BD_RD_PREFETCH: u32 = 0x00000080; +pub(crate) const MACB_CAPS_NEEDS_RSTONUBR: u32 = 0x00000100; +pub(crate) const MACB_CAPS_MIIONRGMII: u32 = 0x00000200; +pub(crate) const MACB_CAPS_CLK_HW_CHG: u32 = 0x04000000; +pub(crate) const MACB_CAPS_MACB_IS_EMAC: u32 = 0x08000000; +pub(crate) const MACB_CAPS_FIFO_MODE: u32 = 0x10000000; +pub(crate) const MACB_CAPS_GIGABIT_MODE_AVAILABLE: u32 = 0x20000000; +pub(crate) const MACB_CAPS_SG_DISABLED: u32 = 0x40000000; +pub(crate) const MACB_CAPS_MACB_IS_GEM: u32 = 0x80000000; +pub(crate) const MACB_CAPS_PCS: u32 = 0x01000000; +pub(crate) const MACB_CAPS_HIGH_SPEED: u32 = 0x02000000; + +/* LSO settings */ +pub(crate) const MACB_LSO_UFO_ENABLE: u32 = 0x01; +pub(crate) const MACB_LSO_TSO_ENABLE: u32 = 0x02; + +/* +/* Bit manipulation macros */ +pub(crate) const MACB_BIT(name) \ + (1 << MACB_##name##_OFFSET) +pub(crate) const MACB_BF(name,value) \ + (((value) & ((1 << MACB_##name##_SIZE) - 1)) \ + << MACB_##name##_OFFSET) +pub(crate) const MACB_BFEXT(name,value)\ + (((value) >> MACB_##name##_OFFSET) \ + & ((1 << MACB_##name##_SIZE) - 1)) +pub(crate) const MACB_BFINS(name,value,old) \ + (((old) & ~(((1 << MACB_##name##_SIZE) - 1) \ + << MACB_##name##_OFFSET)) \ + | MACB_BF(name,value)) + +pub(crate) const GEM_BIT(name) \ + (1 << GEM_##name##_OFFSET) +pub(crate) const GEM_BF(name, value) \ + (((value) & ((1 << GEM_##name##_SIZE) - 1)) \ + << GEM_##name##_OFFSET) +pub(crate) const GEM_BFEXT(name, value)\ + (((value) >> GEM_##name##_OFFSET) \ + & ((1 << GEM_##name##_SIZE) - 1)) +pub(crate) const GEM_BFINS(name, value, old) \ + (((old) & ~(((1 << GEM_##name##_SIZE) - 1) \ + << GEM_##name##_OFFSET)) \ + | GEM_BF(name, value)) + +/* Register access macros */ +pub(crate) const macb_readl(port, reg) (port)->macb_reg_readl((port), MACB_##reg) +pub(crate) const macb_writel(port, reg, value) (port)->macb_reg_writel((port), MACB_##reg, (value)) +pub(crate) const gem_readl(port, reg) (port)->macb_reg_readl((port), GEM_##reg) +pub(crate) const gem_writel(port, reg, value) (port)->macb_reg_writel((port), GEM_##reg, (value)) +pub(crate) const queue_readl(queue, reg) (queue)->bp->macb_reg_readl((queue)->bp, (queue)->reg) +pub(crate) const queue_writel(queue, reg, value) (queue)->bp->macb_reg_writel((queue)->bp, (queue)->reg, (value)) +pub(crate) const gem_readl_n(port, reg, idx) (port)->macb_reg_readl((port), GEM_##reg + idx * 4) +pub(crate) const gem_writel_n(port, reg, idx, value) (port)->macb_reg_writel((port), GEM_##reg + idx * 4, (value)) + +pub(crate) const PTP_TS_BUFFER_SIZE 128 /* must be power of 2 */ + +/* struct macb_dma_desc - Hardware DMA descriptor + * @addr: DMA address of data buffer + * @ctrl: Control and status bits + */ +struct macb_dma_desc { + u32 addr; + u32 ctrl; +}; + +#ifdef MACB_EXT_DESC +pub(crate) const HW_DMA_CAP_32B 0 +pub(crate) const HW_DMA_CAP_64B (1 << 0) +pub(crate) const HW_DMA_CAP_PTP (1 << 1) +pub(crate) const HW_DMA_CAP_64B_PTP (HW_DMA_CAP_64B | HW_DMA_CAP_PTP) + +struct macb_dma_desc_64 { + u32 addrh; + u32 resvd; +}; + +struct macb_dma_desc_ptp { + u32 ts_1; + u32 ts_2; +}; + +struct gem_tx_ts { + struct sk_buff *skb; + struct macb_dma_desc_ptp desc_ptp; +}; +#endif + +*/ +/* DMA descriptor bitfields */ +pub(crate) const MACB_RX_USED_OFFSET: u32 = 0; +pub(crate) const MACB_RX_USED_SIZE: u32 = 1; +pub(crate) const MACB_RX_WRAP_OFFSET: u32 = 1; +pub(crate) const MACB_RX_WRAP_SIZE: u32 = 1; +pub(crate) const MACB_RX_WADDR_OFFSET: u32 = 2; +pub(crate) const MACB_RX_WADDR_SIZE: u32 = 30; + +pub(crate) const MACB_RX_FRMLEN_OFFSET: u32 = 0; +pub(crate) const MACB_RX_FRMLEN_SIZE: u32 = 12; +pub(crate) const MACB_RX_OFFSET_OFFSET: u32 = 12; +pub(crate) const MACB_RX_OFFSET_SIZE: u32 = 2; +pub(crate) const MACB_RX_SOF_OFFSET: u32 = 14; +pub(crate) const MACB_RX_SOF_SIZE: u32 = 1; +pub(crate) const MACB_RX_EOF_OFFSET: u32 = 15; +pub(crate) const MACB_RX_EOF_SIZE: u32 = 1; +pub(crate) const MACB_RX_CFI_OFFSET: u32 = 16; +pub(crate) const MACB_RX_CFI_SIZE: u32 = 1; +pub(crate) const MACB_RX_VLAN_PRI_OFFSET: u32 = 17; +pub(crate) const MACB_RX_VLAN_PRI_SIZE: u32 = 3; +pub(crate) const MACB_RX_PRI_TAG_OFFSET: u32 = 20; +pub(crate) const MACB_RX_PRI_TAG_SIZE: u32 = 1; +pub(crate) const MACB_RX_VLAN_TAG_OFFSET: u32 = 21; +pub(crate) const MACB_RX_VLAN_TAG_SIZE: u32 = 1; +pub(crate) const MACB_RX_TYPEID_MATCH_OFFSET: u32 = 22; +pub(crate) const MACB_RX_TYPEID_MATCH_SIZE: u32 = 1; +pub(crate) const MACB_RX_SA4_MATCH_OFFSET: u32 = 23; +pub(crate) const MACB_RX_SA4_MATCH_SIZE: u32 = 1; +pub(crate) const MACB_RX_SA3_MATCH_OFFSET: u32 = 24; +pub(crate) const MACB_RX_SA3_MATCH_SIZE: u32 = 1; +pub(crate) const MACB_RX_SA2_MATCH_OFFSET: u32 = 25; +pub(crate) const MACB_RX_SA2_MATCH_SIZE: u32 = 1; +pub(crate) const MACB_RX_SA1_MATCH_OFFSET: u32 = 26; +pub(crate) const MACB_RX_SA1_MATCH_SIZE: u32 = 1; +pub(crate) const MACB_RX_EXT_MATCH_OFFSET: u32 = 28; +pub(crate) const MACB_RX_EXT_MATCH_SIZE: u32 = 1; +pub(crate) const MACB_RX_UHASH_MATCH_OFFSET: u32 = 29; +pub(crate) const MACB_RX_UHASH_MATCH_SIZE: u32 = 1; +pub(crate) const MACB_RX_MHASH_MATCH_OFFSET: u32 = 30; +pub(crate) const MACB_RX_MHASH_MATCH_SIZE: u32 = 1; +pub(crate) const MACB_RX_BROADCAST_OFFSET: u32 = 31; +pub(crate) const MACB_RX_BROADCAST_SIZE: u32 = 1; + +pub(crate) const MACB_RX_FRMLEN_MASK: u32 = 0xFFF; +pub(crate) const MACB_RX_JFRMLEN_MASK: u32 = 0x3FFF; + +/* RX checksum offload disabled: bit 24 clear in NCFGR */ +pub(crate) const GEM_RX_TYPEID_MATCH_OFFSET: u32 = 22; +pub(crate) const GEM_RX_TYPEID_MATCH_SIZE: u32 = 2; + +/* RX checksum offload enabled: bit 24 set in NCFGR */ +pub(crate) const GEM_RX_CSUM_OFFSET: u32 = 22; +pub(crate) const GEM_RX_CSUM_SIZE: u32 = 2; + +pub(crate) const MACB_TX_FRMLEN_OFFSET: u32 = 0; +pub(crate) const MACB_TX_FRMLEN_SIZE: u32 = 11; +pub(crate) const MACB_TX_LAST_OFFSET: u32 = 15; +pub(crate) const MACB_TX_LAST_SIZE: u32 = 1; +pub(crate) const MACB_TX_NOCRC_OFFSET: u32 = 16; +pub(crate) const MACB_TX_NOCRC_SIZE: u32 = 1; +pub(crate) const MACB_MSS_MFS_OFFSET: u32 = 16; +pub(crate) const MACB_MSS_MFS_SIZE: u32 = 14; +pub(crate) const MACB_TX_LSO_OFFSET: u32 = 17; +pub(crate) const MACB_TX_LSO_SIZE: u32 = 2; +pub(crate) const MACB_TX_TCP_SEQ_SRC_OFFSET: u32 = 19; +pub(crate) const MACB_TX_TCP_SEQ_SRC_SIZE: u32 = 1; +pub(crate) const MACB_TX_BUF_EXHAUSTED_OFFSET: u32 = 27; +pub(crate) const MACB_TX_BUF_EXHAUSTED_SIZE: u32 = 1; +pub(crate) const MACB_TX_UNDERRUN_OFFSET: u32 = 28; +pub(crate) const MACB_TX_UNDERRUN_SIZE: u32 = 1; +pub(crate) const MACB_TX_ERROR_OFFSET: u32 = 29; +pub(crate) const MACB_TX_ERROR_SIZE: u32 = 1; +pub(crate) const MACB_TX_WRAP_OFFSET: u32 = 30; +pub(crate) const MACB_TX_WRAP_SIZE: u32 = 1; +pub(crate) const MACB_TX_USED_OFFSET: u32 = 31; +pub(crate) const MACB_TX_USED_SIZE: u32 = 1; + +pub(crate) const GEM_TX_FRMLEN_OFFSET: u32 = 0; +pub(crate) const GEM_TX_FRMLEN_SIZE: u32 = 14; + +/* Buffer descriptor constants */ +pub(crate) const GEM_RX_CSUM_NONE: u32 = 0; +pub(crate) const GEM_RX_CSUM_IP_ONLY: u32 = 1; +pub(crate) const GEM_RX_CSUM_IP_TCP: u32 = 2; +pub(crate) const GEM_RX_CSUM_IP_UDP: u32 = 3; + +/* limit RX checksum offload to TCP and UDP packets */ +pub(crate) const GEM_RX_CSUM_CHECKED_MASK: u32 = 2; + +/* Scaled PPM fraction */ +pub(crate) const PPM_FRACTION: u32 = 16; + +/* +static inline bool macb_is_gem(struct macb *bp) +{ + return !!(bp->caps & MACB_CAPS_MACB_IS_GEM); +} + +static inline bool gem_has_ptp(struct macb *bp) +{ + return !!(bp->caps & MACB_CAPS_GEM_HAS_PTP); +} +*/ \ No newline at end of file diff --git a/src/mii_const.rs b/src/mii_const.rs new file mode 100644 index 000000000..dc99dd1c9 --- /dev/null +++ b/src/mii_const.rs @@ -0,0 +1,274 @@ +// linux/mii.h: definitions for MII-compatible transceivers + +/* Generic MII registers. */ +pub(crate) const MII_BMCR: u32 = 0x00; /* Basic mode control register */ +pub(crate) const MII_BMSR: u32 = 0x01; /* Basic mode status register */ +pub(crate) const MII_PHYSID1: u32 = 0x02; /* PHYS ID 1 */ +pub(crate) const MII_PHYSID2: u32 = 0x03; /* PHYS ID 2 */ +pub(crate) const MII_ADVERTISE: u32 = 0x04; /* Advertisement control reg */ +pub(crate) const MII_LPA: u32 = 0x05; /* Link partner ability reg */ +pub(crate) const MII_EXPANSION: u32 = 0x06; /* Expansion register */ +pub(crate) const MII_CTRL1000: u32 = 0x09; /* 1000BASE-T control */ +pub(crate) const MII_STAT1000: u32 = 0x0a; /* 1000BASE-T status */ +pub(crate) const MII_MMD_CTRL: u32 = 0x0d; /* MMD Access Control Register */ +pub(crate) const MII_MMD_DATA: u32 = 0x0e; /* MMD Access Data Register */ +pub(crate) const MII_ESTATUS: u32 = 0x0f; /* Extended Status */ +pub(crate) const MII_DCOUNTER: u32 = 0x12; /* Disconnect counter */ +pub(crate) const MII_FCSCOUNTER: u32 = 0x13; /* False carrier counter */ +pub(crate) const MII_NWAYTEST: u32 = 0x14; /* N-way auto-neg test reg */ +pub(crate) const MII_RERRCOUNTER: u32 = 0x15; /* Receive error counter */ +pub(crate) const MII_SREVISION: u32 = 0x16; /* Silicon revision */ +pub(crate) const MII_RESV1: u32 = 0x17; /* Reserved... */ +pub(crate) const MII_LBRERROR: u32 = 0x18; /* Lpback, rx, bypass error */ +pub(crate) const MII_PHYADDR: u32 = 0x19; /* PHY address */ +pub(crate) const MII_RESV2: u32 = 0x1a; /* Reserved... */ +pub(crate) const MII_TPISTATUS: u32 = 0x1b; /* TPI status for 10mbps */ +pub(crate) const MII_NCONFIG: u32 = 0x1c; /* Network interface config */ + +/* Basic mode control register. */ +pub(crate) const BMCR_RESV: u32 = 0x003f; /* Unused... */ +pub(crate) const BMCR_SPEED1000: u32 = 0x0040; /* MSB of Speed (1000) */ +pub(crate) const BMCR_CTST: u32 = 0x0080; /* Collision test */ +pub(crate) const BMCR_FULLDPLX: u32 = 0x0100; /* Full duplex */ +pub(crate) const BMCR_ANRESTART: u32 = 0x0200; /* Auto negotiation restart */ +pub(crate) const BMCR_ISOLATE: u32 = 0x0400; /* Isolate data paths from MII */ +pub(crate) const BMCR_PDOWN: u32 = 0x0800; /* Enable low power state */ +pub(crate) const BMCR_ANENABLE: u32 = 0x1000; /* Enable auto negotiation */ +pub(crate) const BMCR_SPEED100: u32 = 0x2000; /* Select 100Mbps */ +pub(crate) const BMCR_LOOPBACK: u32 = 0x4000; /* TXD loopback bits */ +pub(crate) const BMCR_RESET: u32 = 0x8000; /* Reset to default state */ +pub(crate) const BMCR_SPEED10: u32 = 0x0000; /* Select 10Mbps */ + +/* Basic mode status register. */ +pub(crate) const BMSR_ERCAP: u32 = 0x0001; /* Ext-reg capability */ +pub(crate) const BMSR_JCD: u32 = 0x0002; /* Jabber detected */ +pub(crate) const BMSR_LSTATUS: u32 = 0x0004; /* Link status */ +pub(crate) const BMSR_ANEGCAPABLE: u32 = 0x0008; /* Able to do auto-negotiation */ +pub(crate) const BMSR_RFAULT: u32 = 0x0010; /* Remote fault detected */ +pub(crate) const BMSR_ANEGCOMPLETE: u32 = 0x0020; /* Auto-negotiation complete */ +pub(crate) const BMSR_RESV: u32 = 0x00c0; /* Unused... */ +pub(crate) const BMSR_ESTATEN: u32 = 0x0100; /* Extended Status in R15 */ +pub(crate) const BMSR_100HALF2: u32 = 0x0200; /* Can do 100BASE-T2 HDX */ +pub(crate) const BMSR_100FULL2: u32 = 0x0400; /* Can do 100BASE-T2 FDX */ +pub(crate) const BMSR_10HALF: u32 = 0x0800; /* Can do 10mbps, half-duplex */ +pub(crate) const BMSR_10FULL: u32 = 0x1000; /* Can do 10mbps, full-duplex */ +pub(crate) const BMSR_100HALF: u32 = 0x2000; /* Can do 100mbps, half-duplex */ +pub(crate) const BMSR_100FULL: u32 = 0x4000; /* Can do 100mbps, full-duplex */ +pub(crate) const BMSR_100BASE4: u32 = 0x8000; /* Can do 100mbps, 4k packets */ + +/* Advertisement control register. */ +pub(crate) const ADVERTISE_SLCT: u32 = 0x001f; /* Selector bits */ +pub(crate) const ADVERTISE_CSMA: u32 = 0x0001; /* Only selector supported */ +pub(crate) const ADVERTISE_10HALF: u32 = 0x0020; /* Try for 10mbps half-duplex */ +pub(crate) const ADVERTISE_1000XFULL: u32 = 0x0020; /* Try for 1000BASE-X full-duplex */ +pub(crate) const ADVERTISE_10FULL: u32 = 0x0040; /* Try for 10mbps full-duplex */ +pub(crate) const ADVERTISE_1000XHALF: u32 = 0x0040; /* Try for 1000BASE-X half-duplex */ +pub(crate) const ADVERTISE_100HALF: u32 = 0x0080; /* Try for 100mbps half-duplex */ +pub(crate) const ADVERTISE_1000XPAUSE: u32 = 0x0080; /* Try for 1000BASE-X pause */ +pub(crate) const ADVERTISE_100FULL: u32 = 0x0100; /* Try for 100mbps full-duplex */ +pub(crate) const ADVERTISE_1000XPSE_ASYM: u32 = 0x0100; /* Try for 1000BASE-X asym pause */ +pub(crate) const ADVERTISE_100BASE4: u32 = 0x0200; /* Try for 100mbps 4k packets */ +pub(crate) const ADVERTISE_PAUSE_CAP: u32 = 0x0400; /* Try for pause */ +pub(crate) const ADVERTISE_PAUSE_ASYM: u32 = 0x0800; /* Try for asymetric pause */ +pub(crate) const ADVERTISE_RESV: u32 = 0x1000; /* Unused... */ +pub(crate) const ADVERTISE_RFAULT: u32 = 0x2000; /* Say we can detect faults */ +pub(crate) const ADVERTISE_LPACK: u32 = 0x4000; /* Ack link partners response */ +pub(crate) const ADVERTISE_NPAGE: u32 = 0x8000; /* Next page bit */ + +pub(crate) const ADVERTISE_FULL: u32 = ADVERTISE_100FULL | ADVERTISE_10FULL | ADVERTISE_CSMA; +pub(crate) const ADVERTISE_ALL: u32 = + ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | ADVERTISE_100FULL; + +/* Link partner ability register. */ +pub(crate) const LPA_SLCT: u32 = 0x001f; /* Same as advertise selector */ +pub(crate) const LPA_10HALF: u32 = 0x0020; /* Can do 10mbps half-duplex */ +pub(crate) const LPA_1000XFULL: u32 = 0x0020; /* Can do 1000BASE-X full-duplex */ +pub(crate) const LPA_10FULL: u32 = 0x0040; /* Can do 10mbps full-duplex */ +pub(crate) const LPA_1000XHALF: u32 = 0x0040; /* Can do 1000BASE-X half-duplex */ +pub(crate) const LPA_100HALF: u32 = 0x0080; /* Can do 100mbps half-duplex */ +pub(crate) const LPA_1000XPAUSE: u32 = 0x0080; /* Can do 1000BASE-X pause */ +pub(crate) const LPA_100FULL: u32 = 0x0100; /* Can do 100mbps full-duplex */ +pub(crate) const LPA_1000XPAUSE_ASYM: u32 = 0x0100; /* Can do 1000BASE-X pause asym*/ +pub(crate) const LPA_100BASE4: u32 = 0x0200; /* Can do 100mbps 4k packets */ +pub(crate) const LPA_PAUSE_CAP: u32 = 0x0400; /* Can pause */ +pub(crate) const LPA_PAUSE_ASYM: u32 = 0x0800; /* Can pause asymetrically */ +pub(crate) const LPA_RESV: u32 = 0x1000; /* Unused... */ +pub(crate) const LPA_RFAULT: u32 = 0x2000; /* Link partner faulted */ +pub(crate) const LPA_LPACK: u32 = 0x4000; /* Link partner acked us */ +pub(crate) const LPA_NPAGE: u32 = 0x8000; /* Next page bit */ + +pub(crate) const LPA_DUPLEX: u32 = LPA_10FULL | LPA_100FULL; +pub(crate) const LPA_100: u32 = LPA_100FULL | LPA_100HALF | LPA_100BASE4; + +/* Expansion register for auto-negotiation. */ +pub(crate) const EXPANSION_NWAY: u32 = 0x0001; /* Can do N-way auto-nego */ +pub(crate) const EXPANSION_LCWP: u32 = 0x0002; /* Got new RX page code word */ +pub(crate) const EXPANSION_ENABLENPAGE: u32 = 0x0004; /* This enables npage words */ +pub(crate) const EXPANSION_NPCAPABLE: u32 = 0x0008; /* Link partner supports npage */ +pub(crate) const EXPANSION_MFAULTS: u32 = 0x0010; /* Multiple faults detected */ +pub(crate) const EXPANSION_RESV: u32 = 0xffe0; /* Unused... */ + +pub(crate) const ESTATUS_1000_XFULL: u32 = 0x8000; /* Can do 1000BaseX Full */ +pub(crate) const ESTATUS_1000_XHALF: u32 = 0x4000; /* Can do 1000BaseX Half */ +pub(crate) const ESTATUS_1000_TFULL: u32 = 0x2000; /* Can do 1000BT Full */ +pub(crate) const ESTATUS_1000_THALF: u32 = 0x1000; /* Can do 1000BT Half */ + +/* N-way test register. */ +pub(crate) const NWAYTEST_RESV1: u32 = 0x00ff; /* Unused... */ +pub(crate) const NWAYTEST_LOOPBACK: u32 = 0x0100; /* Enable loopback for N-way */ +pub(crate) const NWAYTEST_RESV2: u32 = 0xfe00; /* Unused... */ + +/* MAC and PHY tx_config_Reg[15:0] for SGMII in-band auto-negotiation.*/ +pub(crate) const ADVERTISE_SGMII: u32 = 0x0001; /* MAC can do SGMII */ +pub(crate) const LPA_SGMII: u32 = 0x0001; /* PHY can do SGMII */ +pub(crate) const LPA_SGMII_SPD_MASK: u32 = 0x0c00; /* SGMII speed mask */ +pub(crate) const LPA_SGMII_FULL_DUPLEX: u32 = 0x1000; /* SGMII full duplex */ +pub(crate) const LPA_SGMII_DPX_SPD_MASK: u32 = 0x1C00; /* SGMII duplex and speed bits */ +pub(crate) const LPA_SGMII_10: u32 = 0x0000; /* 10Mbps */ +pub(crate) const LPA_SGMII_10HALF: u32 = 0x0000; /* Can do 10mbps half-duplex */ +pub(crate) const LPA_SGMII_10FULL: u32 = 0x1000; /* Can do 10mbps full-duplex */ +pub(crate) const LPA_SGMII_100: u32 = 0x0400; /* 100Mbps */ +pub(crate) const LPA_SGMII_100HALF: u32 = 0x0400; /* Can do 100mbps half-duplex */ +pub(crate) const LPA_SGMII_100FULL: u32 = 0x1400; /* Can do 100mbps full-duplex */ +pub(crate) const LPA_SGMII_1000: u32 = 0x0800; /* 1000Mbps */ +pub(crate) const LPA_SGMII_1000HALF: u32 = 0x0800; /* Can do 1000mbps half-duplex */ +pub(crate) const LPA_SGMII_1000FULL: u32 = 0x1800; /* Can do 1000mbps full-duplex */ +pub(crate) const LPA_SGMII_LINK: u32 = 0x8000; /* PHY link with copper-side partner */ + +/* 1000BASE-T Control register */ +pub(crate) const ADVERTISE_1000FULL: u32 = 0x0200; /* Advertise 1000BASE-T full duplex */ +pub(crate) const ADVERTISE_1000HALF: u32 = 0x0100; /* Advertise 1000BASE-T half duplex */ +pub(crate) const CTL1000_PREFER_MASTER: u32 = 0x0400; /* prefer to operate as master */ +pub(crate) const CTL1000_AS_MASTER: u32 = 0x0800; +pub(crate) const CTL1000_ENABLE_MASTER: u32 = 0x1000; + +/* 1000BASE-T Status register */ +pub(crate) const LPA_1000MSFAIL: u32 = 0x8000; /* Master/Slave resolution failure */ +pub(crate) const LPA_1000MSRES: u32 = 0x4000; /* Master/Slave resolution status */ +pub(crate) const LPA_1000LOCALRXOK: u32 = 0x2000; /* Link partner local receiver status */ +pub(crate) const LPA_1000REMRXOK: u32 = 0x1000; /* Link partner remote receiver status */ +pub(crate) const LPA_1000FULL: u32 = 0x0800; /* Link partner 1000BASE-T full duplex */ +pub(crate) const LPA_1000HALF: u32 = 0x0400; /* Link partner 1000BASE-T half duplex */ + +/* Flow control flags */ +pub(crate) const FLOW_CTRL_TX: u32 = 0x01; +pub(crate) const FLOW_CTRL_RX: u32 = 0x02; + +/* MMD Access Control register fields */ +pub(crate) const MII_MMD_CTRL_DEVAD_MASK: u32 = 0x1f; /* Mask MMD DEVAD*/ +pub(crate) const MII_MMD_CTRL_ADDR: u32 = 0x0000; /* Address */ +pub(crate) const MII_MMD_CTRL_NOINCR: u32 = 0x4000; /* no post increment */ +pub(crate) const MII_MMD_CTRL_INCR_RDWT: u32 = 0x8000; /* post increment on reads & writes */ +pub(crate) const MII_MMD_CTRL_INCR_ON_WT: u32 = 0xC000; /* post increment on writes only */ + +// MDIO +pub(crate) const MDIO_PRTAD_NONE: i32 = -1; +pub(crate) const MDIO_DEVAD_NONE: i32 = -1; +pub(crate) const MDIO_EMULATE_C22: i32 = 4; + +// PHY +pub(crate) const PHY_FIXED_ID: u32 = 0xa5a55a5a; +pub(crate) const PHY_NCSI_ID: u32 = 0xbeefcafe; +pub(crate) const PHY_GMII2RGMII_ID: u32 = 0x5a5a5a5a; +pub(crate) const PHY_MAX_ADDR: u32 = 32; +pub(crate) const PHY_FLAG_BROKEN_RESET: u32 = 1 << 0; /* soft reset not supported */ + +/* phy seed setup */ +pub(crate) const AUTO: u32 = 99; +pub(crate) const _1000BASET: u32 = 1000; +pub(crate) const _100BASET: u32 = 100; +pub(crate) const _10BASET: u32 = 10; +pub(crate) const HALF: u32 = 22; +pub(crate) const FULL: u32 = 44; + +/* phy register offsets */ +pub(crate) const MII_MIPSCR: u32 = 0x11; + +/* MII_LPA */ +pub(crate) const PHY_ANLPAR_PSB_802_3: u32 = 0x0001; +pub(crate) const PHY_ANLPAR_PSB_802_9: u32 = 0x0002; + +/* MII_CTRL1000 masks */ +pub(crate) const PHY_1000BTCR_1000FD: u32 = 0x0200; +pub(crate) const PHY_1000BTCR_1000HD: u32 = 0x0100; + +/* MII_STAT1000 masks */ +pub(crate) const PHY_1000BTSR_MSCF: u32 = 0x8000; +pub(crate) const PHY_1000BTSR_MSCR: u32 = 0x4000; +pub(crate) const PHY_1000BTSR_LRS: u32 = 0x2000; +pub(crate) const PHY_1000BTSR_RRS: u32 = 0x1000; +pub(crate) const PHY_1000BTSR_1000FD: u32 = 0x0800; +pub(crate) const PHY_1000BTSR_1000HD: u32 = 0x0400; + +/* phy EXSR */ +pub(crate) const ESTATUS_1000XF: u32 = 0x8000; +pub(crate) const ESTATUS_1000XH: u32 = 0x4000; + +/* Indicates what features are supported by the interface. */ +pub(crate) const SUPPORTED_10baseT_Half: u32 = 1 << 0; +pub(crate) const SUPPORTED_10baseT_Full: u32 = 1 << 1; +pub(crate) const SUPPORTED_100baseT_Half: u32 = 1 << 2; +pub(crate) const SUPPORTED_100baseT_Full: u32 = 1 << 3; +pub(crate) const SUPPORTED_1000baseT_Half: u32 = 1 << 4; +pub(crate) const SUPPORTED_1000baseT_Full: u32 = 1 << 5; +pub(crate) const SUPPORTED_Autoneg: u32 = 1 << 6; +pub(crate) const SUPPORTED_TP: u32 = 1 << 7; +pub(crate) const SUPPORTED_AUI: u32 = 1 << 8; +pub(crate) const SUPPORTED_MII: u32 = 1 << 9; +pub(crate) const SUPPORTED_FIBRE: u32 = 1 << 10; +pub(crate) const SUPPORTED_BNC: u32 = 1 << 11; +pub(crate) const SUPPORTED_10000baseT_Full: u32 = 1 << 12; +pub(crate) const SUPPORTED_Pause: u32 = 1 << 13; +pub(crate) const SUPPORTED_Asym_Pause: u32 = 1 << 14; +pub(crate) const SUPPORTED_2500baseX_Full: u32 = 1 << 15; +pub(crate) const SUPPORTED_Backplane: u32 = 1 << 16; +pub(crate) const SUPPORTED_1000baseKX_Full: u32 = 1 << 17; +pub(crate) const SUPPORTED_10000baseKX4_Full: u32 = 1 << 18; +pub(crate) const SUPPORTED_10000baseKR_Full: u32 = 1 << 19; +pub(crate) const SUPPORTED_10000baseR_FEC: u32 = 1 << 20; +pub(crate) const SUPPORTED_1000baseX_Half: u32 = 1 << 21; +pub(crate) const SUPPORTED_1000baseX_Full: u32 = 1 << 22; + +/* Indicates what features are advertised by the interface. */ +pub(crate) const ADVERTISED_10baseT_Half: u32 = 1 << 0; +pub(crate) const ADVERTISED_10baseT_Full: u32 = 1 << 1; +pub(crate) const ADVERTISED_100baseT_Half: u32 = 1 << 2; +pub(crate) const ADVERTISED_100baseT_Full: u32 = 1 << 3; +pub(crate) const ADVERTISED_1000baseT_Half: u32 = 1 << 4; +pub(crate) const ADVERTISED_1000baseT_Full: u32 = 1 << 5; +pub(crate) const ADVERTISED_Autoneg: u32 = 1 << 6; +pub(crate) const ADVERTISED_TP: u32 = 1 << 7; +pub(crate) const ADVERTISED_AUI: u32 = 1 << 8; +pub(crate) const ADVERTISED_MII: u32 = 1 << 9; +pub(crate) const ADVERTISED_FIBRE: u32 = 1 << 10; +pub(crate) const ADVERTISED_BNC: u32 = 1 << 11; +pub(crate) const ADVERTISED_10000baseT_Full: u32 = 1 << 12; +pub(crate) const ADVERTISED_Pause: u32 = 1 << 13; +pub(crate) const ADVERTISED_Asym_Pause: u32 = 1 << 14; +pub(crate) const ADVERTISED_2500baseX_Full: u32 = 1 << 15; +pub(crate) const ADVERTISED_Backplane: u32 = 1 << 16; +pub(crate) const ADVERTISED_1000baseKX_Full: u32 = 1 << 17; +pub(crate) const ADVERTISED_10000baseKX4_Full: u32 = 1 << 18; +pub(crate) const ADVERTISED_10000baseKR_Full: u32 = 1 << 19; +pub(crate) const ADVERTISED_10000baseR_FEC: u32 = 1 << 20; +pub(crate) const ADVERTISED_1000baseX_Half: u32 = 1 << 21; +pub(crate) const ADVERTISED_1000baseX_Full: u32 = 1 << 22; + +/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */ +pub(crate) const SPEED_10: u32 = 10; +pub(crate) const SPEED_100: u32 = 100; +pub(crate) const SPEED_1000: u32 = 1000; +pub(crate) const SPEED_2500: u32 = 2500; +pub(crate) const SPEED_10000: u32 = 10000; +pub(crate) const SPEED_14000: u32 = 14000; +pub(crate) const SPEED_20000: u32 = 20000; +pub(crate) const SPEED_25000: u32 = 25000; +pub(crate) const SPEED_40000: u32 = 40000; +pub(crate) const SPEED_50000: u32 = 50000; +pub(crate) const SPEED_56000: u32 = 56000; +pub(crate) const SPEED_100000: u32 = 100000; +pub(crate) const SPEED_200000: u32 = 200000; + +/* Duplex, half or full. */ +pub(crate) const DUPLEX_HALF: u32 = 0x00; +pub(crate) const DUPLEX_FULL: u32 = 0x01; From c2bc4668c79a2a0f939a9ab3882e6460a5bdee01 Mon Sep 17 00:00:00 2001 From: hky1999 <976929993@qq.com> Date: Thu, 9 Jan 2025 00:04:32 +0800 Subject: [PATCH 002/132] Initial commit, basic framework --- .github/workflows/ci.yml | 55 +++++++++ .gitignore | 4 + Cargo.toml | 20 +++ src/consts.rs | 201 ++++++++++++++++++++++++++++++ src/lib.rs | 122 +++++++++++++++++++ src/vlapic.rs | 255 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 657 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/consts.rs create mode 100644 src/lib.rs create mode 100644 src/vlapic.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..4a2d898e7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: CI + +on: [push, pull_request] + +jobs: + ci: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rust-toolchain: [nightly] + targets: [x86_64-unknown-none] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: ${{ matrix.rust-toolchain }} + components: rust-src, clippy, rustfmt + targets: ${{ matrix.targets }} + - name: Check rust version + run: rustc --version --verbose + - name: Check code format + run: cargo fmt --all -- --check + - name: Clippy + run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default + - name: Build + run: cargo build --target ${{ matrix.targets }} --all-features + - name: Unit test + if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} + run: cargo test --target ${{ matrix.targets }} -- --nocapture + + doc: + runs-on: ubuntu-latest + strategy: + fail-fast: false + permissions: + contents: write + env: + default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - name: Build docs + continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} + run: | + cargo doc --no-deps --all-features + printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html + - name: Deploy to Github Pages + if: ${{ github.ref == env.default-branch }} + uses: JamesIves/github-pages-deploy-action@v4 + with: + single-commit: true + branch: gh-pages + folder: target/doc diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ff78c42af --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +/.vscode +.DS_Store +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..e1adc0fac --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "x86_vlapic" +version = "0.1.0" +edition = "2024" +description = "x86 Virtual Local APIC" + +[dependencies] +log = "0.4.19" +memory_addr = "0.3" +axerrno = "0.1.0" +paste = "1.0.15" + +axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git" } +axdevice_base = { git = "https://github.com/arceos-hypervisor/axdevice_crates.git"} + +[patch."https://github.com/arceos-hypervisor/axaddrspace.git"] +axaddrspace = { path = "../axaddrspace" } + +[patch."https://github.com/arceos-hypervisor/axdevice_crates.git"] +axdevice_base = { path = "../axdevice_crates/axdevice_base" } \ No newline at end of file diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 000000000..b18a3b2d6 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,201 @@ +use paste::paste; + +macro_rules! define_index_enum { + ($name:ident) => { + paste! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum $name { + [<$name 0>] = 0, + [<$name 1>] = 1, + [<$name 2>] = 2, + [<$name 3>] = 3, + [<$name 4>] = 4, + [<$name 5>] = 5, + [<$name 6>] = 6, + [<$name 7>] = 7, + } + + impl $name { + const fn from(value: usize) -> Self { + match value { + 0 => $name::[<$name 0>], + 1 => $name::[<$name 1>], + 2 => $name::[<$name 2>], + 3 => $name::[<$name 3>], + 4 => $name::[<$name 4>], + 5 => $name::[<$name 5>], + 6 => $name::[<$name 6>], + 7 => $name::[<$name 7>], + _ => panic!("Invalid index"), + } + } + + pub const fn as_usize(&self) -> usize { + *self as usize + } + } + + impl core::fmt::Display for $name { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + $name::[<$name 0>] => write!(f, "{}0", stringify!($name)), + $name::[<$name 1>] => write!(f, "{}1", stringify!($name)), + $name::[<$name 2>] => write!(f, "{}2", stringify!($name)), + $name::[<$name 3>] => write!(f, "{}3", stringify!($name)), + $name::[<$name 4>] => write!(f, "{}4", stringify!($name)), + $name::[<$name 5>] => write!(f, "{}5", stringify!($name)), + $name::[<$name 6>] => write!(f, "{}6", stringify!($name)), + $name::[<$name 7>] => write!(f, "{}7", stringify!($name)), + } + } + } + } + }; +} + +define_index_enum!(ISRIndex); +define_index_enum!(TMRIndex); +define_index_enum!(IRRIndex); + +#[derive(Debug)] +pub enum ApicRegOffset { + /// ID register 0x2. + ID, + /// Version register 0x3. + Version, + /// Task Priority register 0x8. + TPR, + /// Arbitration Priority register 0x9. + APR, + /// Processor Priority register 0xA. + PPR, + /// EOI register 0xB. + EOI, + /// Remote Read register 0xC. + RRR, + /// Logical Destination Register 0xD. + LDR, + /// Destination Format register 0xE. + DFR, + /// Spurious Interrupt Vector register 0xF. + SIVR, + /// In-Service register 0x10..=0x17. + ISR(ISRIndex), + /// Trigger Mode register 0x18..=0x1F. + TMR(TMRIndex), + /// Interrupt Request register 0x20..=0x27. + IRR(IRRIndex), + /// Error Status register 0x28. + ESR, + /// LVT CMCI register 0x2F. + LvtCMCI, + /// Interrupt Command register 0x30. + ICR, + /// LVT Timer Interrupt register 0x32. + LvtTimer, + /// LVT Thermal Sensor Interrupt register 0x33. + LvtThermal, + /// LVT Performance Monitor register 0x34. + LvtPmi, + /// LVT LINT0 register 0x35. + LvtLint0, + /// LVT LINT1 register 0x36. + LvtLint1, + /// LVT Error register 0x37. + LvtErr, + /// Initial Count register (for Timer) 0x38. + TimerInitCount, + /// Current Count register (for Timer) 0x39. + TimerCurCount, + /// Divide Configuration register (for Timer) 0x3E. + TimerDivConf, +} + +impl ApicRegOffset { + const fn from(value: usize) -> Self { + match value as u32 { + 0x2 => ApicRegOffset::ID, + 0x3 => ApicRegOffset::Version, + 0x8 => ApicRegOffset::TPR, + 0x9 => ApicRegOffset::APR, + 0xA => ApicRegOffset::PPR, + 0xB => ApicRegOffset::EOI, + 0xC => ApicRegOffset::RRR, + 0xD => ApicRegOffset::LDR, + 0xE => ApicRegOffset::DFR, + 0xF => ApicRegOffset::SIVR, + 0x10..=0x17 => ApicRegOffset::ISR(ISRIndex::from(value - 0x10)), + 0x18..=0x1F => ApicRegOffset::TMR(TMRIndex::from(value - 0x18)), + 0x20..=0x27 => ApicRegOffset::IRR(IRRIndex::from(value - 0x20)), + 0x28 => ApicRegOffset::ESR, + 0x2F => ApicRegOffset::LvtCMCI, + 0x30 => ApicRegOffset::ICR, + 0x32 => ApicRegOffset::LvtTimer, + 0x33 => ApicRegOffset::LvtThermal, + 0x34 => ApicRegOffset::LvtPmi, + 0x35 => ApicRegOffset::LvtLint0, + 0x36 => ApicRegOffset::LvtLint1, + 0x37 => ApicRegOffset::LvtErr, + 0x38 => ApicRegOffset::TimerInitCount, + 0x39 => ApicRegOffset::TimerCurCount, + 0x3E => ApicRegOffset::TimerDivConf, + _ => panic!("Invalid APIC register offset"), + } + } +} + +impl core::fmt::Display for ApicRegOffset { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + ApicRegOffset::ID => write!(f, "ID"), + ApicRegOffset::Version => write!(f, "Version"), + ApicRegOffset::TPR => write!(f, "TPR"), + ApicRegOffset::APR => write!(f, "APR"), + ApicRegOffset::PPR => write!(f, "PPR"), + ApicRegOffset::EOI => write!(f, "EOI"), + ApicRegOffset::RRR => write!(f, "RRR"), + ApicRegOffset::LDR => write!(f, "LDR"), + ApicRegOffset::DFR => write!(f, "DFR"), + ApicRegOffset::SIVR => write!(f, "SIVR"), + ApicRegOffset::ISR(index) => write!(f, "{:?}", index), + ApicRegOffset::TMR(index) => write!(f, "{:?}", index), + ApicRegOffset::IRR(index) => write!(f, "{:?}", index), + ApicRegOffset::ESR => write!(f, "ESR"), + ApicRegOffset::LvtCMCI => write!(f, "LvtCMCI"), + ApicRegOffset::ICR => write!(f, "ICR"), + ApicRegOffset::LvtTimer => write!(f, "LvtTimer"), + ApicRegOffset::LvtThermal => write!(f, "LvtThermal"), + ApicRegOffset::LvtPmi => write!(f, "LvtPmi"), + ApicRegOffset::LvtLint0 => write!(f, "LvtLint0"), + ApicRegOffset::LvtLint1 => write!(f, "LvtLint1"), + ApicRegOffset::LvtErr => write!(f, "LvtErr"), + ApicRegOffset::TimerInitCount => write!(f, "TimerInitCount"), + ApicRegOffset::TimerCurCount => write!(f, "TimerCurCount"), + ApicRegOffset::TimerDivConf => write!(f, "TimerDivConf"), + } + } +} + +pub mod xapic { + use super::ApicRegOffset; + + pub const DEFAULT_APIC_BASE: usize = 0xFEE0_0000; + pub const APIC_MMIO_SIZE: usize = 0x1000; + + pub(crate) const fn xapic_mmio_access_reg_offset(addr: usize) -> ApicRegOffset { + ApicRegOffset::from((addr & (APIC_MMIO_SIZE - 1)) >> 4) + } +} + +pub mod x2apic { + use axaddrspace::device::SysRegAddr; + + use super::ApicRegOffset; + + pub const X2APIC_MSE_REG_BASE: usize = 0x800; + pub const X2APIC_MSE_REG_SIZE: usize = 0x100; + + pub(crate) const fn x2apic_mmio_access_reg(addr: SysRegAddr) -> ApicRegOffset { + ApicRegOffset::from(addr.addr() - X2APIC_MSE_REG_BASE) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..2e716b0d4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,122 @@ +#![no_std] + +extern crate alloc; + +#[macro_use] +extern crate log; + +mod consts; +mod vlapic; + +use alloc::boxed::Box; + +use axerrno::AxResult; +use memory_addr::AddrRange; + +use axaddrspace::device::{AccessWidth, SysRegAddr, SysRegAddrRange}; +use axaddrspace::{AxMmHal, GuestPhysAddr}; +use axdevice_base::{BaseDeviceOps, DeviceRWContext, EmuDeviceType, InterruptInjector}; + +use crate::vlapic::VirtualApicRegs; + +pub struct EmulatedLocalApic { + vlapic_regs: VirtualApicRegs, +} + +impl EmulatedLocalApic { + pub fn new() -> Self { + EmulatedLocalApic { + vlapic_regs: VirtualApicRegs::new(), + } + } +} + +impl BaseDeviceOps> for EmulatedLocalApic { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::EmuDeviceTInterruptController + } + + fn address_range(&self) -> AddrRange { + use crate::consts::xapic::{APIC_MMIO_SIZE, DEFAULT_APIC_BASE}; + AddrRange::new( + GuestPhysAddr::from_usize(DEFAULT_APIC_BASE), + GuestPhysAddr::from_usize(DEFAULT_APIC_BASE + APIC_MMIO_SIZE), + ) + } + + fn handle_read( + &self, + addr: GuestPhysAddr, + width: AccessWidth, + context: DeviceRWContext, + ) -> AxResult { + debug!( + "EmulatedLocalApic::handle_read: addr={:?}, width={:?}, context={:?}", + addr, width, context.vcpu_id + ); + todo!() + } + + fn handle_write( + &self, + addr: GuestPhysAddr, + width: AccessWidth, + val: usize, + context: DeviceRWContext, + ) -> AxResult { + debug!( + "EmulatedLocalApic::handle_write: addr={:?}, width={:?}, val={:#x}, context={:?}", + addr, width, val, context.vcpu_id + ); + todo!() + } + + fn set_interrupt_injector(&mut self, _injector: Box) { + todo!() + } +} + +impl BaseDeviceOps for EmulatedLocalApic { + fn emu_type(&self) -> EmuDeviceType { + EmuDeviceType::EmuDeviceTInterruptController + } + + fn address_range(&self) -> SysRegAddrRange { + use crate::consts::x2apic::{X2APIC_MSE_REG_BASE, X2APIC_MSE_REG_SIZE}; + SysRegAddrRange::new( + SysRegAddr(X2APIC_MSE_REG_BASE), + SysRegAddr(X2APIC_MSE_REG_BASE + X2APIC_MSE_REG_SIZE), + ) + } + + fn handle_read( + &self, + addr: SysRegAddr, + width: AccessWidth, + context: DeviceRWContext, + ) -> AxResult { + debug!( + "EmulatedLocalApic::handle_read: addr={:?}, width={:?}, context={:?}", + addr, width, context.vcpu_id + ); + todo!() + } + + fn handle_write( + &self, + addr: SysRegAddr, + width: AccessWidth, + val: usize, + context: DeviceRWContext, + ) -> AxResult { + debug!( + "EmulatedLocalApic::handle_write: addr={:?}, width={:?}, val={:#x}, context={:?}", + addr, width, val, context.vcpu_id + ); + todo!() + } + + fn set_interrupt_injector(&mut self, _injector: Box) { + todo!() + } +} diff --git a/src/vlapic.rs b/src/vlapic.rs new file mode 100644 index 000000000..bc6d96c48 --- /dev/null +++ b/src/vlapic.rs @@ -0,0 +1,255 @@ +use paste::paste; + +use memory_addr::PAGE_SIZE_4K; + +use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr, PhysFrame}; + +use crate::consts::ApicRegOffset; + +#[repr(align(4096))] +struct APICAccessPage([u8; PAGE_SIZE_4K]); + +static VIRTUAL_APIC_ACCESS_PAGE: APICAccessPage = APICAccessPage([0; PAGE_SIZE_4K]); + +// Virtual-APIC Registers. +pub struct VirtualApicRegs { + /// The virtual-APIC page is a 4-KByte region of memory + /// that the processor uses to virtualize certain accesses to APIC registers and to manage virtual interrupts. + /// The physical address of the virtual-APIC page is the virtual-APIC address, + /// a 64-bit VM-execution control field in the VMCS (see Section 25.6.8). + virtual_apic_page: PhysFrame, +} + +impl VirtualApicRegs { + pub fn new() -> Self { + Self { + virtual_apic_page: PhysFrame::alloc_zero().expect("allocate virtual-APIC page failed"), + } + } + + /// APIC-access address (64 bits). + /// This field contains the physical address of the 4-KByte APIC-access page. + /// If the “virtualize APIC accesses” VM-execution control is 1, + /// access to this page may cause VM exits or be virtualized by the processor. + /// See Section 30.4. + pub fn virtual_apic_access_addr() -> HostPhysAddr { + H::virt_to_phys(HostVirtAddr::from_usize( + VIRTUAL_APIC_ACCESS_PAGE.0.as_ptr() as usize, + )) + } + + /// Virtual-APIC address (64 bits). + /// This field contains the physical address of the 4-KByte virtual-APIC page. + /// The processor uses the virtual-APIC page to virtualize certain accesses to APIC registers and to manage virtual interrupts; + /// see Chapter 30. + pub fn virtual_apic_page_addr(&self) -> HostPhysAddr { + self.virtual_apic_page.start_paddr() + } +} + +impl Drop for VirtualApicRegs { + fn drop(&mut self) { + H::dealloc_frame(self.virtual_apic_page.start_paddr()); + } +} + +macro_rules! impl_virtual_apic_set_reg { + ($name:ident, $offset:expr) => { + paste! { + #[doc = concat!("Sets the ", stringify!($name), " register.")] + fn [](&self, value: u32) { + let ptr = self.virtual_apic_page.as_mut_ptr().wrapping_add($offset) as *mut u32; + unsafe { ptr.write_volatile(value) } + } + } + }; + ($name:ident, $offset:expr, $length:expr) => { + paste! { + #[doc = concat!("Sets the ", stringify!($name), " register at the given index.")] + fn [](&self, index: usize, value: u32) { + assert!(index < $length); + let ptr = self + .virtual_apic_page + .as_mut_ptr() + .wrapping_add($offset + index * 0x10) as *mut u32; + unsafe { ptr.write_volatile(value) } + } + } + }; + () => {}; +} + +macro_rules! impl_virtual_apic_get_reg { + ($name:ident, $offset:expr) => { + paste! { + #[doc = concat!("Gets the ", stringify!($name), " register.")] + fn [](&self) -> u32 { + let ptr = self.virtual_apic_page.as_mut_ptr().wrapping_add($offset) as *mut u32; + unsafe { ptr.read_volatile() } + } + } + }; + ($name:ident, $offset:expr, $length:expr) => { + paste! { + #[doc = concat!("Gets the ", stringify!($name), " register at the given index.")] + fn [](&self, index: usize) -> u32 { + assert!(index < $length); + let ptr = self + .virtual_apic_page + .as_mut_ptr() + .wrapping_add($offset + index * 0x10) as *mut u32; + unsafe { ptr.read_volatile() } + } + } + }; + () => {}; +} + +macro_rules! impl_virtual_apic_regs_get_set { + ($(($name:ident, $offset:expr)),* $(,)?) => { + $( + impl_virtual_apic_get_reg!($name, $offset); + impl_virtual_apic_set_reg!($name, $offset); + )* + }; + ($(($name:ident, $offset:expr, $length:expr)),* $(,)?) => { + $( + impl_virtual_apic_get_reg!($name, $offset, $length); + impl_virtual_apic_set_reg!($name, $offset, $length); + )* + }; + () => {}; +} + +/// 30.1 VIRTUAL APIC STATE +/// 30.1.1 Virtualized APIC Registers +#[allow(unused)] +impl VirtualApicRegs { + impl_virtual_apic_regs_get_set!( + // Local APIC ID register (VID): the 32-bit field located at offset 000H on the virtual-APIC page. + (id, 0x20), + // Local APIC Version register (VVER): the 32-bit field located at offset 030H on the virtual-APIC page. + (version, 0x30), + // Virtual task-priority register (VTPR): the 32-bit field located at offset 080H on the virtual-APIC page. + (tpr, 0x80), + // Virtual APIC-priority register (VAPR): the 32-bit field located at offset 090H on the virtual-APIC page. + (apr, 0x90), + // Virtual processor-priority register (VPPR): the 32-bit field located at offset 0A0H on the virtual-APIC page. + (ppr, 0xA0), + // Virtual end-of-interrupt register (VEOI): the 32-bit field located at offset 0B0H on the virtual-APIC page. + (eoi, 0xB0), + // Virtual Remote Read Register1 (RRD): the 32-bit field located at offset 0C0H on the virtual-APIC page. + (rrd, 0xC0), + // Virtual Remote Read Register1 (RRD): the 32-bit field located at offset 0D0H on the virtual-APIC page. + (ldr, 0xD0), + // Virtual Destination Format Register (DFR): the 32-bit field located at offset 0E0H on the virtual-APIC page. + (dfr, 0xE0), + // Virtual Spurious Interrupt Vector Register (SVR): the 32-bit field located at offset 0F0H on the virtual-APIC page. + (svr, 0xF0), + // Virtual Error Status Register (VESR): the 32-bit field located at offset 280H on the virtual-APIC page. + (esr, 0x280), + // Virtual LVT Corrected Machine Check Interrupt (CMCI) Register + (lvt_cmci, 0x2F0), + // Virtual Interrupt Command Register (ICR): the 64-bit field located at offset 300H on the virtual-APIC page. + (icr_lo, 0x300), + (icr_hi, 0x310), + // Virtual LVT Timer Register: the 32-bit field located at offset 320H on the virtual-APIC page. + (lvt_timer, 0x320), + // Virtual LVT Thermal Sensor register: the 32-bit field located at offset 330H on the virtual-APIC page. + (lvt_thermal, 0x330), + // Virtual LVT Performance Monitoring Counters register: the 32-bit field located at offset 340H on the virtual-APIC page. + (lvt_pmi, 0x340), + // Virtual LVT LINT0 register: the 32-bit field located at offset 350H on the virtual-APIC page. + (lvt_lint0, 0x350), + // Virtual LVT LINT1 register: the 32-bit field located at offset 360H on the virtual-APIC page. + (lvt_lint1, 0x360), + // Virtual LVT Error register: the 32-bit field located at offset 370H on the virtual-APIC page. + (lvt_error, 0x370), + // Virtual Initial Count Register (for Timer): the 32-bit field located at offset 380H on the virtual-APIC page. + (icr_timer, 0x380), + // Virtual Current Count Register (for Timer): the 32-bit field located at offset 390H on the virtual-APIC page. + (ccr_timer, 0x390), + // Virtual Divide Configuration Register (for Timer): the 32-bit field located at offset 3E0H on the virtual-APIC page. + (dcr_timer, 0x3E0), + // Virtual SELF IPI Register: the 32-bit field located at offset 3F0H on the virtual-APIC page. + (self_ipi, 0x3F0) + ); + + impl_virtual_apic_regs_get_set!( + // Virtual interrupt-service register (VISR): + // the 256-bit value comprising eight non-contiguous 32-bit fields at offsets + // 100H, 110H, 120H, 130H, 140H, 150H, 160H, and 170H on the virtual-APIC page. + (isr, 0x100, 8), + // Virtual trigger-mode register (VTMR): + // the 256-bit value comprising eight non-contiguous 32-bit fields at offsets + // 180H, 190H, 1A0H, 1B0H, 1C0H, 1D0H, 1E0H, and 1F0H on the virtual-APIC page. + (tmr, 0x180, 8), + // Virtual interrupt-request register (VIRR): + // the 256-bit value comprising eight non-contiguous 32-bit fields at offsets + // 200H, 210H, 220H, 230H, 240H, 250H, 260H, and 270H on the virtual-APIC page. + // Bit x of the VIRR is at bit position (x & 1FH) at offset (200H | ((x & E0H) » 1)). + // The processor uses only the low 4 bytes of each of the 16-Byte fields at offsets 200H, 210H, 220H, 230H, 240H, 250H, 260H, and 270H. + (irr, 0x200, 8), + ); +} + +impl VirtualApicRegs { + /// Returns the host physical address of the virtual-APIC page. + pub fn virtual_apic_page_host_paddr(&self) -> HostPhysAddr { + self.virtual_apic_page.start_paddr() + } + + pub fn handle_read(&self, offset: ApicRegOffset) -> u32 { + let mut value = 0; + match offset { + ApicRegOffset::ID => { + value = self.get_id(); + debug!("[VLAPIC] read APIC ID register: {:#010X}", value); + } + ApicRegOffset::Version => { + value = self.get_version(); + debug!("[VLAPIC] read APIC Version register: {:#010X}", value); + } + ApicRegOffset::PPR => { + value = self.get_ppr(); + debug!("[VLAPIC] read PPR register: {:#010X}", value); + } + ApicRegOffset::EOI => { + value = self.get_eoi(); + debug!("[VLAPIC] read EOI register: {:#010X}", value); + } + ApicRegOffset::LDR => { + value = self.get_ldr(); + debug!("[VLAPIC] read LDR register: {:#010X}", value); + } + ApicRegOffset::DFR => { + value = self.get_dfr(); + debug!("[VLAPIC] read DFR register: {:#010X}", value); + } + ApicRegOffset::SIVR => { + value = self.get_svr(); + debug!("[VLAPIC] read SIVR register: {:#010X}", value); + } + ApicRegOffset::ISR(index) => { + value = self.get_isr(index.as_usize()); + debug!("[VLAPIC] read ISR[{}] register: {:#010X}", index, value); + } + ApicRegOffset::TMR(index) => { + value = self.get_tmr(index.as_usize()); + debug!("[VLAPIC] read TMR[{}] register: {:#010X}", index, value); + } + ApicRegOffset::IRR(index) => { + value = self.get_irr(index.as_usize()); + debug!("[VLAPIC] read IRR[{}] register: {:#010X}", index, value); + } + ApicRegOffset::ESR => { + value = self.get_esr(); + debug!("[VLAPIC] read ESR register: {:#010X}", value); + } + _ => { + warn!("[VLAPIC] read unknown APIC register: {:?}", offset); + } + } + value + } +} From 9b85fb9db7440ec1f867ecbb5c2549862b2fb5f3 Mon Sep 17 00:00:00 2001 From: hky1999 Date: Sat, 11 Jan 2025 11:36:44 +0800 Subject: [PATCH 003/132] [feat] add lvt regs (#1) --- Cargo.toml | 16 ++- src/consts.rs | 35 +++-- src/lib.rs | 49 ++++++- src/lvt.rs | 38 +++++ src/regs/lvt/cmci.rs | 73 ++++++++++ src/regs/lvt/error.rs | 44 ++++++ src/regs/lvt/lint0.rs | 97 +++++++++++++ src/regs/lvt/lint1.rs | 97 +++++++++++++ src/regs/lvt/mod.rs | 15 ++ src/regs/lvt/perfmon.rs | 74 ++++++++++ src/regs/lvt/thermal.rs | 73 ++++++++++ src/regs/lvt/timer.rs | 59 ++++++++ src/regs/mod.rs | 104 ++++++++++++++ src/regs/svr.rs | 70 ++++++++++ src/vlapic.rs | 299 ++++++++++++++++------------------------ 15 files changed, 936 insertions(+), 207 deletions(-) create mode 100644 src/lvt.rs create mode 100644 src/regs/lvt/cmci.rs create mode 100644 src/regs/lvt/error.rs create mode 100644 src/regs/lvt/lint0.rs create mode 100644 src/regs/lvt/lint1.rs create mode 100644 src/regs/lvt/mod.rs create mode 100644 src/regs/lvt/perfmon.rs create mode 100644 src/regs/lvt/thermal.rs create mode 100644 src/regs/lvt/timer.rs create mode 100644 src/regs/mod.rs create mode 100644 src/regs/svr.rs diff --git a/Cargo.toml b/Cargo.toml index e1adc0fac..6381a732b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,15 +6,17 @@ description = "x86 Virtual Local APIC" [dependencies] log = "0.4.19" +paste = "1.0.15" +tock-registers = "0.9.0" + memory_addr = "0.3" axerrno = "0.1.0" -paste = "1.0.15" -axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git" } -axdevice_base = { git = "https://github.com/arceos-hypervisor/axdevice_crates.git"} +axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git", branch = "inject_interrupt" } +axdevice_base = { git = "https://github.com/arceos-hypervisor/axdevice_crates.git", branch = "inject_interrupt"} -[patch."https://github.com/arceos-hypervisor/axaddrspace.git"] -axaddrspace = { path = "../axaddrspace" } +# [patch."https://github.com/arceos-hypervisor/axaddrspace.git"] +# axaddrspace = { path = "../axaddrspace" } -[patch."https://github.com/arceos-hypervisor/axdevice_crates.git"] -axdevice_base = { path = "../axdevice_crates/axdevice_base" } \ No newline at end of file +# [patch."https://github.com/arceos-hypervisor/axdevice_crates.git"] +# axdevice_base = { path = "../axdevice_crates/axdevice_base" } \ No newline at end of file diff --git a/src/consts.rs b/src/consts.rs index b18a3b2d6..1263628d3 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -90,13 +90,15 @@ pub enum ApicRegOffset { /// LVT CMCI register 0x2F. LvtCMCI, /// Interrupt Command register 0x30. - ICR, + ICRLow, + /// Interrupt Command register high 0x30. + ICRHi, /// LVT Timer Interrupt register 0x32. LvtTimer, /// LVT Thermal Sensor Interrupt register 0x33. LvtThermal, - /// LVT Performance Monitor register 0x34. - LvtPmi, + /// LVT Performance Monitoring Counters Register 0x34. + LvtPmc, /// LVT LINT0 register 0x35. LvtLint0, /// LVT LINT1 register 0x36. @@ -129,10 +131,11 @@ impl ApicRegOffset { 0x20..=0x27 => ApicRegOffset::IRR(IRRIndex::from(value - 0x20)), 0x28 => ApicRegOffset::ESR, 0x2F => ApicRegOffset::LvtCMCI, - 0x30 => ApicRegOffset::ICR, + 0x30 => ApicRegOffset::ICRLow, + 0x31 => ApicRegOffset::ICRHi, 0x32 => ApicRegOffset::LvtTimer, 0x33 => ApicRegOffset::LvtThermal, - 0x34 => ApicRegOffset::LvtPmi, + 0x34 => ApicRegOffset::LvtPmc, 0x35 => ApicRegOffset::LvtLint0, 0x36 => ApicRegOffset::LvtLint1, 0x37 => ApicRegOffset::LvtErr, @@ -162,10 +165,11 @@ impl core::fmt::Display for ApicRegOffset { ApicRegOffset::IRR(index) => write!(f, "{:?}", index), ApicRegOffset::ESR => write!(f, "ESR"), ApicRegOffset::LvtCMCI => write!(f, "LvtCMCI"), - ApicRegOffset::ICR => write!(f, "ICR"), + ApicRegOffset::ICRLow => write!(f, "ICR_LOW"), + ApicRegOffset::ICRHi => write!(f, "ICR_HI"), ApicRegOffset::LvtTimer => write!(f, "LvtTimer"), ApicRegOffset::LvtThermal => write!(f, "LvtThermal"), - ApicRegOffset::LvtPmi => write!(f, "LvtPmi"), + ApicRegOffset::LvtPmc => write!(f, "LvtPerformanceMonitoringCounter"), ApicRegOffset::LvtLint0 => write!(f, "LvtLint0"), ApicRegOffset::LvtLint1 => write!(f, "LvtLint1"), ApicRegOffset::LvtErr => write!(f, "LvtErr"), @@ -176,14 +180,25 @@ impl core::fmt::Display for ApicRegOffset { } } +/// 11.5.1 Local Vector Table +/// Figure 11-8. Local Vector Table (LVT) +/// - Value After Reset: 0001 0000H +pub const RESET_LVT_REG: u32 = 0x0001_0000; +/// 11.9 SPURIOUS INTERRUPT +/// - Address: FEE0 00F0H +/// - Value after reset: 0000 00FFH +pub const RESET_SPURIOUS_INTERRUPT_VECTOR: u32 = 0x0000_00FF; + pub mod xapic { + use axaddrspace::GuestPhysAddr; + use super::ApicRegOffset; pub const DEFAULT_APIC_BASE: usize = 0xFEE0_0000; pub const APIC_MMIO_SIZE: usize = 0x1000; - pub(crate) const fn xapic_mmio_access_reg_offset(addr: usize) -> ApicRegOffset { - ApicRegOffset::from((addr & (APIC_MMIO_SIZE - 1)) >> 4) + pub(crate) const fn xapic_mmio_access_reg_offset(addr: GuestPhysAddr) -> ApicRegOffset { + ApicRegOffset::from((addr.as_usize() & (APIC_MMIO_SIZE - 1)) >> 4) } } @@ -195,7 +210,7 @@ pub mod x2apic { pub const X2APIC_MSE_REG_BASE: usize = 0x800; pub const X2APIC_MSE_REG_SIZE: usize = 0x100; - pub(crate) const fn x2apic_mmio_access_reg(addr: SysRegAddr) -> ApicRegOffset { + pub(crate) const fn x2apic_msr_access_reg(addr: SysRegAddr) -> ApicRegOffset { ApicRegOffset::from(addr.addr() - X2APIC_MSE_REG_BASE) } } diff --git a/src/lib.rs b/src/lib.rs index 2e716b0d4..0beeff8ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +//! Emulated Local APIC. #![no_std] extern crate alloc; @@ -6,24 +7,35 @@ extern crate alloc; extern crate log; mod consts; +mod lvt; +mod regs; mod vlapic; use alloc::boxed::Box; use axerrno::AxResult; -use memory_addr::AddrRange; +use memory_addr::{AddrRange, PAGE_SIZE_4K}; use axaddrspace::device::{AccessWidth, SysRegAddr, SysRegAddrRange}; -use axaddrspace::{AxMmHal, GuestPhysAddr}; +use axaddrspace::{AxMmHal, GuestPhysAddr, HostPhysAddr, HostVirtAddr}; use axdevice_base::{BaseDeviceOps, DeviceRWContext, EmuDeviceType, InterruptInjector}; +use crate::consts::x2apic::x2apic_msr_access_reg; +use crate::consts::xapic::xapic_mmio_access_reg_offset; use crate::vlapic::VirtualApicRegs; +#[repr(align(4096))] +struct APICAccessPage([u8; PAGE_SIZE_4K]); + +static VIRTUAL_APIC_ACCESS_PAGE: APICAccessPage = APICAccessPage([0; PAGE_SIZE_4K]); + +/// A emulated local APIC device. pub struct EmulatedLocalApic { vlapic_regs: VirtualApicRegs, } impl EmulatedLocalApic { + /// Create a new `EmulatedLocalApic`. pub fn new() -> Self { EmulatedLocalApic { vlapic_regs: VirtualApicRegs::new(), @@ -31,6 +43,27 @@ impl EmulatedLocalApic { } } +impl EmulatedLocalApic { + /// APIC-access address (64 bits). + /// This field contains the physical address of the 4-KByte APIC-access page. + /// If the “virtualize APIC accesses” VM-execution control is 1, + /// access to this page may cause VM exits or be virtualized by the processor. + /// See Section 30.4. + pub fn virtual_apic_access_addr() -> HostPhysAddr { + H::virt_to_phys(HostVirtAddr::from_usize( + VIRTUAL_APIC_ACCESS_PAGE.0.as_ptr() as usize, + )) + } + + /// Virtual-APIC address (64 bits). + /// This field contains the physical address of the 4-KByte virtual-APIC page. + /// The processor uses the virtual-APIC page to virtualize certain accesses to APIC registers and to manage virtual interrupts; + /// see Chapter 30. + pub fn virtual_apic_page_addr(&self) -> HostPhysAddr { + self.vlapic_regs.virtual_apic_page_addr() + } +} + impl BaseDeviceOps> for EmulatedLocalApic { fn emu_type(&self) -> EmuDeviceType { EmuDeviceType::EmuDeviceTInterruptController @@ -54,7 +87,8 @@ impl BaseDeviceOps> for EmulatedLocalApic BaseDeviceOps> for EmulatedLocalApic) { @@ -99,7 +134,8 @@ impl BaseDeviceOps for EmulatedLocalApic { "EmulatedLocalApic::handle_read: addr={:?}, width={:?}, context={:?}", addr, width, context.vcpu_id ); - todo!() + let reg_off = x2apic_msr_access_reg(addr); + self.vlapic_regs.handle_read(reg_off, width, context) } fn handle_write( @@ -113,7 +149,8 @@ impl BaseDeviceOps for EmulatedLocalApic { "EmulatedLocalApic::handle_write: addr={:?}, width={:?}, val={:#x}, context={:?}", addr, width, val, context.vcpu_id ); - todo!() + let reg_off = x2apic_msr_access_reg(addr); + self.vlapic_regs.handle_write(reg_off, width, context) } fn set_interrupt_injector(&mut self, _injector: Box) { diff --git a/src/lvt.rs b/src/lvt.rs new file mode 100644 index 000000000..ad96733db --- /dev/null +++ b/src/lvt.rs @@ -0,0 +1,38 @@ +//! Local Vector Table + +use crate::consts::RESET_LVT_REG; +use crate::regs::lvt::{ + LvtCmciRegisterLocal, LvtErrorRegisterLocal, LvtLint0RegisterLocal, LvtLint1RegisterLocal, + LvtPerformanceCounterRegisterLocal, LvtThermalMonitorRegisterLocal, LvtTimerRegisterLocal, +}; + +pub struct LocalVectorTable { + /// LVT CMCI Register (FEE0 02F0H) + pub lvt_cmci: LvtCmciRegisterLocal, + /// LVT Timer Register (FEE0 0320H) + pub lvt_timer: LvtTimerRegisterLocal, + /// LVT Thermal Monitor Register (FEE0 0330H) + pub lvt_thermal: LvtThermalMonitorRegisterLocal, + /// LVT Performance Counter Register (FEE0 0340H) + pub lvt_perf_count: LvtPerformanceCounterRegisterLocal, + /// LVT LINT0 Register (FEE0 0350H) + pub lvt_lint0: LvtLint0RegisterLocal, + /// LVT LINT1 Register (FEE0 0360H) + pub lvt_lint1: LvtLint1RegisterLocal, + /// LVT Error register 0x37. + pub lvt_err: LvtErrorRegisterLocal, +} + +impl Default for LocalVectorTable { + fn default() -> Self { + LocalVectorTable { + lvt_cmci: LvtCmciRegisterLocal::new(RESET_LVT_REG), + lvt_timer: LvtTimerRegisterLocal::new(RESET_LVT_REG), + lvt_thermal: LvtThermalMonitorRegisterLocal::new(RESET_LVT_REG), + lvt_perf_count: LvtPerformanceCounterRegisterLocal::new(RESET_LVT_REG), + lvt_lint0: LvtLint0RegisterLocal::new(RESET_LVT_REG), + lvt_lint1: LvtLint1RegisterLocal::new(RESET_LVT_REG), + lvt_err: LvtErrorRegisterLocal::new(RESET_LVT_REG), + } + } +} diff --git a/src/regs/lvt/cmci.rs b/src/regs/lvt/cmci.rs new file mode 100644 index 000000000..aef7434d7 --- /dev/null +++ b/src/regs/lvt/cmci.rs @@ -0,0 +1,73 @@ +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub LVT_CMCI [ + /// Reserved2 + Reserved2 OFFSET(17) NUMBITS(15) [], + /// Mask + Mask OFFSET(16) NUMBITS(1) [ + /// Not masked. + NotMasked = 0, + /// Masked. + Masked = 1 + ], + Reserved1 OFFSET(13) NUMBITS(3) [], + /// Delivery Status (Read Only): Indicates the interrupt delivery status + DeliveryStatus OFFSET(12) NUMBITS(1) [ + /// 0 (Idle) + /// There is currently no activity for this interrupt source, + /// or the previous interrupt from this source was delivered to the processor core and accepted. + Idle = 0, + /// 1 (Send Pending) + /// Indicates that an interrupt from this source has been delivered to the processor core + /// but has not yet been accepted (see Section 11.5.5, “Local Interrupt Acceptance”). + SendPending = 1 + ], + Reserved0 OFFSET(11) NUMBITS(1) [], + /// Delivery Mode: Specifies the type of interrupt to be sent to the processor. + /// Some delivery modes will only operate as intended when used in conjunction with a specific trigger mode. + DeliveryMode OFFSET(8) NUMBITS(3) [ + /// 000 (Fixed) Delivers the interrupt specified in the vector field. + Fixed = 0b000, + /// 010 (SMI) Delivers an SMI interrupt to the processor core through + /// the processor’s local SMI signal path. + /// When using this delivery mode, the vector field should be set to 00H for future compatibility. + SMI = 0b010, + /// 100 (NMI) Delivers an NMI interrupt to the processor. The vector information is ignored. + NMI = 0b100, + /// 101 (INIT) Delivers an INIT request to the processor core, + /// which causes the processor to perform an INIT. + /// When using this delivery mode, the vector field should be set to 00H for future compatibility. + /// Not supported for the LVT CMCI register, the LVT thermal monitor register, or the LVT performance counter register. + INIT = 0b101, + /// 110 Reserved; not supported for any LVT register. + Reserved = 0b110, + /// 111 (ExtINT) Causes the processor to respond to the interrupt + /// as if the interrupt originated in an externally connected (8259A-compatible) interrupt controller. + /// A special INTA bus cycle corresponding to ExtINT, is routed to the external controller. + /// The external controller is expected to supply the vector information. + /// The APIC architecture supports only one ExtINT source in a system, usually contained in the compatibility bridge. + /// Only one processor in the system should have an LVT entry configured to use the ExtINT delivery mode. + /// Not supported for the LVT CMCI register, the LVT thermal monitor register, or the LVT performance counter register. + ExtINT = 0b111 + ], + /// Vector: Interrupt vector number. + Vector OFFSET(0) NUMBITS(8) [], + ] +} + +/// LVT CMCI Register (FEE0 02F0H) using MMIO. +/// Specifies interrupt delivery when an overflow condition of corrected machine check error count +/// reaching a threshold value occurred in a machine check bank supporting CMCI +/// (see Section 16.5.1, “CMCI Local APIC Interface”). +pub type LvtCmciRegisterMmio = ReadWrite; + +/// A read-write copy of LVT CMCI Register (FEE0 02F0H). +/// +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type LvtCmciRegisterLocal = LocalRegisterCopy; diff --git a/src/regs/lvt/error.rs b/src/regs/lvt/error.rs new file mode 100644 index 000000000..4bb1113e2 --- /dev/null +++ b/src/regs/lvt/error.rs @@ -0,0 +1,44 @@ +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub LVT_ERROR [ + /// Reserved2 + Reserved2 OFFSET(17) NUMBITS(15) [], + /// Mask + Mask OFFSET(16) NUMBITS(1) [ + /// Not masked. + NotMasked = 0, + /// Masked. + Masked = 1 + ], + Reserved1 OFFSET(13) NUMBITS(3) [], + /// Delivery Status (Read Only): Indicates the interrupt delivery status + DeliveryStatus OFFSET(12) NUMBITS(1) [ + /// 0 (Idle) + /// There is currently no activity for this interrupt source, + /// or the previous interrupt from this source was delivered to the processor core and accepted. + Idle = 0, + /// 1 (Send Pending) + /// Indicates that an interrupt from this source has been delivered to the processor core + /// but has not yet been accepted (see Section 11.5.5, “Local Interrupt Acceptance”). + SendPending = 1 + ], + Reserved0 OFFSET(8) NUMBITS(4) [], + /// Vector: Interrupt vector number. + Vector OFFSET(0) NUMBITS(8) [], + ] +} + +/// LVT Error Register (FEE0 0370H) +/// Specifies interrupt delivery when the APIC detects an internal error (see Section 11.5.3, “Error Handling”). +pub type LvtErrorRegisterMmio = ReadWrite; + +/// A read-write copy of LVT Error Register (FEE0 0370H). +/// +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type LvtErrorRegisterLocal = LocalRegisterCopy; diff --git a/src/regs/lvt/lint0.rs b/src/regs/lvt/lint0.rs new file mode 100644 index 000000000..36518f7b7 --- /dev/null +++ b/src/regs/lvt/lint0.rs @@ -0,0 +1,97 @@ +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub LVT_LINT0 [ + /// Reserved1 + Reserved1 OFFSET(17) NUMBITS(15) [], + /// Mask + Mask OFFSET(16) NUMBITS(1) [ + /// Not masked. + NotMasked = 0, + /// Masked. + Masked = 1 + ], + /// Trigger Mode Selects the trigger mode for the local LINT0 and LINT1 pins: + /// (0) edge sensitive and (1) level sensitive. + /// This flag is only used when the delivery mode is Fixed. + /// When the delivery mode is NMI, SMI, or INIT, the trigger mode is always edge sensitive. + /// When the delivery mode is ExtINT, the trigger mode is always level sensitive. The timer and error interrupts are always treated as edge sensitive. + /// If the local APIC is not used in conjunction with an I/O APIC and fixed delivery mode is selected; + /// the Pentium 4, Intel Xeon, and P6 family processors will always use level-sensitive triggering, regardless if edge-sensitive triggering is selected. + /// Software should always set the trigger mode in the LVT LINT1 register to 0 (edge sensitive). Level-sensitive interrupts are not supported for LINT1. + TriggerMode OFFSET(15) NUMBITS(1) [ + /// Edge sensitive. + EdgeSensitive = 0, + /// Level sensitive. + LevelSensitive = 1 + ], + /// Remote IRR Flag (Read Only) + /// For fixed mode, level-triggered interrupts; + /// this flag is set when the local APIC accepts the interrupt for servicing and is reset when an EOI command is received from the processor. + /// The meaning of this flag is undefined for edge-triggered interrupts and other delivery modes. + RemoteIRR OFFSET(14) NUMBITS(1) [], + /// Interrupt Input Pin Polarity Specifies the polarity of the corresponding interrupt pin: + /// (0) active high or (1) active low. + InterruptInputPinPolarity OFFSET(13) NUMBITS(1) [ + /// Active high. + ActiveHigh = 0, + /// Active low. + ActiveLow = 1 + ], + /// Delivery Status (Read Only): Indicates the interrupt delivery status + DeliveryStatus OFFSET(12) NUMBITS(1) [ + /// 0 (Idle) + /// There is currently no activity for this interrupt source, + /// or the previous interrupt from this source was delivered to the processor core and accepted. + Idle = 0, + /// 1 (Send Pending) + /// Indicates that an interrupt from this source has been delivered to the processor core + /// but has not yet been accepted (see Section 11.5.5, “Local Interrupt Acceptance”). + SendPending = 1 + ], + Reserved0 OFFSET(11) NUMBITS(1) [], + /// Delivery Mode: Specifies the type of interrupt to be sent to the processor. + /// Some delivery modes will only operate as intended when used in conjunction with a specific trigger mode. + DeliveryMode OFFSET(8) NUMBITS(3) [ + /// 000 (Fixed) Delivers the interrupt specified in the vector field. + Fixed = 0b000, + /// 010 (SMI) Delivers an SMI interrupt to the processor core through + /// the processor’s local SMI signal path. + /// When using this delivery mode, the vector field should be set to 00H for future compatibility. + SMI = 0b010, + /// 100 (NMI) Delivers an NMI interrupt to the processor. The vector information is ignored. + NMI = 0b100, + /// 101 (INIT) Delivers an INIT request to the processor core, + /// which causes the processor to perform an INIT. + /// When using this delivery mode, the vector field should be set to 00H for future compatibility. + /// Not supported for the LVT CMCI register, the LVT thermal monitor register, or the LVT performance counter register. + INIT = 0b101, + /// 110 Reserved; not supported for any LVT register. + Reserved = 0b110, + /// 111 (ExtINT) Causes the processor to respond to the interrupt + /// as if the interrupt originated in an externally connected (8259A-compatible) interrupt controller. + /// A special INTA bus cycle corresponding to ExtINT, is routed to the external controller. + /// The external controller is expected to supply the vector information. + /// The APIC architecture supports only one ExtINT source in a system, usually contained in the compatibility bridge. + /// Only one processor in the system should have an LVT entry configured to use the ExtINT delivery mode. + /// Not supported for the LVT CMCI register, the LVT thermal monitor register, or the LVT performance counter register. + ExtINT = 0b111 + ], + /// Vector: Interrupt vector number. + Vector OFFSET(0) NUMBITS(8) [], + ] +} + +/// LVT LINT0 Register (FEE0 0350H) +/// Specifies interrupt delivery when an interrupt is signaled at the LINT0 pin. +pub type LvtLint0RegisterMmio = ReadWrite; + +/// A read-write copy of LVT LINT0 Register (FEE0 0350H). +/// +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type LvtLint0RegisterLocal = LocalRegisterCopy; diff --git a/src/regs/lvt/lint1.rs b/src/regs/lvt/lint1.rs new file mode 100644 index 000000000..a8222fe0c --- /dev/null +++ b/src/regs/lvt/lint1.rs @@ -0,0 +1,97 @@ +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub LVT_LINT1 [ + /// Reserved1 + Reserved1 OFFSET(17) NUMBITS(15) [], + /// Mask + Mask OFFSET(16) NUMBITS(1) [ + /// Not masked. + NotMasked = 0, + /// Masked. + Masked = 1 + ], + /// Trigger Mode Selects the trigger mode for the local LINT0 and LINT1 pins: + /// (0) edge sensitive and (1) level sensitive. + /// This flag is only used when the delivery mode is Fixed. + /// When the delivery mode is NMI, SMI, or INIT, the trigger mode is always edge sensitive. + /// When the delivery mode is ExtINT, the trigger mode is always level sensitive. The timer and error interrupts are always treated as edge sensitive. + /// If the local APIC is not used in conjunction with an I/O APIC and fixed delivery mode is selected; + /// the Pentium 4, Intel Xeon, and P6 family processors will always use level-sensitive triggering, regardless if edge-sensitive triggering is selected. + /// Software should always set the trigger mode in the LVT LINT1 register to 0 (edge sensitive). Level-sensitive interrupts are not supported for LINT1. + TriggerMode OFFSET(15) NUMBITS(1) [ + /// Edge sensitive. + EdgeSensitive = 0, + /// Level sensitive. + LevelSensitive = 1 + ], + /// Remote IRR Flag (Read Only) + /// For fixed mode, level-triggered interrupts; + /// this flag is set when the local APIC accepts the interrupt for servicing and is reset when an EOI command is received from the processor. + /// The meaning of this flag is undefined for edge-triggered interrupts and other delivery modes. + RemoteIRR OFFSET(14) NUMBITS(1) [], + /// Interrupt Input Pin Polarity Specifies the polarity of the corresponding interrupt pin: + /// (0) active high or (1) active low. + InterruptInputPinPolarity OFFSET(13) NUMBITS(1) [ + /// Active high. + ActiveHigh = 0, + /// Active low. + ActiveLow = 1 + ], + /// Delivery Status (Read Only): Indicates the interrupt delivery status + DeliveryStatus OFFSET(12) NUMBITS(1) [ + /// 0 (Idle) + /// There is currently no activity for this interrupt source, + /// or the previous interrupt from this source was delivered to the processor core and accepted. + Idle = 0, + /// 1 (Send Pending) + /// Indicates that an interrupt from this source has been delivered to the processor core + /// but has not yet been accepted (see Section 11.5.5, “Local Interrupt Acceptance”). + SendPending = 1 + ], + Reserved0 OFFSET(11) NUMBITS(1) [], + /// Delivery Mode: Specifies the type of interrupt to be sent to the processor. + /// Some delivery modes will only operate as intended when used in conjunction with a specific trigger mode. + DeliveryMode OFFSET(8) NUMBITS(3) [ + /// 000 (Fixed) Delivers the interrupt specified in the vector field. + Fixed = 0b000, + /// 010 (SMI) Delivers an SMI interrupt to the processor core through + /// the processor’s local SMI signal path. + /// When using this delivery mode, the vector field should be set to 00H for future compatibility. + SMI = 0b010, + /// 100 (NMI) Delivers an NMI interrupt to the processor. The vector information is ignored. + NMI = 0b100, + /// 101 (INIT) Delivers an INIT request to the processor core, + /// which causes the processor to perform an INIT. + /// When using this delivery mode, the vector field should be set to 00H for future compatibility. + /// Not supported for the LVT CMCI register, the LVT thermal monitor register, or the LVT performance counter register. + INIT = 0b101, + /// 110 Reserved; not supported for any LVT register. + Reserved = 0b110, + /// 111 (ExtINT) Causes the processor to respond to the interrupt + /// as if the interrupt originated in an externally connected (8259A-compatible) interrupt controller. + /// A special INTA bus cycle corresponding to ExtINT, is routed to the external controller. + /// The external controller is expected to supply the vector information. + /// The APIC architecture supports only one ExtINT source in a system, usually contained in the compatibility bridge. + /// Only one processor in the system should have an LVT entry configured to use the ExtINT delivery mode. + /// Not supported for the LVT CMCI register, the LVT thermal monitor register, or the LVT performance counter register. + ExtINT = 0b111 + ], + /// Vector: Interrupt vector number. + Vector OFFSET(0) NUMBITS(8) [], + ] +} + +/// LVT LINT1 Register (FEE0 0360H) +/// Specifies interrupt delivery when an interrupt is signaled at the LINT1 pin. +pub type LvtLint1RegisterMmio = ReadWrite; + +/// A read-write copy of LVT LINT1 Register (FEE0 0360H). +/// +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type LvtLint1RegisterLocal = LocalRegisterCopy; diff --git a/src/regs/lvt/mod.rs b/src/regs/lvt/mod.rs new file mode 100644 index 000000000..1f7c88915 --- /dev/null +++ b/src/regs/lvt/mod.rs @@ -0,0 +1,15 @@ +mod cmci; +mod error; +mod lint0; +mod lint1; +mod perfmon; +mod thermal; +mod timer; + +pub use cmci::*; +pub use error::*; +pub use lint0::*; +pub use lint1::*; +pub use perfmon::*; +pub use thermal::*; +pub use timer::*; diff --git a/src/regs/lvt/perfmon.rs b/src/regs/lvt/perfmon.rs new file mode 100644 index 000000000..54f6c3d4b --- /dev/null +++ b/src/regs/lvt/perfmon.rs @@ -0,0 +1,74 @@ +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub LVT_PERFORMANCE_COUNTER [ + /// Reserved2 + Reserved2 OFFSET(17) NUMBITS(15) [], + /// Mask + Mask OFFSET(16) NUMBITS(1) [ + /// Not masked. + NotMasked = 0, + /// Masked. + Masked = 1 + ], + Reserved1 OFFSET(13) NUMBITS(3) [], + /// Delivery Status (Read Only): Indicates the interrupt delivery status + DeliveryStatus OFFSET(12) NUMBITS(1) [ + /// 0 (Idle) + /// There is currently no activity for this interrupt source, + /// or the previous interrupt from this source was delivered to the processor core and accepted. + Idle = 0, + /// 1 (Send Pending) + /// Indicates that an interrupt from this source has been delivered to the processor core + /// but has not yet been accepted (see Section 11.5.5, “Local Interrupt Acceptance”). + SendPending = 1 + ], + Reserved0 OFFSET(11) NUMBITS(1) [], + /// Delivery Mode: Specifies the type of interrupt to be sent to the processor. + /// Some delivery modes will only operate as intended when used in conjunction with a specific trigger mode. + DeliveryMode OFFSET(8) NUMBITS(3) [ + /// 000 (Fixed) Delivers the interrupt specified in the vector field. + Fixed = 0b000, + /// 010 (SMI) Delivers an SMI interrupt to the processor core through + /// the processor’s local SMI signal path. + /// When using this delivery mode, the vector field should be set to 00H for future compatibility. + SMI = 0b010, + /// 100 (NMI) Delivers an NMI interrupt to the processor. The vector information is ignored. + NMI = 0b100, + /// 101 (INIT) Delivers an INIT request to the processor core, + /// which causes the processor to perform an INIT. + /// When using this delivery mode, the vector field should be set to 00H for future compatibility. + /// Not supported for the LVT CMCI register, the LVT thermal monitor register, or the LVT performance counter register. + INIT = 0b101, + /// 110 Reserved; not supported for any LVT register. + Reserved = 0b110, + /// 111 (ExtINT) Causes the processor to respond to the interrupt + /// as if the interrupt originated in an externally connected (8259A-compatible) interrupt controller. + /// A special INTA bus cycle corresponding to ExtINT, is routed to the external controller. + /// The external controller is expected to supply the vector information. + /// The APIC architecture supports only one ExtINT source in a system, usually contained in the compatibility bridge. + /// Only one processor in the system should have an LVT entry configured to use the ExtINT delivery mode. + /// Not supported for the LVT CMCI register, the LVT thermal monitor register, or the LVT performance counter register. + ExtINT = 0b111 + ], + /// Vector: Interrupt vector number. + Vector OFFSET(0) NUMBITS(8) [], + ] +} + +/// LVT Performance Counter Register (FEE0 0340H) +/// Specifies interrupt delivery when a performance counter generates an interrupt on overflow (see Section 20.6.3.5.8, “Generating an Interrupt on Overflow”) +/// or when Intel PT signals a ToPA PMI (see Section 33.2.7.2). +/// This LVT entry is implementation specific, not architectural. If implemented, it is not guaranteed to be at base address FEE0 0340H. +pub type LvtPerformanceCounterRegisterMmio = ReadWrite; + +/// A read-write copy of LVT Performance Counter Register (FEE0 0340H). +/// +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type LvtPerformanceCounterRegisterLocal = + LocalRegisterCopy; diff --git a/src/regs/lvt/thermal.rs b/src/regs/lvt/thermal.rs new file mode 100644 index 000000000..d11b13290 --- /dev/null +++ b/src/regs/lvt/thermal.rs @@ -0,0 +1,73 @@ +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub LVT_THERMAL_MONITOR [ + /// Reserved2 + Reserved2 OFFSET(17) NUMBITS(15) [], + /// Mask + Mask OFFSET(16) NUMBITS(1) [ + /// Not masked. + NotMasked = 0, + /// Masked. + Masked = 1 + ], + Reserved1 OFFSET(13) NUMBITS(3) [], + /// Delivery Status (Read Only): Indicates the interrupt delivery status + DeliveryStatus OFFSET(12) NUMBITS(1) [ + /// 0 (Idle) + /// There is currently no activity for this interrupt source, + /// or the previous interrupt from this source was delivered to the processor core and accepted. + Idle = 0, + /// 1 (Send Pending) + /// Indicates that an interrupt from this source has been delivered to the processor core + /// but has not yet been accepted (see Section 11.5.5, “Local Interrupt Acceptance”). + SendPending = 1 + ], + Reserved0 OFFSET(11) NUMBITS(1) [], + /// Delivery Mode: Specifies the type of interrupt to be sent to the processor. + /// Some delivery modes will only operate as intended when used in conjunction with a specific trigger mode. + DeliveryMode OFFSET(8) NUMBITS(3) [ + /// 000 (Fixed) Delivers the interrupt specified in the vector field. + Fixed = 0b000, + /// 010 (SMI) Delivers an SMI interrupt to the processor core through + /// the processor’s local SMI signal path. + /// When using this delivery mode, the vector field should be set to 00H for future compatibility. + SMI = 0b010, + /// 100 (NMI) Delivers an NMI interrupt to the processor. The vector information is ignored. + NMI = 0b100, + /// 101 (INIT) Delivers an INIT request to the processor core, + /// which causes the processor to perform an INIT. + /// When using this delivery mode, the vector field should be set to 00H for future compatibility. + /// Not supported for the LVT CMCI register, the LVT thermal monitor register, or the LVT performance counter register. + INIT = 0b101, + /// 110 Reserved; not supported for any LVT register. + Reserved = 0b110, + /// 111 (ExtINT) Causes the processor to respond to the interrupt + /// as if the interrupt originated in an externally connected (8259A-compatible) interrupt controller. + /// A special INTA bus cycle corresponding to ExtINT, is routed to the external controller. + /// The external controller is expected to supply the vector information. + /// The APIC architecture supports only one ExtINT source in a system, usually contained in the compatibility bridge. + /// Only one processor in the system should have an LVT entry configured to use the ExtINT delivery mode. + /// Not supported for the LVT CMCI register, the LVT thermal monitor register, or the LVT performance counter register. + ExtINT = 0b111 + ], + /// Vector: Interrupt vector number. + Vector OFFSET(0) NUMBITS(8) [], + ] +} + +/// LVT Thermal Monitor Register (FEE0 0330H) +/// Specifies interrupt delivery when the thermal sensor generates an interrupt (see Section 15.8.2, “Thermal Monitor”). +/// This LVT entry is implementation specific, not architectural. +/// If implemented, it will always be at base address FEE0 0330H. +pub type LvtThermalMonitorRegisterMmio = ReadWrite; + +/// A read-write copy of LVT Thermal Monitor Register (FEE0 0330H). +/// +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type LvtThermalMonitorRegisterLocal = LocalRegisterCopy; diff --git a/src/regs/lvt/timer.rs b/src/regs/lvt/timer.rs new file mode 100644 index 000000000..aebcdaa3c --- /dev/null +++ b/src/regs/lvt/timer.rs @@ -0,0 +1,59 @@ +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub LVT_TIMER [ + /// Reserved2 + Reserved2 OFFSET(19) NUMBITS(13) [], + /// Timer Mode + TimerMode OFFSET(17) NUMBITS(2) [ + /// (00b) one-shot mode using a count-down value + OneShot = 0b00, + /// (01b) periodic mode reloading a count-down value + Periodic = 0b01, + /// (10b) TSC-Deadline mode using absolute target value in IA32_TSC_DEADLINE MSR (see Section 11.5.4.1) + TSCDeadline = 0b10, + /// (11b) is reserved + Reserved = 0b11 + ], + /// Mask: Interrupt mask: + /// (0) enables reception of the interrupt and (1) inhibits reception of the interrupt. + /// When the local APIC handles a performance-monitoring counters interrupt, + /// it automatically sets the mask flag in the LVT performance counter register. + /// This flag is set to 1 on reset. + /// It can be cleared only by software. + Mask OFFSET(16) NUMBITS(1) [ + /// Not masked, enables reception of the interrupt. + NotMasked = 0, + /// Masked, inhibits reception of the interrupt. + Masked = 1 + ], + Reserved1 OFFSET(13) NUMBITS(3) [], + /// Delivery Status (Read Only): Indicates the interrupt delivery status + DeliveryStatus OFFSET(12) NUMBITS(1) [ + /// 0 (Idle) + /// There is currently no activity for this interrupt source, + /// or the previous interrupt from this source was delivered to the processor core and accepted. + Idle = 0, + /// 1 (Send Pending) + /// Indicates that an interrupt from this source has been delivered to the processor core + /// but has not yet been accepted (see Section 11.5.5, “Local Interrupt Acceptance”). + SendPending = 1 + ], + Reserved0 OFFSET(8) NUMBITS(4) [], + /// Vector: Interrupt vector number. + Vector OFFSET(0) NUMBITS(8) [], + ] +} + +/// LVT Timer Register (FEE0 0320H) +pub type LvtTimerRegisterMmio = ReadWrite; + +/// A read-write copy of LVT Timer Register (FEE0 0320H). +/// +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type LvtTimerRegisterLocal = LocalRegisterCopy; diff --git a/src/regs/mod.rs b/src/regs/mod.rs new file mode 100644 index 000000000..80037b4f3 --- /dev/null +++ b/src/regs/mod.rs @@ -0,0 +1,104 @@ +pub mod lvt; +mod svr; +pub use svr::*; + +use tock_registers::register_structs; +use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly}; + +use lvt::{ + LvtCmciRegisterMmio, LvtErrorRegisterMmio, LvtLint0RegisterMmio, LvtLint1RegisterMmio, + LvtPerformanceCounterRegisterMmio, LvtThermalMonitorRegisterMmio, LvtTimerRegisterMmio, +}; + +register_structs! { + #[allow(non_snake_case)] + pub LocalAPICRegs { + (0x00 => _reserved0), + /// Local APIC ID register (VID): the 32-bit field located at offset 000H on the virtual-APIC page. + (0x20 => pub ID: ReadWrite), + (0x24 => _reserved1), + /// Local APIC Version register (VVER): the 32-bit field located at offset 030H on the virtual-APIC page. + (0x30 => pub VERSION: ReadOnly), + (0x34 => _reserved2), + /// Virtual task-priority register (VTPR): the 32-bit field located at offset 080H on the virtual-APIC page. + (0x80 => pub TPR: ReadWrite), + (0x84 => _reserved3), + /// Virtual APIC-priority register (VAPR): the 32-bit field located at offset 090H on the virtual-APIC page. + (0x90 => pub APR: ReadOnly), + (0x94 => _reserved4), + /// Virtual processor-priority register (VPPR): the 32-bit field located at offset 0A0H on the virtual-APIC page. + (0xA0 => pub PPR: ReadOnly), + (0xA4 => _reserved5), + /// Virtual end-of-interrupt register (VEOI): the 32-bit field located at offset 0B0H on the virtual-APIC page. + (0xB0 => pub EOI: WriteOnly), + (0xB4 => _reserved6), + /// Virtual Remote Read Register (RRD): the 32-bit field located at offset 0C0H on the virtual-APIC page. + (0xC0 => pub RRD: ReadOnly), + (0xC4 => _reserved7), + /// Virtual Logical Destination Register (LDR): the 32-bit field located at offset 0D0H on the virtual-APIC page. + (0xD0 => pub LDR: ReadWrite), + (0xD4 => _reserved8), + /// Virtual Destination Format Register (DFR): the 32-bit field located at offset 0E0H on the virtual-APIC page. + (0xE0 => pub DFR: ReadWrite), + (0xE4 => _reserved9), + /// Virtual Spurious Interrupt Vector Register (SVR): the 32-bit field located at offset 0F0H on the virtual-APIC page. + (0xF0 => pub SVR: SpuriousInterruptVectorRegisterMmio), + (0xF4 => _reserved10), + /// Virtual interrupt-service register (VISR): + /// the 256-bit value comprising eight non-contiguous 32-bit fields at offsets + /// 100H, 110H, 120H, 130H, 140H, 150H, 160H, and 170H on the virtual-APIC page. + (0x100 => pub ISR: [ReadOnly; 8]), + /// Virtual trigger-mode register (VTMR): + /// the 256-bit value comprising eight non-contiguous 32-bit fields at offsets + /// 180H, 190H, 1A0H, 1B0H, 1C0H, 1D0H, 1E0H, and 1F0H on the virtual-APIC page. + (0x180 => pub TMR: [ReadOnly; 8]), + /// Virtual interrupt-request register (VIRR): + /// the 256-bit value comprising eight non-contiguous 32-bit fields at offsets + /// 200H, 210H, 220H, 230H, 240H, 250H, 260H, and 270H on the virtual-APIC page. + /// Bit x of the VIRR is at bit position (x & 1FH) at offset (200H | ((x & E0H) » 1)). + /// The processor uses only the low 4 bytes of each of the 16-Byte fields at offsets 200H, 210H, 220H, 230H, 240H, 250H, 260H, and 270H. + (0x200 => pub IRR: [ReadOnly; 8]), + /// Virtual error-status register (VESR): the 32-bit field located at offset 280H on the virtual-APIC page. + (0x280 => pub ESR: ReadWrite), + (0x284 => _reserved11), + /// Virtual LVT Corrected Machine Check Interrupt (CMCI) Register + (0x2F0 => pub LVT_CMCI: LvtCmciRegisterMmio), + (0x2F4 => _reserved12), + /// Virtual Interrupt Command Register (ICR): the 64-bit field located at offset 300H on the virtual-APIC page. + (0x300 => pub ICR_LO: ReadWrite), + (0x304 => _reserved13), + (0x310 => pub ICR_HI: ReadWrite), + (0x314 => _reserved14), + /// Virtual LVT Timer Register: the 32-bit field located at offset 320H on the virtual-APIC page. + (0x320 => pub LVT_TIMER: LvtTimerRegisterMmio), + (0x324 => _reserved15), + /// Virtual LVT Thermal Sensor register: the 32-bit field located at offset 330H on the virtual-APIC page. + (0x330 => pub LVT_THERMAL: LvtThermalMonitorRegisterMmio), + (0x334 => _reserved16), + /// Virtual LVT Performance Monitoring Counters register: the 32-bit field located at offset 340H on the virtual-APIC page. + (0x340 => pub LVT_PMI: LvtPerformanceCounterRegisterMmio), + (0x344 => _reserved17), + /// Virtual LVT LINT0 register: the 32-bit field located at offset 350H on the virtual-APIC page. + (0x350 => pub LVT_LINT0: LvtLint0RegisterMmio), + (0x354 => _reserved18), + /// Virtual LVT LINT1 register: the 32-bit field located at offset 360H on the virtual-APIC page. + (0x360 => pub LVT_LINT1: LvtLint1RegisterMmio), + (0x364 => _reserved19), + /// Virtual LVT Error register: the 32-bit field located at offset 370H on the virtual-APIC page. + (0x370 => pub LVT_ERROR: LvtErrorRegisterMmio), + (0x374 => _reserved20), + /// Virtual Initial Count Register (for Timer): the 32-bit field located at offset 380H on the virtual-APIC page. + (0x380 => pub ICR_TIMER: ReadWrite), + (0x384 => _reserved21), + /// Virtual Current Count Register (for Timer): the 32-bit field located at offset 390H on the virtual-APIC page. + (0x390 => pub CCR_TIMER: ReadOnly), + (0x394 => _reserved22), + /// Virtual Divide Configuration Register (for Timer): the 32-bit field located at offset 3E0H on the virtual-APIC page. + (0x3E0 => pub DCR_TIMER: ReadWrite), + (0x3E4 => _reserved23), + /// Virtual SELF IPI Register: the 32-bit field located at offset 3F0H on the virtual-APIC page. + (0x3F0 => pub SELF_IPI: WriteOnly), + (0x3F4 => _reserved24), + (0x1000 => @END), + } +} diff --git a/src/regs/svr.rs b/src/regs/svr.rs new file mode 100644 index 000000000..aad86dee3 --- /dev/null +++ b/src/regs/svr.rs @@ -0,0 +1,70 @@ +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub SPURIOUS_INTERRUPT_VECTOR [ + /// Reserved2 + Reserved1 OFFSET(13) NUMBITS(19) [], + /// Suppress EOI Broadcasts + /// Determines whether an EOI for a level-triggered interrupt causes EOI messages to be broadcast to the I/O APICs (0) or not (1). + /// See Section 11.8.5. + /// The default value for this bit is 0, indicating that EOI broadcasts are performed. + /// This bit is reserved to 0 if the processor does not support EOI-broadcast suppression. + /// - 0: Disabled + /// - 1: Enabled + EOIBroadcastSuppression OFFSET(12) NUMBITS(1) [ + /// Disabled + Disabled = 0, + /// Enabled + Enabled = 1 + ], + Reserved0 OFFSET(10) NUMBITS(2) [], + /// Focus Processor Checking + /// Determines if focus processor checking is enabled (0) or disabled (1) when using the lowestpriority delivery mode. + /// In Pentium 4 and Intel Xeon processors, this bit is reserved and should be cleared to 0. + /// - 0: Enabled + /// - 1: Disabled + FocusProcessorChecking OFFSET(9) NUMBITS(1) [ + /// Enabled + Enabled = 0, + /// Disabled + Disabled = 1 + ], + /// APIC Software Enable/Disable + /// Allows software to temporarily enable (1) or disable (0) the local APIC + /// (see Section 11.4.3, “Enabling or Disabling the Local APIC”). + /// - 0: APIC Disabled + /// - 1: APIC Enabled + APICSoftwareEnableDisable OFFSET(8) NUMBITS(1) [ + /// APIC Disabled + Disabled = 0, + /// APIC Enabled + Enabled = 1 + ], + /// Spurious Vector. + /// Determines the vector number to be delivered to the processor when the local APIC generates a spurious vector. + /// - (Pentium 4 and Intel Xeon processors.) Bits 0 through 7 of the this field are programmable by software. + /// - (P6 family and Pentium processors). Bits 4 through 7 of the this field are programmable by software, and bits 0 through 3 are hardwired to logical ones. Software writes to bits 0 through 3 have no effect. + /// For the P6 family and Pentium processors, bits 0 through 3 are always 1. + SPURIOUS_VECTOR OFFSET(0) NUMBITS(8) [], + ] +} + +/// Spurious-Interrupt Vector Register using MMIO. +/// - Address: FEE0 00F0H +/// - Value after reset: 0000 00FFH +/// +/// A special situation may occur when a processor raises its task priority to be greater than or equal to the level of the interrupt for which the processor INTR signal is currently being asserted. +/// If at the time the INTA cycle is issued, the interrupt that was to be dispensed has become masked (programmed by software), the local APIC will deliver a spurious-interrupt vector. +/// Dispensing the spurious-interrupt vector does not affect the ISR, so the handler for this vector should return without an EOI. +pub type SpuriousInterruptVectorRegisterMmio = ReadWrite; + +/// A read-write copy of Spurious-Interrupt Vector Register (FEE0 00F0H). +/// +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type SpuriousInterruptVectorRegisterLocal = + LocalRegisterCopy; diff --git a/src/vlapic.rs b/src/vlapic.rs index bc6d96c48..af38388c5 100644 --- a/src/vlapic.rs +++ b/src/vlapic.rs @@ -1,41 +1,46 @@ -use paste::paste; +use core::ptr::NonNull; -use memory_addr::PAGE_SIZE_4K; +use axerrno::AxResult; +use tock_registers::interfaces::Readable; -use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr, PhysFrame}; +use axaddrspace::device::AccessWidth; +use axaddrspace::{AxMmHal, HostPhysAddr, PhysFrame}; +use axdevice_base::DeviceRWContext; -use crate::consts::ApicRegOffset; +use crate::consts::{ApicRegOffset, RESET_SPURIOUS_INTERRUPT_VECTOR}; +use crate::lvt::LocalVectorTable; +use crate::regs::{LocalAPICRegs, SpuriousInterruptVectorRegisterLocal}; -#[repr(align(4096))] -struct APICAccessPage([u8; PAGE_SIZE_4K]); - -static VIRTUAL_APIC_ACCESS_PAGE: APICAccessPage = APICAccessPage([0; PAGE_SIZE_4K]); - -// Virtual-APIC Registers. +/// Virtual-APIC Registers. pub struct VirtualApicRegs { /// The virtual-APIC page is a 4-KByte region of memory /// that the processor uses to virtualize certain accesses to APIC registers and to manage virtual interrupts. /// The physical address of the virtual-APIC page is the virtual-APIC address, /// a 64-bit VM-execution control field in the VMCS (see Section 25.6.8). - virtual_apic_page: PhysFrame, + virtual_lapic: NonNull, + /// Copies of some registers in the virtual APIC page, + /// to be able to detect what changed (e.g. svr_last) + svr_last: SpuriousInterruptVectorRegisterLocal, + /// Copies of some registers in the virtual APIC page, + /// to maintain a coherent snapshot of the register (e.g. lvt_last) + lvt_last: LocalVectorTable, + apic_page: PhysFrame, } impl VirtualApicRegs { + /// Create new virtual-APIC registers by allocating a 4-KByte page for the virtual-APIC page. pub fn new() -> Self { + let apic_frame = PhysFrame::alloc_zero().expect("allocate virtual-APIC page failed"); Self { - virtual_apic_page: PhysFrame::alloc_zero().expect("allocate virtual-APIC page failed"), + virtual_lapic: NonNull::new(apic_frame.as_mut_ptr().cast()).unwrap(), + apic_page: apic_frame, + svr_last: SpuriousInterruptVectorRegisterLocal::new(RESET_SPURIOUS_INTERRUPT_VECTOR), + lvt_last: LocalVectorTable::default(), } } - /// APIC-access address (64 bits). - /// This field contains the physical address of the 4-KByte APIC-access page. - /// If the “virtualize APIC accesses” VM-execution control is 1, - /// access to this page may cause VM exits or be virtualized by the processor. - /// See Section 30.4. - pub fn virtual_apic_access_addr() -> HostPhysAddr { - H::virt_to_phys(HostVirtAddr::from_usize( - VIRTUAL_APIC_ACCESS_PAGE.0.as_ptr() as usize, - )) + const fn regs(&self) -> &LocalAPICRegs { + unsafe { self.virtual_lapic.as_ref() } } /// Virtual-APIC address (64 bits). @@ -43,213 +48,139 @@ impl VirtualApicRegs { /// The processor uses the virtual-APIC page to virtualize certain accesses to APIC registers and to manage virtual interrupts; /// see Chapter 30. pub fn virtual_apic_page_addr(&self) -> HostPhysAddr { - self.virtual_apic_page.start_paddr() + self.apic_page.start_paddr() } } impl Drop for VirtualApicRegs { fn drop(&mut self) { - H::dealloc_frame(self.virtual_apic_page.start_paddr()); + H::dealloc_frame(self.apic_page.start_paddr()); } } -macro_rules! impl_virtual_apic_set_reg { - ($name:ident, $offset:expr) => { - paste! { - #[doc = concat!("Sets the ", stringify!($name), " register.")] - fn [](&self, value: u32) { - let ptr = self.virtual_apic_page.as_mut_ptr().wrapping_add($offset) as *mut u32; - unsafe { ptr.write_volatile(value) } - } - } - }; - ($name:ident, $offset:expr, $length:expr) => { - paste! { - #[doc = concat!("Sets the ", stringify!($name), " register at the given index.")] - fn [](&self, index: usize, value: u32) { - assert!(index < $length); - let ptr = self - .virtual_apic_page - .as_mut_ptr() - .wrapping_add($offset + index * 0x10) as *mut u32; - unsafe { ptr.write_volatile(value) } - } - } - }; - () => {}; -} - -macro_rules! impl_virtual_apic_get_reg { - ($name:ident, $offset:expr) => { - paste! { - #[doc = concat!("Gets the ", stringify!($name), " register.")] - fn [](&self) -> u32 { - let ptr = self.virtual_apic_page.as_mut_ptr().wrapping_add($offset) as *mut u32; - unsafe { ptr.read_volatile() } - } - } - }; - ($name:ident, $offset:expr, $length:expr) => { - paste! { - #[doc = concat!("Gets the ", stringify!($name), " register at the given index.")] - fn [](&self, index: usize) -> u32 { - assert!(index < $length); - let ptr = self - .virtual_apic_page - .as_mut_ptr() - .wrapping_add($offset + index * 0x10) as *mut u32; - unsafe { ptr.read_volatile() } - } - } - }; - () => {}; -} - -macro_rules! impl_virtual_apic_regs_get_set { - ($(($name:ident, $offset:expr)),* $(,)?) => { - $( - impl_virtual_apic_get_reg!($name, $offset); - impl_virtual_apic_set_reg!($name, $offset); - )* - }; - ($(($name:ident, $offset:expr, $length:expr)),* $(,)?) => { - $( - impl_virtual_apic_get_reg!($name, $offset, $length); - impl_virtual_apic_set_reg!($name, $offset, $length); - )* - }; - () => {}; -} - -/// 30.1 VIRTUAL APIC STATE -/// 30.1.1 Virtualized APIC Registers -#[allow(unused)] impl VirtualApicRegs { - impl_virtual_apic_regs_get_set!( - // Local APIC ID register (VID): the 32-bit field located at offset 000H on the virtual-APIC page. - (id, 0x20), - // Local APIC Version register (VVER): the 32-bit field located at offset 030H on the virtual-APIC page. - (version, 0x30), - // Virtual task-priority register (VTPR): the 32-bit field located at offset 080H on the virtual-APIC page. - (tpr, 0x80), - // Virtual APIC-priority register (VAPR): the 32-bit field located at offset 090H on the virtual-APIC page. - (apr, 0x90), - // Virtual processor-priority register (VPPR): the 32-bit field located at offset 0A0H on the virtual-APIC page. - (ppr, 0xA0), - // Virtual end-of-interrupt register (VEOI): the 32-bit field located at offset 0B0H on the virtual-APIC page. - (eoi, 0xB0), - // Virtual Remote Read Register1 (RRD): the 32-bit field located at offset 0C0H on the virtual-APIC page. - (rrd, 0xC0), - // Virtual Remote Read Register1 (RRD): the 32-bit field located at offset 0D0H on the virtual-APIC page. - (ldr, 0xD0), - // Virtual Destination Format Register (DFR): the 32-bit field located at offset 0E0H on the virtual-APIC page. - (dfr, 0xE0), - // Virtual Spurious Interrupt Vector Register (SVR): the 32-bit field located at offset 0F0H on the virtual-APIC page. - (svr, 0xF0), - // Virtual Error Status Register (VESR): the 32-bit field located at offset 280H on the virtual-APIC page. - (esr, 0x280), - // Virtual LVT Corrected Machine Check Interrupt (CMCI) Register - (lvt_cmci, 0x2F0), - // Virtual Interrupt Command Register (ICR): the 64-bit field located at offset 300H on the virtual-APIC page. - (icr_lo, 0x300), - (icr_hi, 0x310), - // Virtual LVT Timer Register: the 32-bit field located at offset 320H on the virtual-APIC page. - (lvt_timer, 0x320), - // Virtual LVT Thermal Sensor register: the 32-bit field located at offset 330H on the virtual-APIC page. - (lvt_thermal, 0x330), - // Virtual LVT Performance Monitoring Counters register: the 32-bit field located at offset 340H on the virtual-APIC page. - (lvt_pmi, 0x340), - // Virtual LVT LINT0 register: the 32-bit field located at offset 350H on the virtual-APIC page. - (lvt_lint0, 0x350), - // Virtual LVT LINT1 register: the 32-bit field located at offset 360H on the virtual-APIC page. - (lvt_lint1, 0x360), - // Virtual LVT Error register: the 32-bit field located at offset 370H on the virtual-APIC page. - (lvt_error, 0x370), - // Virtual Initial Count Register (for Timer): the 32-bit field located at offset 380H on the virtual-APIC page. - (icr_timer, 0x380), - // Virtual Current Count Register (for Timer): the 32-bit field located at offset 390H on the virtual-APIC page. - (ccr_timer, 0x390), - // Virtual Divide Configuration Register (for Timer): the 32-bit field located at offset 3E0H on the virtual-APIC page. - (dcr_timer, 0x3E0), - // Virtual SELF IPI Register: the 32-bit field located at offset 3F0H on the virtual-APIC page. - (self_ipi, 0x3F0) - ); - - impl_virtual_apic_regs_get_set!( - // Virtual interrupt-service register (VISR): - // the 256-bit value comprising eight non-contiguous 32-bit fields at offsets - // 100H, 110H, 120H, 130H, 140H, 150H, 160H, and 170H on the virtual-APIC page. - (isr, 0x100, 8), - // Virtual trigger-mode register (VTMR): - // the 256-bit value comprising eight non-contiguous 32-bit fields at offsets - // 180H, 190H, 1A0H, 1B0H, 1C0H, 1D0H, 1E0H, and 1F0H on the virtual-APIC page. - (tmr, 0x180, 8), - // Virtual interrupt-request register (VIRR): - // the 256-bit value comprising eight non-contiguous 32-bit fields at offsets - // 200H, 210H, 220H, 230H, 240H, 250H, 260H, and 270H on the virtual-APIC page. - // Bit x of the VIRR is at bit position (x & 1FH) at offset (200H | ((x & E0H) » 1)). - // The processor uses only the low 4 bytes of each of the 16-Byte fields at offsets 200H, 210H, 220H, 230H, 240H, 250H, 260H, and 270H. - (irr, 0x200, 8), - ); -} - -impl VirtualApicRegs { - /// Returns the host physical address of the virtual-APIC page. - pub fn virtual_apic_page_host_paddr(&self) -> HostPhysAddr { - self.virtual_apic_page.start_paddr() - } - - pub fn handle_read(&self, offset: ApicRegOffset) -> u32 { - let mut value = 0; + pub fn handle_read( + &self, + offset: ApicRegOffset, + width: AccessWidth, + context: DeviceRWContext, + ) -> AxResult { + let mut value: usize = 0; match offset { ApicRegOffset::ID => { - value = self.get_id(); + value = self.regs().ID.get() as _; debug!("[VLAPIC] read APIC ID register: {:#010X}", value); } ApicRegOffset::Version => { - value = self.get_version(); + value = self.regs().VERSION.get() as _; debug!("[VLAPIC] read APIC Version register: {:#010X}", value); } + ApicRegOffset::TPR => { + value = self.regs().TPR.get() as _; + debug!("[VLAPIC] read TPR register: {:#010X}", value); + } ApicRegOffset::PPR => { - value = self.get_ppr(); + value = self.regs().PPR.get() as _; debug!("[VLAPIC] read PPR register: {:#010X}", value); } ApicRegOffset::EOI => { - value = self.get_eoi(); - debug!("[VLAPIC] read EOI register: {:#010X}", value); + // value = self.regs().EOI.get() as _; + warn!("[VLAPIC] read EOI register: {:#010X}", value); } ApicRegOffset::LDR => { - value = self.get_ldr(); + value = self.regs().LDR.get() as _; debug!("[VLAPIC] read LDR register: {:#010X}", value); } ApicRegOffset::DFR => { - value = self.get_dfr(); + value = self.regs().DFR.get() as _; debug!("[VLAPIC] read DFR register: {:#010X}", value); } ApicRegOffset::SIVR => { - value = self.get_svr(); - debug!("[VLAPIC] read SIVR register: {:#010X}", value); + value = self.regs().SVR.get() as _; + debug!("[VLAPIC] read SVR register: {:#010X}", value); } ApicRegOffset::ISR(index) => { - value = self.get_isr(index.as_usize()); + value = self.regs().ISR[index.as_usize()].get() as _; debug!("[VLAPIC] read ISR[{}] register: {:#010X}", index, value); } ApicRegOffset::TMR(index) => { - value = self.get_tmr(index.as_usize()); + value = self.regs().TMR[index.as_usize()].get() as _; debug!("[VLAPIC] read TMR[{}] register: {:#010X}", index, value); } ApicRegOffset::IRR(index) => { - value = self.get_irr(index.as_usize()); + value = self.regs().IRR[index.as_usize()].get() as _; debug!("[VLAPIC] read IRR[{}] register: {:#010X}", index, value); } ApicRegOffset::ESR => { - value = self.get_esr(); + value = self.regs().ESR.get() as _; debug!("[VLAPIC] read ESR register: {:#010X}", value); } + ApicRegOffset::ICRLow => { + value = self.regs().ICR_LO.get() as _; + debug!("[VLAPIC] read ICR_LOW register: {:#010X}", value); + if width == AccessWidth::Qword { + let icr_hi = self.regs().ICR_HI.get() as usize; + value |= icr_hi << 32; + debug!("[VLAPIC] read ICR register: {:#018X}", value); + } + } + ApicRegOffset::ICRHi => { + value = self.regs().ICR_HI.get() as _; + debug!("[VLAPIC] read ICR_HI register: {:#010X}", value); + } + ApicRegOffset::LvtCMCI => { + value = self.lvt_last.lvt_cmci.get() as _; + debug!("[VLAPIC] read LVT_CMCI register: {:#010X}", value); + } + ApicRegOffset::LvtTimer => { + value = self.lvt_last.lvt_timer.get() as _; + debug!("[VLAPIC] read LVT_TIMER register: {:#010X}", value); + } + ApicRegOffset::LvtThermal => { + value = self.lvt_last.lvt_thermal.get() as _; + debug!("[VLAPIC] read LvtThermal register: {:#010X}", value); + } + ApicRegOffset::LvtPmc => { + value = self.lvt_last.lvt_perf_count.get() as _; + debug!("[VLAPIC] read LvtPmi register: {:#010X}", value); + } + ApicRegOffset::LvtLint0 => { + value = self.lvt_last.lvt_lint0.get() as _; + debug!("[VLAPIC] read LvtLint0 register: {:#010X}", value); + } + ApicRegOffset::LvtLint1 => { + value = self.lvt_last.lvt_lint1.get() as _; + debug!("[VLAPIC] read LvtLint1 register: {:#010X}", value); + } + ApicRegOffset::LvtErr => { + value = self.lvt_last.lvt_err.get() as _; + debug!("[VLAPIC] read LvtErr register: {:#010X}", value); + } + ApicRegOffset::TimerInitCount => { + value = self.regs().ICR_TIMER.get() as _; + debug!("[VLAPIC] read TimerInitCount register: {:#010X}", value); + } + ApicRegOffset::TimerCurCount => { + value = self.regs().CCR_TIMER.get() as _; + debug!("[VLAPIC] read TimerCurCount register: {:#010X}", value); + } + ApicRegOffset::TimerDivConf => { + value = self.regs().DCR_TIMER.get() as _; + debug!("[VLAPIC] read TimerDivConf register: {:#010X}", value); + } _ => { warn!("[VLAPIC] read unknown APIC register: {:?}", offset); } } - value + Ok(value) + } + + pub fn handle_write( + &self, + offset: ApicRegOffset, + width: AccessWidth, + context: DeviceRWContext, + ) -> AxResult { + Ok(()) } } From 2f4e8a36b2a757afd062ae95a6f020409f0d6565 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Fri, 17 Jan 2025 22:44:33 +0800 Subject: [PATCH 004/132] fxmac ethernet Rust driver on PhytiumPi --- Cargo.lock | 32 ++ Cargo.toml | 3 +- README.md | 2 +- src/fxmac.rs | 758 +++++++++++++++++-------- src/fxmac_const.rs | 682 ++++++++++++++++++++++ src/fxmac_dma.rs | 1341 +++++++++++++++++++++++++++++++++++++++----- src/fxmac_intr.rs | 438 +++++++++++++++ src/fxmac_phy.rs | 192 ++++--- src/lib.rs | 9 +- src/macb_const.rs | 894 ----------------------------- src/utils.rs | 191 +++++++ 11 files changed, 3215 insertions(+), 1327 deletions(-) create mode 100644 Cargo.lock create mode 100644 src/fxmac_const.rs create mode 100644 src/fxmac_intr.rs delete mode 100644 src/macb_const.rs create mode 100644 src/utils.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..c3a2b5ace --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,32 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aarch64-cpu" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a21cd0131c25c438e19cd6a774adf7e3f64f7f4d723022882facc2dee0f8bc9" +dependencies = [ + "tock-registers", +] + +[[package]] +name = "fxmac_rs" +version = "0.1.0" +dependencies = [ + "aarch64-cpu", + "log", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "tock-registers" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b9e2fdb3a1e862c0661768b7ed25390811df1947a8acbfbefe09b47078d93c4" diff --git a/Cargo.toml b/Cargo.toml index 7a5ce232b..c5e7ddd3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,5 @@ repository = "https://github.com/elliott10/cadence-macb.git" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -log = "0.4" \ No newline at end of file +log = "0.4" +aarch64-cpu = "10" \ No newline at end of file diff --git a/README.md b/README.md index 315160f80..b1d5d92af 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Cadence Macb ethernet driver -cadence-macb ethernet Rust driver on SiFive FU740 board. +fxmac ethernet Rust driver on PhytiumPi board. ### Quick Start diff --git a/src/fxmac.rs b/src/fxmac.rs index 397c3fc3b..e5aff0234 100644 --- a/src/fxmac.rs +++ b/src/fxmac.rs @@ -1,63 +1,87 @@ -pub struct FXmac { - FXmacConfig config; - is_ready: u32, /* Device is ininitialized and ready*/ - is_started: u32, - link_status: u32, /* indicates link status ,FXMAC_LINKUP is link up ,FXMAC_LINKDOWN is link down,FXMAC_NEGOTIATING is need to negotiating*/ - options: u32, - mask: u32, /* indicates intr mask */ - caps: u32, /* Capability mask bits */ +use core::sync::atomic::Ordering; + +use alloc::boxed::Box; +use log::*; +use crate::fxmac_const::*; +use crate::fxmac_phy::*; +use crate::fxmac_dma::*; +use crate::fxmac_intr::*; +use crate::utils::*; + +pub const FXMAC_HANDLER_DMASEND: u32 = 1; /* 发送中断 */ +pub const FXMAC_HANDLER_DMARECV: u32 = 2; /* 接收中断 */ +pub const FXMAC_HANDLER_ERROR: u32 = 3; /* 异常中断 */ +pub const FXMAC_HANDLER_LINKCHANGE: u32 = 4; /* 连接状态 */ +pub const FXMAC_HANDLER_RESTART: u32 = 5; /* 发送描述符队列发生异常 */ - FXmacQueue tx_bd_queue; /* Transmit Queue */ - FXmacQueue rx_bd_queue; /* Receive Queue */ +pub const FXMAC_LINKDOWN: u32 = 0; +pub const FXMAC_LINKUP: u32 = 1; +pub const FXMAC_NEGOTIATING: u32 = 2; - /* - FXmacIrqHandler send_irq_handler; - void *send_args; +pub const FXMAC0_PCLK: u32 = 50000000; +pub const FXMAC0_HOTPLUG_IRQ_NUM: u32 = 53 + 30; +pub const FXMAC_QUEUE_MAX_NUM: u32 = 4; // 16 - FXmacIrqHandler recv_irq_handler; - void *recv_args; +pub const ULONG64_HI_MASK: u64 = 0xFFFFFFFF00000000; +pub const ULONG64_LO_MASK: u64 = !ULONG64_HI_MASK; - FXmacErrorIrqHandler error_irq_handler; - void *error_args; +pub const FT_COMPONENT_IS_READY: u32 = 0x11111111; +pub const FT_COMPONENT_IS_STARTED: u32 = 0x22222222; - FXmacIrqHandler link_change_handler; - void *link_change_args; +pub const PAGE_SIZE: usize = 4096; +pub(crate) const FXMAC_IOBASE: u64 = 0x3200c000; // FXMAC0_BASE_ADDR - FXmacIrqHandler restart_handler; - void *restart_args; - */ +pub struct FXmac { + pub config: FXmacConfig, + pub is_ready: u32, /* Device is ininitialized and ready*/ + pub is_started: u32, + pub link_status: u32, /* indicates link status ,FXMAC_LINKUP is link up ,FXMAC_LINKDOWN is link down,FXMAC_NEGOTIATING is need to negotiating*/ + pub options: u32, + pub mask: u32, /* indicates intr mask */ + pub caps: u32, /* Capability mask bits */ + + pub lwipport: FXmacLwipPort, - moudle_id: u32, /* Module identification number */ - max_mtu_size: u32, - max_frame_size: u32, + pub tx_bd_queue: FXmacQueue, /* Transmit Queue */ + pub rx_bd_queue: FXmacQueue, /* Receive Queue */ - phy_address: u32, /* phy address */ - rxbuf_mask: u32, /* 1000,100,10 */ + pub moudle_id: u32, /* Module identification number */ + pub max_mtu_size: u32, + pub max_frame_size: u32, + pub phy_address: u32, /* phy address */ + pub rxbuf_mask: u32, /* 1000,100,10 */ } pub struct FXmacConfig { - instance_id: u32, /* Id of device*/ - base_address: u64, - extral_mode_base: u64, - extral_loopback_base: u64, - interface: FXmacPhyInterface; - speed: u32, /* FXMAC_SPEED_XXX */ - duplex: u32, /* 1 is full-duplex , 0 is half-duplex */ - auto_neg: u32, /* Enable auto-negotiation - when set active high, autonegotiation operation is enabled. */ - pclk_hz: u32, - max_queue_num: u32, /* Number of Xmac Controller Queues */ - tx_queue_id: u32, /* 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number */ - rx_queue_id: u32, /* 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number */ - hotplug_irq_num: u32, - dma_brust_length: u32, /* burst length */ - network_default_config: u32, - queue_irq_num[u32; FXMAC_QUEUE_MAX_NUM]; /* mac0 8个 ,其他的 4个 */ - caps: u32, /* used to configure tail ptr feature */ + pub instance_id: u32, /* Id of device*/ + pub base_address: u64, + pub extral_mode_base: u64, + pub extral_loopback_base: u64, + pub interface: FXmacPhyInterface, + pub speed: u32, /* FXMAC_SPEED_XXX */ + pub duplex: u32, /* 1 is full-duplex , 0 is half-duplex */ + pub auto_neg: u32, /* Enable auto-negotiation - when set active high, autonegotiation operation is enabled. */ + pub pclk_hz: u32, + pub max_queue_num: u32, /* Number of Xmac Controller Queues */ + pub tx_queue_id: u32, /* 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number */ + pub rx_queue_id: u32, /* 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number */ + pub hotplug_irq_num: u32, + pub dma_brust_length: u32, /* burst length */ + pub network_default_config: u32, + pub queue_irq_num: [u32; FXMAC_QUEUE_MAX_NUM as usize], /* mac0 8个 ,其他的 4个 */ + pub caps: u32, /* used to configure tail ptr feature */ + pub mac: [u8; 6], +} + +pub struct FXmacQueue { + pub queue_id: u32, + pub bdring: FXmacBdRing, } /// Interface Mode definitions +#[derive(Debug, Clone, Copy, PartialEq)] pub enum FXmacPhyInterface { FXMAC_PHY_INTERFACE_MODE_SGMII = 0, @@ -69,14 +93,92 @@ pub enum FXmacPhyInterface FXMAC_PHY_INTERFACE_MODE_2500BASEX = 6, } + +pub fn read_reg(src: *const T) -> T { + unsafe { core::ptr::read_volatile(phys_to_virt(src as usize) as *const T) } +} + +pub fn write_reg(dst: *mut T, value: T) { + unsafe { + core::ptr::write_volatile(phys_to_virt(dst as usize) as *mut T, value); + } +} + pub fn xmac_init() -> i32 { + let mut hwaddr: [u8; 6] = [0x98, 0x0e, 0x24, 0x00, 0x11, 0x0]; - let xmac = FXmac{ }; +/* +FXmacConfig mac_config: +mac_config.instance_id=0, +mac_config.base_address=0x3200c000, +mac_config.extral_mode_base=0x3200dc00, +mac_config.extral_loopback_base=0x3200dc04, +mac_config.interface=0, +mac_config.speed=100, +mac_config.duplex=1, +mac_config.auto_neg=0, +mac_config.pclk_hz=50000000, +mac_config.max_queue_num=4, +mac_config.tx_queue_id=0, +mac_config.rx_queue_id=0 +mac_config.hotplug_irq_num=83, +mac_config.dma_brust_length=16, +mac_config.network_default_config=0x37f0, +mac_config.queue_irq_num[0]=87, +mac_config.caps=0 +*/ + let mut mac_config: FXmacConfig = FXmacConfig { + instance_id: FXMAC0_ID, + base_address: FXMAC0_BASE_ADDR as u64, + extral_mode_base: FXMAC0_MODE_SEL_BASE_ADDR as u64, + extral_loopback_base: FXMAC0_LOOPBACK_SEL_BASE_ADDR as u64, + interface: FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII, + speed: 100, + duplex: 1, + auto_neg: 0, + pclk_hz: FXMAC0_PCLK, + max_queue_num: 4, // .max_queue_num = 16 + tx_queue_id: 0, + rx_queue_id: 0, + hotplug_irq_num: FXMAC0_HOTPLUG_IRQ_NUM, + dma_brust_length: 16, + network_default_config: FXMAC_DEFAULT_OPTIONS, + queue_irq_num: [FXMAC0_QUEUE0_IRQ_NUM, FXMAC0_QUEUE1_IRQ_NUM, FXMAC0_QUEUE2_IRQ_NUM, FXMAC0_QUEUE3_IRQ_NUM], + caps: 0, + mac: hwaddr, + }; + + let mut xmac = FXmac { + config: mac_config, + is_ready: FT_COMPONENT_IS_READY, + is_started: 0, + link_status: FXMAC_LINKDOWN, + options: 0, + mask: 0, + caps: 0, + lwipport: FXmacLwipPort{ + buffer: FXmacNetifBuffer::default(), + feature: FXMAC_LWIP_PORT_CONFIG_MULTICAST_ADDRESS_FILITER, + hwaddr, + recv_flg: 0, + }, + tx_bd_queue: FXmacQueue{ queue_id: 0, bdring: FXmacBdRing::default()}, + rx_bd_queue: FXmacQueue{ queue_id: 0, bdring: FXmacBdRing::default()}, + moudle_id: 0, + max_mtu_size: 0, + max_frame_size: 0, + phy_address: 0, + rxbuf_mask: 0, + }; - //mii_interface = FXMAC_LWIP_PORT_INTERFACE_SGMII; +// xmac_config: interface=FXMAC_PHY_INTERFACE_MODE_SGMII, autonegotiation=0, phy_speed=FXMAC_PHY_SPEED_100M, phy_duplex=FXMAC_PHY_FULL_DUPLEX +// FXmacDmaReset, moudle_id=12, max_frame_size=1518, max_queue_num=4 (或16), dma_brust_length=16 +// network_default_config = 0x37f0, base_address=0x3200c000,FXMAC_RXBUF_HASH_MASK: GENMASK(30, 29)= 0x60000000 (0b 0110_0000_0000_0000_0000000000000000) + // mii_interface = 1 = FXMAC_LWIP_PORT_INTERFACE_SGMII; +// FXmacLwipPortInit(): /* step 1: initialize instance */ /* step 2: depend on config set some options : JUMBO / IGMP */ /* step 3: FXmacSelectClk */ @@ -86,84 +188,162 @@ pub fn xmac_init() -> i32 { /* step 7: initialize interrupt */ /* step 8: start mac */ -// FXmacLwipPortInit() -// fxmac_cfg_tbl[0].base_address = 0x3200c000 -// xmac_config: interface=FXMAC_PHY_INTERFACE_MODE_SGMII, autonegotiation=0, phy_speed=FXMAC_PHY_SPEED_100M, phy_duplex=FXMAC_PHY_FULL_DUPLEX +let mut status: u32 = 0; // Reset the hardware and set default options -let link_status = FXMAC_LINKDOWN; -let is_ready = FT_COMPONENT_IS_READY; -FXmacReset(); +//xmac.link_status = FXMAC_LINKDOWN; +//xmac.is_ready = FT_COMPONENT_IS_READY; -// irq_handler = (FXmacIrqHandler)FXmacIrqStubHandler; -let mask = FXMAC_INTR_MASK; +FXmacReset(&mut xmac); -if (config.caps & FXMAC_CAPS_TAILPTR) != 0 -{ - FXmacSetOptions(xmac_p, FXMAC_TAIL_PTR_OPTION, 0); - xmac_p->mask &= !FXMAC_IXR_TXUSED_MASK; +// irq_handler = (FXmacIrqHandler)FXmacIrqStubHandler; +// interrupts bit mask +xmac.mask = FXMAC_IXR_LINKCHANGE_MASK | FXMAC_IXR_TX_ERR_MASK | FXMAC_IXR_RX_ERR_MASK | FXMAC_IXR_RXCOMPL_MASK; // FXMAC_INTR_MASK +if (xmac.config.caps & FXMAC_CAPS_TAILPTR) != 0 +{ + FXmacSetOptions(&mut xmac, FXMAC_TAIL_PTR_OPTION, 0); + xmac.mask &= !FXMAC_IXR_TXUSED_MASK; } -FxmacFeatureSetOptions(instance_p->feature,xmac_p); +// xmac.lwipport.feature = LWIP_PORT_MODE_MULTICAST_ADDRESS_FILITER; + +FxmacFeatureSetOptions(xmac.lwipport.feature, &mut xmac); -status = FXmacSetMacAddress(xmac_p, (void *)(instance_p->hwaddr), 0); +status = FXmacSetMacAddress( &xmac.lwipport.hwaddr, 0); +//mac_config.interface = FXMAC_PHY_INTERFACE_MODE_SGMII; -if(mac_config.interface != FXMAC_PHY_INTERFACE_MODE_USXGMII) -{ +if xmac.config.interface != FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_USXGMII +{ /* initialize phy */ - status = FXmacPhyInit(xmac_p, xmac_p->config.speed, xmac_p->config.duplex, xmac_p->config.auto_neg, XMAC_PHY_RESET_ENABLE); - if (status != FT_SUCCESS) + status = FXmacPhyInit(&mut xmac, XMAC_PHY_RESET_ENABLE); + if status != 0 { warn!("FXmacPhyInit is error"); - } + } } else { info!("interface == FXMAC_PHY_INTERFACE_MODE_USXGMII"); -} +} -FXmacSelectClk(xmac_p); -FXmacInitInterface(xmac_p); +FXmacSelectClk(&mut xmac); +FXmacInitInterface(&mut xmac); // initialize dma -dmacrreg = FXMAC_READREG32(xmac_p->config.base_address, FXMAC_DMACR_OFFSET); +let mut dmacrreg: u32 = read_reg((xmac.config.base_address + FXMAC_DMACR_OFFSET) as *const u32); dmacrreg &= !(FXMAC_DMACR_BLENGTH_MASK); dmacrreg = dmacrreg | FXMAC_DMACR_INCR16_AHB_AXI_BURST; /* Attempt to use bursts of up to 16. */ -write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, dmacrreg); - -// TODO -FXmacInitDma(instance_p); +write_reg((xmac.config.base_address + FXMAC_DMACR_OFFSET) as *mut u32, dmacrreg); -/* initialize interrupt */ -FXmacSetupIsr(instance_p); +FXmacInitDma(&mut xmac); -return FT_SUCCESS; +// initialize interrupt +// 网卡中断初始化设置 +FXmacSetupIsr(&mut xmac); +// end of FXmacLwipPortInit() +if (xmac.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_UNICAST_ADDRESS_FILITER) != 0 { +debug!("Set unicast hash table"); +FXmac_SetHash(&mut xmac, &hwaddr); +} +/* 注册了 lwip_port->ops: +ethernetif_link_detect() +ethernetif_input() +ethernetif_deinit() +ethernetif_start() -> FXmacLwipPortStart() -> FXmacStart() +ethernetif_debug() +*/ -// Set unicast hash table +///////// -FXmac_SetHash() +// ethernetif_start() +// start mac +FXmacStart(&mut xmac); -/* -ethernetif_link_detect() +// 开始发包的函数:FXmacLwipPortTx()->FXmacSgsend() -> FXmacSendHandler() -> FXmacProcessSentBds() +// 触发中断函数:FXmacIntrHandler() +// 收包handle: FXmacRecvIsrHandler()->FXmacRecvHandler -ethernetif_input() -ethernetif_deinit() +XMAC.store(Box::into_raw(Box::new(xmac)), Ordering::Relaxed); -ethernetif_start() +0 +} -ethernetif_debug() +/// FXmacStart(): Start mac +/// Start the Ethernet controller as follows: +/// - Enable transmitter if FXMAC_TRANSMIT_ENABLE_OPTION is set +/// - Enable receiver if FXMAC_RECEIVER_ENABLE_OPTION is set +/// - Start the SG DMA send and receive channels and enable the device +/// interrupt +pub fn FXmacStart(instance_p: &mut FXmac) +{ -*/ + assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); + + /* clear any existed int status */ + write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_ALL_MASK); + + /* Enable transmitter if not already enabled */ + if (instance_p.config.network_default_config & FXMAC_TRANSMITTER_ENABLE_OPTION as u32) != 0 + { + let reg_val = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + if (reg_val & FXMAC_NWCTRL_TXEN_MASK) == 0 + { + write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_val | FXMAC_NWCTRL_TXEN_MASK as u32); + } + } + + /* Enable receiver if not already enabled */ + if (instance_p.config.network_default_config & FXMAC_RECEIVER_ENABLE_OPTION) != 0 + { + + let reg_val = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + info!("Enable receiver, FXMAC_NWCTRL_OFFSET = {:#x}", reg_val); + if (reg_val & FXMAC_NWCTRL_RXEN_MASK) == 0 + { + write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_val | FXMAC_NWCTRL_RXEN_MASK as u32); + } + } + info!("FXMAC_NWCTRL_OFFSET = {:#x}", read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32)); + + info!("Enable TX and RX by Mask={:#x}", instance_p.mask); + + // Enable TX and RX interrupt + //FXMAC_INT_ENABLE(instance_p, instance_p->mask); + // Enable interrupts specified in 'Mask'. The corresponding interrupt for each bit set to 1 in 'Mask', will be enabled. + write_reg((instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, + instance_p.mask & FXMAC_IXR_ALL_MASK); + + // Mark as started + instance_p.is_started = FT_COMPONENT_IS_STARTED; +} +/// Gracefully stop the Ethernet MAC as follows: +/// - Disable all interrupts from this device +/// - Stop DMA channels +/// - Disable the tansmitter and receiver +pub fn FXmacStop(instance_p: &mut FXmac) +{ + assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); + // Disable all interrupts + write_reg((instance_p.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_ALL_MASK); + + /* Disable the receiver & transmitter */ + let mut reg_val: u32 = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + reg_val &= !(FXMAC_NWCTRL_RXEN_MASK as u32); + reg_val &= !(FXMAC_NWCTRL_TXEN_MASK as u32); + write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_val); + + // Mark as stopped + instance_p.is_started = 0; } /* @@ -181,34 +361,39 @@ ethernetif_debug() * */ -fn FXmacReset() { - let mac_addr: [u8; 6] = [0; 6]; - let mut reg_val: u32 = 0 - let mut write_reg: u32 = 0; +fn FXmacReset(instance_p: &mut FXmac) { + let mut mac_addr: [u8; 6] = [0; 6]; /* Stop the device and reset hardware */ - FXmacStop(); + FXmacStop(instance_p); // Module identification number - let moudle_id = ( read_reg((FXMAC_IOBASE + FXMAC_REVISION_REG_OFFSET) as *const u32) - & FXMAC_IDENTIFICATION_MASK ) >> 16; - let mut max_mtu_size = FXMAC_MTU; - let mut max_frame_size = FXMAC_MAX_FRAME_SIZE; - let max_queue_num = 16; - let network_default_config = FXMAC_DEFAULT_OPTIONS; + // instance_p->moudle_id = 12 + instance_p.moudle_id = ( read_reg((FXMAC_IOBASE + FXMAC_REVISION_REG_OFFSET) as *const u32) & + FXMAC_IDENTIFICATION_MASK ) >> 16; + info!("FXmacReset, Got Moudle IDENTIFICATION: {}", instance_p.moudle_id); + + instance_p.max_mtu_size = FXMAC_MTU; + instance_p.max_frame_size = FXMAC_MAX_FRAME_SIZE; + instance_p.config.max_queue_num = 16; + instance_p.config.dma_brust_length = 16; + instance_p.config.network_default_config = FXMAC_DEFAULT_OPTIONS; + + instance_p.config.pclk_hz = FXMAC0_PCLK; // 50000000 + let netctrl = (FXMAC_NWCTRL_STATCLR_MASK & !(FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK as u32)) | FXMAC_NWCTRL_MDEN_MASK; write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, netctrl); - FXmacConfigureCaps(); + FXmacConfigureCaps(instance_p); // mdio clock division - let mut write_reg = FXmacClkDivGet(moudle_id); - // DMA bus width - write_reg |= FXmacDmaWidth(moudle_id); - write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, write_reg); + let mut w_reg: u32 = FXmacClkDivGet(instance_p); + // DMA bus width, DMA位宽为128 + w_reg |= FXmacDmaWidth(instance_p.moudle_id); + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, w_reg); - FXmacDmaReset(moudle_id, max_frame_size, max_queue_num); + FXmacDmaReset(instance_p); // This register, when read provides details of the status of the receive path. write_reg((FXMAC_IOBASE + FXMAC_RXSR_OFFSET) as *mut u32, FXMAC_SR_ALL_MASK); @@ -216,7 +401,7 @@ fn FXmacReset() { // write 1 ro the relavant bit location disable that particular interrupt write_reg((FXMAC_IOBASE + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_ALL_MASK); - reg_val = read_reg((FXMAC_IOBASE + FXMAC_ISR_OFFSET) as *const u32); + let reg_val: u32 = read_reg((FXMAC_IOBASE + FXMAC_ISR_OFFSET) as *const u32); write_reg((FXMAC_IOBASE + FXMAC_ISR_OFFSET) as *mut u32, reg_val); write_reg((FXMAC_IOBASE + FXMAC_TXSR_OFFSET) as *mut u32, FXMAC_SR_ALL_MASK); @@ -225,70 +410,57 @@ fn FXmacReset() { // set default mac address for i in 0..4 { - FXmacSetMacAddress(mac_addr, i); - FXmacGetMacAddress(mac_addr, i); + FXmacSetMacAddress(&mac_addr, i); + FXmacGetMacAddress(&mut mac_addr, i); FXmacSetTypeIdCheck(0, i); } /* clear all counters */ for i in 0..((FXMAC_LAST_OFFSET - FXMAC_OCTTXL_OFFSET) / 4) { - read_reg((FXMAC_IOBASE + FXMAC_OCTTXL_OFFSET + (i * 4) as u32) as *mut u32); + read_reg((FXMAC_IOBASE + FXMAC_OCTTXL_OFFSET + (i * 4)) as *mut u32); } /* Sync default options with hardware but leave receiver and * transmitter disabled. They get enabled with FXmacStart() if * FXMAC_TRANSMITTER_ENABLE_OPTION and FXMAC_RECEIVER_ENABLE_OPTION are set. */ - FXmacSetOptions(max_mtu_size, max_frame_size, max_queue_num, network_default_config & !((FXMAC_TRANSMITTER_ENABLE_OPTION | FXMAC_RECEIVER_ENABLE_OPTION) as u32), 0); - FXmacClearOptions(max_mtu_size, max_frame_size, max_queue_num, !network_default_config, 0); + let options = instance_p.config.network_default_config & !((FXMAC_TRANSMITTER_ENABLE_OPTION | FXMAC_RECEIVER_ENABLE_OPTION) as u32); + FXmacSetOptions(instance_p, options, 0); + let options = !instance_p.config.network_default_config; + FXmacClearOptions(instance_p, options, 0); } -/* - * Gracefully stop the Ethernet MAC as follows: - * - Disable all interrupts from this device - * - Stop DMA channels - * - Disable the tansmitter and receiver - */ - pub fn FXmacStop() { - // Disable all interrupts - write_reg((FXMAC_IOBASE + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_ALL_MASK); - // Disable the receiver & transmitter - let reg_val = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - reg_val &= (!FXMAC_NWCTRL_RXEN_MASK) as u32; - reg_val &= (!FXMAC_NWCTRL_TXEN_MASK) as u32; - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_val); - } - - fn FXmacDmaReset(moudle_id: u32, max_frame_size: u32, max_queue_num: u32) + fn FXmacDmaReset(instance_p: &mut FXmac) { + let max_frame_size: u32 = instance_p.max_frame_size; + let mut dmacfg: u32 = 0; //let max_queue_num = 16; - let dma_brust_length = 16; + //let dma_brust_length = 16; let mut rx_buf_size: u32 = max_frame_size / FXMAC_RX_BUF_UNIT; - rx_buf_size += ((max_frame_size % FXMAC_RX_BUF_UNIT) != 0) ? 1 : 0; /* roundup */ + rx_buf_size += if (max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { 1 } else { 0 }; /* roundup */ - let FXMAC_RXBUFQX_SIZE_OFFSET = |value: u32| (FXMAC_RXBUFQ1_SIZE_OFFSET + (value << 2)); - // moudle_id=12 - if (moudle_id >= 2) + if (instance_p.moudle_id >= 2) { - for queue in 0..max_queue_num { + for queue in 0..instance_p.config.max_queue_num { dmacfg = 0; - FXmacSetQueuePtr(0, queue, FXMAC_SEND); - FXmacSetQueuePtr(0, queue, FXMAC_RECV); + + // 设置发包/收包 buffer队列的基地址 + FXmacSetQueuePtr(0, queue as u8, FXMAC_SEND); + FXmacSetQueuePtr(0, queue as u8, FXMAC_RECV); if queue != 0 { - write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue)) as *mut u32, rx_buf_size); - } - else /* queue is 0 */ + write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue as u64)) as *mut u32, rx_buf_size); + } else /* queue is 0 */ { dmacfg |= (FXMAC_DMACR_RXBUF_MASK & (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT)); } } - dmacfg |= (dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); + dmacfg |= (instance_p.config.dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); dmacfg &= !FXMAC_DMACR_ENDIAN_MASK; dmacfg &= !FXMAC_DMACR_SWAP_MANAGEMENT_MASK; /* 选择小端 */ @@ -302,6 +474,7 @@ fn FXmacReset() { which avoid endless rx buffer not available error intrrupts. */ dmacfg |= FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK; /* force_discard_on_rx_err */ + dmacfg |= FXMAC_DMACR_ADDR_WIDTH_64; // Just for aarch64 } else @@ -309,7 +482,7 @@ fn FXmacReset() { FXmacSetQueuePtr(0, 0, FXMAC_SEND); FXmacSetQueuePtr(0, 0, FXMAC_RECV); dmacfg |= (FXMAC_DMACR_RXBUF_MASK & (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT)); - dmacfg |= (dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); + dmacfg |= (instance_p.config.dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); dmacfg &= !FXMAC_DMACR_ENDIAN_MASK; dmacfg &= !FXMAC_DMACR_SWAP_MANAGEMENT_MASK; /* 选择小端 */ @@ -335,7 +508,7 @@ fn FXmacDmaWidth(moudle_id: u32) -> u32 { return FXMAC_NWCFG_BUS_WIDTH_32_MASK; } - let read_regs = read_reg(FXMAC_IOBASE, FXMAC_DESIGNCFG_DEBUG1_OFFSET); + let read_regs = read_reg((FXMAC_IOBASE + FXMAC_DESIGNCFG_DEBUG1_OFFSET as u64) as *const u32); match ((read_regs & FXMAC_DESIGNCFG_DEBUG1_BUS_WIDTH_MASK) >> 25) { 4 => { info!("bus width is 128"); @@ -354,7 +527,7 @@ fn FXmacDmaWidth(moudle_id: u32) -> u32 { -fn FxmacFeatureSetOptions(feature: u32, FXmac* xmac_p) +fn FxmacFeatureSetOptions(feature: u32, xmac_p: &mut FXmac) { let mut options: u32 = 0; @@ -403,37 +576,39 @@ fn FxmacFeatureSetOptions(feature: u32, FXmac* xmac_p) * The buffer queue addresses has to be set before starting the transfer, so * this function has to be called in prior to FXmacStart() */ - fn FXmacSetQueuePtr(queue_p: u64, queue_num: u8, direction: u32) { + pub fn FXmacSetQueuePtr(queue_p: u64, queue_num: u8, direction: u32) { - let flag_queue_p = if queue_p == 0 { 1 }else{ 0 }; + //assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); + // If already started, then just return - let FXMAC_QUEUE_REGISTER_OFFSET = |base_addr: u32, queue_id: u32| (base_addr + (queue_id - 1) * 4); + let flag_queue_p = if queue_p == 0 { 1 }else{ 0 }; + let FXMAC_QUEUE_REGISTER_OFFSET = |base_addr: u64, queue_id: u64| (base_addr + (queue_id - 1) * 4); - if queue_num == 0x00U { + if queue_num == 0 { if direction == FXMAC_SEND { /* set base start address of TX buffer queue (tx buffer descriptor list) */ write_reg((FXMAC_IOBASE + FXMAC_TXQBASE_OFFSET) as *mut u32, - (queue_p & ULONG64_LO_MASK) | flag_queue_p); + ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32); } else { /* set base start address of RX buffer queue (rx buffer descriptor list) */ write_reg((FXMAC_IOBASE + FXMAC_RXQBASE_OFFSET) as *mut u32, - (queue_p & ULONG64_LO_MASK) | flag_queue_p); + ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32); } } else { if direction == FXMAC_SEND { - write_reg((FXMAC_IOBASE + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, queue_num),) as *mut u32, - (queue_p & ULONG64_LO_MASK) | flag_queue_p); + write_reg((FXMAC_IOBASE + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, queue_num as u64)) as *mut u32, + ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32); } else { - write_reg((FXMAC_IOBASE + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_RXQ1BASE_OFFSET, queue_num)) as *mut u32, - (queue_p & ULONG64_LO_MASK) | flag_queue_p); + write_reg((FXMAC_IOBASE + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_RXQ1BASE_OFFSET, queue_num as u64)) as *mut u32, + ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32); } } @@ -441,27 +616,28 @@ fn FxmacFeatureSetOptions(feature: u32, FXmac* xmac_p) { /* Set the MSB of TX Queue start address */ write_reg((FXMAC_IOBASE + FXMAC_MSBBUF_TXQBASE_OFFSET) as *mut u32, - ((queue_p & ULONG64_HI_MASK) >> 32U) as u32); + ((queue_p & ULONG64_HI_MASK) >> 32) as u32); } else { /* Set the MSB of RX Queue start address */ write_reg((FXMAC_IOBASE + FXMAC_MSBBUF_RXQBASE_OFFSET) as *mut u32, - ((queue_p & ULONG64_HI_MASK) >> 32U) as u32); + ((queue_p & ULONG64_HI_MASK) >> 32) as u32); } } -fn FXmacConfigureCaps() { +fn FXmacConfigureCaps(instance_p: &mut FXmac) { + instance_p.caps = 0; let read_regs = read_reg((FXMAC_IOBASE + FXMAC_DESIGNCFG_DEBUG1_OFFSET) as *const u32); if (read_regs & FXMAC_DESIGNCFG_DEBUG1_BUS_IRQCOR_MASK) == 0 { - // let caps |= FXMAC_CAPS_ISR_CLEAR_ON_WRITE; + instance_p.caps |= FXMAC_CAPS_ISR_CLEAR_ON_WRITE; info!("Design ConfigReg1: {:#x} Has FXMAC_CAPS_ISR_CLEAR_ON_WRITE feature", read_regs); } } -fn FXmacClkDivGet(moudle_id: u32) -> u32 { +fn FXmacClkDivGet(instance_p: &mut FXmac) -> u32 { // moudle_id=12 // let pclk_hz = 50000000; - let pclk_hz = FXMAC0_PCLK; + let pclk_hz = instance_p.config.pclk_hz; // FXMAC0_PCLK; if (pclk_hz <= 20000000) { @@ -475,7 +651,7 @@ fn FXmacClkDivGet(moudle_id: u32) -> u32 { { return FXMAC_NWCFG_CLOCK_DIV32_MASK; } - else if (moudle_id >= 2) + else if (instance_p.moudle_id >= 2) { if (pclk_hz <= 120000000) { @@ -509,21 +685,20 @@ fn FXmacClkDivGet(moudle_id: u32) -> u32 { * Set options for the driver/device. The driver should be stopped with * FXmacStop() before changing options. */ - fn FXmacSetOptions(max_mtu_size: mut u32, max_frame_size: mut u32, max_queue_num: u32, options: mut u32, queue_num: u32) -> u32 { + fn FXmacSetOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u32 { let mut reg: u32 = 0; /* Generic register contents */ let mut reg_netcfg: u32 = 0; /* Reflects original contents of NET_CONFIG */ let mut reg_new_netcfg: u32 = 0; /* Reflects new contents of NET_CONFIG */ let mut status: u32 = 0; - let FXMAC_RXBUFQX_SIZE_OFFSET = |value: u32| (FXMAC_RXBUFQ1_SIZE_OFFSET + (value << 2)); - let is_started = 0; + //let is_started = 0; - info!("FXmacSetOptions, is_started={}, options={}, queue_num={}, max_queue_num={}", is_started, options, queue_num, max_queue_num); + info!("FXmacSetOptions, is_started={}, options={}, queue_num={}, max_queue_num={}", instance_p.is_started, options, queue_num, instance_p.config.max_queue_num); /* Be sure device has been stopped */ - if is_started == FT_COMPONENT_IS_STARTED + if instance_p.is_started == FT_COMPONENT_IS_STARTED { - status = FXMAC_ERR_MAC_IS_PROCESSING; + status = 9; //FXMAC_ERR_MAC_IS_PROCESSING; error!("FXMAC is processing when calling FXmacSetOptions function"); } else { @@ -592,7 +767,7 @@ fn FXmacClkDivGet(moudle_id: u32) -> u32 { reg_new_netcfg |= FXMAC_NWCFG_UCASTHASHEN_MASK; } - if options & FXMAC_TAIL_PTR_OPTION + if (options & FXMAC_TAIL_PTR_OPTION) != 0 { write_reg((FXMAC_IOBASE + FXMAC_TAIL_ENABLE) as *mut u32, 0x80000001); } @@ -607,8 +782,8 @@ fn FXmacClkDivGet(moudle_id: u32) -> u32 { /* Enable jumbo frames */ if (options & FXMAC_JUMBO_ENABLE_OPTION) != 0 { - max_mtu_size = FXMAC_MTU_JUMBO; - max_frame_size = FXMAC_MAX_FRAME_SIZE_JUMBO; + instance_p.max_mtu_size = FXMAC_MTU_JUMBO; + instance_p.max_frame_size = FXMAC_MAX_FRAME_SIZE_JUMBO; reg_new_netcfg |= FXMAC_NWCFG_JUMBO_MASK; @@ -622,17 +797,18 @@ fn FXmacClkDivGet(moudle_id: u32) -> u32 { reg &= !FXMAC_DMACR_RXBUF_MASK; - rx_buf_size = max_frame_size / FXMAC_RX_BUF_UNIT; - rx_buf_size += if (max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { 1 } else { 0 }; + rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if (instance_p.max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { 1 } else { 0 }; reg |= (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT) & FXMAC_DMACR_RXBUF_MASK; write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); - } else if queue_num < max_queue_num { + + } else if queue_num < instance_p.config.max_queue_num { let mut rx_buf_size: u32 = 0; - rx_buf_size = max_frame_size / FXMAC_RX_BUF_UNIT; - rx_buf_size += if (max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { 1 } else { 0 }; + rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if (instance_p.max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { 1 } else { 0 }; - write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue_num)) as *mut u32, rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK); + write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue_num as u64)) as *mut u32, rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK); } } @@ -695,27 +871,27 @@ fn FXmacClkDivGet(moudle_id: u32) -> u32 { */ /* Set options word to its new value */ - options |= options; + instance_p.options |= options; - status = FT_SUCCESS; + status = 0; // FT_SUCCESS; } status } /// Clear options for the driver/device -fn FXmacClearOptions(max_mtu_size: mut u32, max_frame_size: mut u32, max_queue_num: u32, options: mut u32, queue_num: u32) -> u32 +fn FXmacClearOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u32 { let mut reg: u32 = 0; /* Generic */ let mut reg_net_cfg: u32 = 0; /* Reflects original contents of NET_CONFIG */ let mut reg_new_net_cfg: u32 = 0; /* Reflects new contents of NET_CONFIG */ let mut status: u32 = 0; - let is_started = 0; + //let is_started = 0; /* Be sure device has been stopped */ - if (is_started == FT_COMPONENT_IS_STARTED) + if (instance_p.is_started == FT_COMPONENT_IS_STARTED) { - status = FXMAC_ERR_MAC_IS_PROCESSING; + status = 9; //FXMAC_ERR_MAC_IS_PROCESSING error!("FXMAC is processing when calling FXmacClearOptions function"); } else { @@ -796,8 +972,8 @@ fn FXmacClearOptions(max_mtu_size: mut u32, max_frame_size: mut u32, max_queue_n /* Disable jumbo frames */ if (options & FXMAC_JUMBO_ENABLE_OPTION) != 0 /* 恢复之前buffer 容量 */ { - max_mtu_size = FXMAC_MTU; - max_frame_size = FXMAC_MAX_FRAME_SIZE; + instance_p.max_mtu_size = FXMAC_MTU; + instance_p.max_frame_size = FXMAC_MAX_FRAME_SIZE; reg_new_net_cfg &= !(FXMAC_NWCFG_JUMBO_MASK as u32); @@ -807,25 +983,25 @@ fn FXmacClearOptions(max_mtu_size: mut u32, max_frame_size: mut u32, max_queue_n if queue_num == 0 { - u32 rx_buf_size = 0; + let mut rx_buf_size: u32 = 0; reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); reg &= !FXMAC_DMACR_RXBUF_MASK; - rx_buf_size = max_frame_size / FXMAC_RX_BUF_UNIT; - rx_buf_size += if max_frame_size % FXMAC_RX_BUF_UNIT != 0 {1} else {0}; + rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if instance_p.max_frame_size % FXMAC_RX_BUF_UNIT != 0 {1} else {0}; reg |= (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT) & FXMAC_DMACR_RXBUF_MASK; write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); } - else if (queue_num < max_queue_num) + else if (queue_num < instance_p.config.max_queue_num) { - let rx_buf_size: u32 = 0; - rx_buf_size = max_frame_size / FXMAC_RX_BUF_UNIT; - rx_buf_size += if (max_frame_size % FXMAC_RX_BUF_UNIT) != 0 {1} else {0}; + let mut rx_buf_size: u32 = 0; + rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if (instance_p.max_frame_size % FXMAC_RX_BUF_UNIT) != 0 {1} else {0}; - write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue_num)) as *mut u32, rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK); + write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue_num as u64)) as *mut u32, rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK); } } @@ -886,9 +1062,9 @@ fn FXmacClearOptions(max_mtu_size: mut u32, max_frame_size: mut u32, max_queue_n */ /* Set options word to its new value */ - options &= !options; + instance_p.options &= !options; - status = FT_SUCCESS; + status = 0; // FT_SUCCESS } status } @@ -903,7 +1079,7 @@ fn FXmacClearHash() { /// Set the MAC address for this driver/device. The address is a 48-bit value. /// The device must be stopped before calling this function. -fn FXmacSetMacAddress(address_ptr: &[u8; 6], index: u8) -> u32 { +pub fn FXmacSetMacAddress(address_ptr: &[u8; 6], index: u8) -> u32 { let mut mac_addr: u32 = 0; let aptr = address_ptr; let index_loc: u8 = index; @@ -912,55 +1088,179 @@ fn FXmacSetMacAddress(address_ptr: &[u8; 6], index: u8) -> u32 { let is_started = 0; /* Be sure device has been stopped */ - if is_started == (u32)FT_COMPONENT_IS_STARTED + if is_started == FT_COMPONENT_IS_STARTED as u32 { - status = FXMAC_ERR_MAC_IS_PROCESSING; + //status = FXMAC_ERR_MAC_IS_PROCESSING; + status = 9; error!("FXMAC is processing when calling FXmacSetMacAddress function"); } else { /* Set the MAC bits [31:0] in BOT */ - mac_addr = aptr[0]; - mac_addr |= aptr[1] as u32 << 8; - mac_addr |= aptr[2] as u32 << 16; - mac_addr |= aptr[3] as u32 << 24; - write_reg((FXMAC_IOBASE + FXMAC_GEM_SA1B + (index_loc * 8) as u32) as *mut u32, mac_addr); + mac_addr = aptr[0] as u32 | ((aptr[1] as u32) << 8) | ((aptr[2] as u32) << 16) | ((aptr[3] as u32) << 24); + write_reg((FXMAC_IOBASE + FXMAC_GEM_SA1B as u64 + (index_loc * 8) as u64) as *mut u32, mac_addr); /* There are reserved bits in TOP so don't affect them */ - mac_addr = read_reg((FXMAC_IOBASE + FXMAC_GEM_SA1T + (index_loc * 8) as u32) as *const u32); + mac_addr = read_reg((FXMAC_IOBASE + FXMAC_GEM_SA1T as u64 + (index_loc * 8) as u64) as *const u32); mac_addr &= !FXMAC_GEM_SAB_MASK; /* Set MAC bits [47:32] in TOP */ mac_addr |= aptr[4] as u32; - mac_addr |= aptr[5] as u32 << 8; + mac_addr |= (aptr[5] as u32) << 8; - write_reg((FXMAC_IOBASE + FXMAC_GEM_SA1T + (index_loc * 8) as u32) as *mut u32, mac_addr); + write_reg((FXMAC_IOBASE + FXMAC_GEM_SA1T as u64 + (index_loc * 8) as u64) as *mut u32, mac_addr); - status = FT_SUCCESS; + status = 0; // FT_SUCCESS } status } +/** + * @name: FXmacGetMacAddress + * @msg: Set the MAC address according to index + * @param {FXmac} *mac is a pointer to the instance to be worked on. + * @param {void} *address_ptr is an output parameter, and is a pointer to a buffer into + * which the current MAC address will be copied. + * @param {u8} index is a index to which MAC (0-3) address. + */ +pub fn FXmacGetMacAddress(address_ptr: &mut [u8; 6], index: u8) +{ + assert!((index as u32)< FXMAC_MAX_MAC_ADDR); + + let mut reg_value: u32 = read_reg((FXMAC_IOBASE + FXMAC_GEM_SA1B as u64 + (index as u64 * 8)) as *const u32); + address_ptr[0] = reg_value as u8; + address_ptr[1] = (reg_value >> 8) as u8; + address_ptr[2] = (reg_value >> 16) as u8; + address_ptr[3] = (reg_value >> 24) as u8; + + reg_value = read_reg((FXMAC_IOBASE + FXMAC_GEM_SA1T as u64 + (index as u64 * 8)) as *const u32); + address_ptr[4] = (reg_value) as u8; + address_ptr[5] = (reg_value >> 8) as u8; +} + +/// Set 48-bit MAC addresses in hash table. +/// The device must be stopped before calling this function. +/// +/// The hash address register is 64 bits long and takes up two locations in +/// the memory map. The least significant bits are stored in hash register +/// bottom and the most significant bits in hash register top. +/// +/// The unicast hash enable and the multicast hash enable bits in the network +/// configuration register enable the reception of hash matched frames. The +/// destination address is reduced to a 6 bit index into the 64 bit hash +/// register using the following hash function. The hash function is an XOR +/// of every sixth bit of the destination address. +pub fn FXmac_SetHash(intance_p: &mut FXmac, mac_address: &[u8; 6]) -> u32 +{ + let mut HashAddr: u32 = 0; + let mut Status: u32 = 0; + debug!("Set MAC: {:x?} in hash table", mac_address); + + // Check that the Ethernet address (MAC) is not 00:00:00:00:00:00 + assert!(!((mac_address[0] == 0) && (mac_address[5] == 0))); + assert!(intance_p.is_ready == FT_COMPONENT_IS_READY); + + /* Be sure device has been stopped */ + if (intance_p.is_started == FT_COMPONENT_IS_STARTED) { + error!("FXmac_SetHash failed: FXMAC_ERR_MAC_IS_PROCESSING"); + Status = 9;// FXMAC_ERR_MAC_IS_PROCESSING + } else { + let Temp1: u8 = ( mac_address[0]) & 0x3F; + let Temp2: u8 = ((mac_address[0] >> 6) & 0x03) | ((mac_address[1] & 0x0F) << 2); + let Temp3: u8 = ((mac_address[1] >> 4) & 0x0F) | ((mac_address[2] & 0x3) << 4); + let Temp4: u8 = ((mac_address[2] >> 2) & 0x3F); + let Temp5: u8 = mac_address[3] & 0x3F; + let Temp6: u8 = ((mac_address[3] >> 6) & 0x03) | ((mac_address[4] & 0x0F) << 2); + let Temp7: u8 = ((mac_address[4] >> 4) & 0x0F) | ((mac_address[5] & 0x03) << 4); + let Temp8: u8 = ((mac_address[5] >> 2) & 0x3F); + + let Result: u32 = (Temp1 as u32) ^ (Temp2 as u32) ^ (Temp3 as u32) ^ (Temp4 as u32) ^ + (Temp5 as u32) ^ (Temp6 as u32) ^ (Temp7 as u32) ^ (Temp8 as u32); + + if (Result >= FXMAC_MAX_HASH_BITS) { + Status = 1;// FXMAC_ERR_INVALID_PARAM + } else { + + if (Result < 32) { + HashAddr = read_reg((intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *const u32); + HashAddr |= 1 << Result; + write_reg((intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *mut u32, HashAddr); + + } else { + HashAddr = read_reg((intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *const u32); + HashAddr |= 1 << (Result - 32); + write_reg((intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *mut u32, HashAddr); + } + Status = 0; + } + } + + Status +} + +/// Delete 48-bit MAC addresses in hash table. +/// The device must be stopped before calling this function. +pub fn FXmac_DeleteHash(intance_p: &mut FXmac, mac_address: &[u8; 6]) -> u32 +{ + let mut HashAddr: u32 = 0; + let mut Status: u32 = 0; + + assert!(intance_p.is_ready == FT_COMPONENT_IS_READY); + + /* Be sure device has been stopped */ + if (intance_p.is_started == FT_COMPONENT_IS_STARTED) { + Status = 9; // (FXMAC_ERR_MAC_IS_PROCESSING); + } else { + let mut Temp1: u8 = ( mac_address[0]) & 0x3F; + let mut Temp2: u8 = ((mac_address[0] >> 6) & 0x03) | ((mac_address[1] & 0x0F) << 2); + let mut Temp3: u8 = ((mac_address[1] >> 4) & 0x0F) | ((mac_address[2] & 0x03) << 4); + let mut Temp4: u8 = ((mac_address[2] >> 2) & 0x3F); + let mut Temp5: u8 = (mac_address[3]) & 0x3F; + let mut Temp6: u8 = ((mac_address[3] >> 6) & 0x03) | ((mac_address[4] & 0x0F) << 2); + let mut Temp7: u8 = ((mac_address[4] >> 4) & 0x0F) | ((mac_address[5] & 0x03) << 4); + let mut Temp8: u8 = ((mac_address[5] >> 2) & 0x3F); + + let Result: u32 = (Temp1 as u32) ^ (Temp2 as u32) ^ (Temp3 as u32) ^ (Temp4 as u32) ^ + (Temp5 as u32) ^ (Temp6 as u32) ^ (Temp7 as u32) ^ (Temp8 as u32); + + if Result >= FXMAC_MAX_HASH_BITS { + Status = 1;//(FXMAC_ERR_INVALID_PARAM); + } else { + if Result < 32 { + HashAddr = read_reg((intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *const u32); + HashAddr &= !((1 << Result) as u32); + write_reg((intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *mut u32, HashAddr); + + } else { + HashAddr = read_reg((intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *const u32); + HashAddr &= !((1 << (Result - 32)) as u32); + write_reg((intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *mut u32, HashAddr); + } + Status = 0; + } + } + Status +} /// Set the Type ID match for this driver/device. The register is a 32-bit value. /// The device must be stopped before calling this function. -fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { - let status: u32 = 0; +fn FXmacSetTypeIdCheck(id_check: u32, index: u8) -> u32 { + let mut status: u32 = 0; assert!((index < FXMAC_MAX_TYPE_ID as u8), "index of Type ID exceed {}", FXMAC_MAX_TYPE_ID); let is_started = 0; /* Be sure device has been stopped */ if is_started == FT_COMPONENT_IS_STARTED { - status = FXMAC_ERR_MAC_IS_PROCESSING; + status = 9; //FXMAC_ERR_MAC_IS_PROCESSING error!("FXMAC is processing when calling FXmacSetTypeIdCheck function"); } else { /* Set the ID bits in MATCHx register */ - write_reg((FXMAC_IOBASE + FXMAC_MATCH1_OFFSET + (index * 4) as u32) as *mut u32, id_check); + write_reg((FXMAC_IOBASE + FXMAC_MATCH1_OFFSET + (index * 4) as u64) as *mut u32, id_check); status = FT_SUCCESS; } - status; + status } /// FXmacSelectClk @@ -969,11 +1269,11 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { fn FXmacSelectClk(instance_p: &mut FXmac) { let speed: u32 = instance_p.config.speed; - let FXMAC_WRITEREG32 = |base_address: u64, offset: u64, reg_value: u32| write_reg((base_address + offset) as *mut u32, reg_value); + let FXMAC_WRITEREG32 = |base_address: u64, offset: u32, reg_value: u32| write_reg((base_address + offset as u64) as *mut u32, reg_value); assert!((speed == FXMAC_SPEED_10) || (speed == FXMAC_SPEED_100) || (speed == FXMAC_SPEED_1000) || (speed == FXMAC_SPEED_2500) || (speed == FXMAC_SPEED_10000)); - if (instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_USXGMII) || (instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_XGMII) + if (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_USXGMII) || (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_XGMII) { if speed == FXMAC_SPEED_10000 { @@ -990,7 +1290,7 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0); /*0x1c10*/ } } - else if instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_5GBASER + else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_5GBASER { if speed == FXMAC_SPEED_5000 { @@ -1000,7 +1300,7 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x0); /*0x1c10*/ } } - else if instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_2500BASEX + else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_2500BASEX { if speed == FXMAC_SPEED_25000 { @@ -1020,7 +1320,7 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ } } - else if instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_SGMII + else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII { info!("FXMAC_PHY_INTERFACE_MODE_SGMII init"); if speed == FXMAC_SPEED_2500 @@ -1078,7 +1378,7 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x1); /*0x1c7c*/ } } - else if instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_RGMII + else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_RGMII { info!("FXMAC_PHY_INTERFACE_MODE_RGMII init"); if speed == FXMAC_SPEED_1000 @@ -1130,7 +1430,7 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL1, 0x0); /*0x1c84*/ } } - else if instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_RMII + else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_RMII { FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ } @@ -1140,8 +1440,8 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { fn FXmacHighSpeedConfiguration(instance_p: &mut FXmac, speed: u32) { - let reg_value: mut u32 = 0; - let set_speed: mut i32 = 0; + let mut reg_value: u32 = 0; + let mut set_speed: i32 = 0; match speed { FXMAC_SPEED_25000 => { @@ -1165,12 +1465,12 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { } /*GEM_HSMAC(0x0050) provide rate to the external*/ - reg_value = read_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC) as *const u32); + reg_value = read_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC as u64) as *const u32); reg_value &= !FXMAC_GEM_HSMACSPEED_MASK; - reg_value |= (set_speed) & FXMAC_GEM_HSMACSPEED_MASK; - write_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC) as *mut u32, reg_value); + reg_value |= (set_speed as u32) & FXMAC_GEM_HSMACSPEED_MASK; + write_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC as u64) as *mut u32, reg_value); - reg_value = read_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC) as *const u32); + reg_value = read_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC as u64) as *const u32); info!("FXMAC_GEM_HSMAC is {:#x}", reg_value); } @@ -1180,12 +1480,12 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { /// Initialize the MAC controller configuration based on the PHY interface type fn FXmacInitInterface(instance_p: &mut FXmac) { - let mut config: u32 = 0, + let mut config: u32 = 0; let mut control: u32 = 0; - info!("FXmacInitInterface, PHY MODE:{}", instance_p.config.interface); + info!("FXmacInitInterface, PHY MODE:{:?}", instance_p.config.interface); - if (instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_XGMII ) + if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_XGMII { config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); config &= !FXMAC_NWCFG_PCSSEL_MASK; @@ -1197,9 +1497,9 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { instance_p.config.duplex = 1; } - else if (instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_USXGMII || instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_5GBASER) + else if (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_USXGMII) || (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_5GBASER) { - info!("usx interface is {}",instance_p.config.interface); + info!("usx interface is {:?}",instance_p.config.interface); /* network_config */ instance_p.config.duplex = 1; config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); @@ -1259,7 +1559,7 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { write_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, control); } - else if(instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_2500BASEX) + else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_2500BASEX { /* network_config */ instance_p.config.duplex = 1; @@ -1299,14 +1599,14 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { write_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, control); } - else if (instance_p.config.interface == FXMAC_PHY_INTERFACE_MODE_SGMII) + else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII { config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); config |= FXMAC_NWCFG_PCSSEL_MASK | FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; config &= !(FXMAC_NWCFG_100_MASK | FXMAC_NWCFG_FDEN_MASK|FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK); - if instance_p->moudle_id >= 2 + if instance_p.moudle_id >= 2 { config &= !FXMAC_NWCFG_1000_MASK; } @@ -1357,7 +1657,7 @@ fn FXmacSetTypeIdCheck(u32 id_check: u32, index: u8) -> u32 { config &= !FXMAC_NWCFG_PCSSEL_MASK; config &= !(FXMAC_NWCFG_100_MASK | FXMAC_NWCFG_FDEN_MASK); - if instance_p->moudle_id >= 2 + if instance_p.moudle_id >= 2 { config &= !FXMAC_NWCFG_1000_MASK; } diff --git a/src/fxmac_const.rs b/src/fxmac_const.rs new file mode 100644 index 000000000..2b0d7c014 --- /dev/null +++ b/src/fxmac_const.rs @@ -0,0 +1,682 @@ +//////////////////// +/// fxmac_hw.h + +pub(crate) const FXMAC_RX_BUF_UNIT:u32 = 64; /* Number of receive buffer bytes as a unit, this is HW setup */ + +pub(crate) const FXMAC_MAX_RXBD:u32 = 128;/* Size of RX buffer descriptor queues */ +pub(crate) const FXMAC_MAX_TXBD:u32 = 128;/* Size of TX buffer descriptor queues */ + +pub(crate) const FXMAC_MAX_HASH_BITS:u32 = 64;/* Maximum value for hash bits. 2**6 */ + +/************************** Constant Definitions *****************************/ + +pub(crate) const FXMAC_MAX_MAC_ADDR:u32 = 4;/* Maxmum number of mac address supported */ +pub(crate) const FXMAC_MAX_TYPE_ID:u32 = 4; /* Maxmum number of type id supported */ + +/// for aarch64 +pub(crate) const FXMAC_BD_ALIGNMENT:u32 = 64;/* Minimum buffer descriptor alignment on the local bus */ + + +pub(crate) const FXMAC_RX_BUF_ALIGNMENT:u32 = 4;/* Minimum buffer alignment when using options that impose + alignment restrictions on the buffer data on the local bus */ + +pub(crate) const FXMAC_NWCTRL_OFFSET:u64 = 0x00000000; /* Network Control reg */ +pub(crate) const FXMAC_NWCFG_OFFSET:u64 = 0x00000004; /* Network Config reg */ +pub(crate) const FXMAC_NWSR_OFFSET:u64 = 0x00000008; /* Network Status reg */ +pub(crate) const FXMAC_DMACR_OFFSET:u64 = 0x00000010; /* DMA Control reg */ +pub(crate) const FXMAC_TXSR_OFFSET:u64 = 0x00000014; /* TX Status reg */ +pub(crate) const FXMAC_RXQBASE_OFFSET:u64 = 0x00000018;/* RX Q Base address reg */ +pub(crate) const FXMAC_TXQBASE_OFFSET:u64 = 0x0000001C;/* TX Q Base address reg */ +pub(crate) const FXMAC_RXSR_OFFSET:u64 = 0x00000020; /* RX Status reg */ + +pub(crate) const FXMAC_ISR_OFFSET:u64 = 0x00000024;/* Interrupt Status reg */ +pub(crate) const FXMAC_IER_OFFSET:u64 = 0x00000028;/* Interrupt Enable reg */ +pub(crate) const FXMAC_IDR_OFFSET:u64 = 0x0000002C;/* Interrupt Disable reg */ +pub(crate) const FXMAC_IMR_OFFSET:u64 = 0x00000030;/* Interrupt Mask reg */ + +pub(crate) const FXMAC_PHYMNTNC_OFFSET:u64 = 0x00000034;/* Phy Maintaince reg */ +pub(crate) const FXMAC_RXPAUSE_OFFSET:u64 = 0x00000038; /* RX Pause Time reg */ +pub(crate) const FXMAC_TXPAUSE_OFFSET:u64 = 0x0000003C; /* TX Pause Time reg */ + +pub(crate) const FXMAC_JUMBOMAXLEN_OFFSET:u64 = 0x00000048;/* Jumbo max length reg */ +pub(crate) const FXMAC_GEM_HSMAC:u32 = 0x0050; /* Hs mac config register*/ +pub(crate) const FXMAC_RXWATERMARK_OFFSET:u64 = 0x0000007C;/* RX watermark reg */ + +pub(crate) const FXMAC_HASHL_OFFSET:u64 = 0x00000080;/* Hash Low address reg */ +pub(crate) const FXMAC_HASHH_OFFSET:u64 = 0x00000084;/* Hash High address reg */ + +pub(crate) const FXMAC_GEM_SA1B:u32 = 0x0088; /* Specific1 Bottom */ +pub(crate) const FXMAC_GEM_SA1T:u32 = 0x008C; /* Specific1 Top */ +pub(crate) const FXMAC_GEM_SA2B:u32 = 0x0090; /* Specific2 Bottom */ +pub(crate) const FXMAC_GEM_SA2T:u32 = 0x0094; /* Specific2 Top */ +pub(crate) const FXMAC_GEM_SA3B:u32 = 0x0098; /* Specific3 Bottom */ +pub(crate) const FXMAC_GEM_SA3T:u32 = 0x009C; /* Specific3 Top */ +pub(crate) const FXMAC_GEM_SA4B:u32 = 0x00A0; /* Specific4 Bottom */ +pub(crate) const FXMAC_GEM_SA4T:u32 = 0x00A4; /* Specific4 Top */ + +pub(crate) const FXMAC_MATCH1_OFFSET:u64 = 0x000000A8;/* Type ID1 Match reg */ +pub(crate) const FXMAC_MATCH2_OFFSET:u64 = 0x000000AC;/* Type ID2 Match reg */ +pub(crate) const FXMAC_MATCH3_OFFSET:u64 = 0x000000B0;/* Type ID3 Match reg */ +pub(crate) const FXMAC_MATCH4_OFFSET:u64 = 0x000000B4;/* Type ID4 Match reg */ + +pub(crate) const FXMAC_STRETCH_OFFSET:u64 = 0x000000BC; /* IPG Stretch reg */ +pub(crate) const FXMAC_REVISION_REG_OFFSET:u64 = 0x000000FC;/* identification number and module revision */ + +pub(crate) const FXMAC_OCTTXL_OFFSET:u64 = 0x00000100;/* Octects transmitted Low reg */ +pub(crate) const FXMAC_OCTTXH_OFFSET:u64 = 0x00000104;/* Octects transmitted High reg */ + +pub(crate) const FXMAC_TXCNT_OFFSET:u64 = 0x00000108; /* Error-free Frmaes transmitted counter */ +pub(crate) const FXMAC_TXBCCNT_OFFSET:u64 = 0x0000010C; /* Error-free Broadcast Frames counter*/ +pub(crate) const FXMAC_TXMCCNT_OFFSET:u64 = 0x00000110; /* Error-free Multicast Frame counter */ +pub(crate) const FXMAC_TXPAUSECNT_OFFSET:u64 = 0x00000114;/* Pause Frames Transmitted Counter */ +pub(crate) const FXMAC_TX64CNT_OFFSET:u64 = 0x00000118; /* Error-free 64 byte Frames Transmitted counter */ +pub(crate) const FXMAC_TX65CNT_OFFSET:u64 = 0x0000011C; /* Error-free 65-127 byte Frames Transmitted counter */ +pub(crate) const FXMAC_TX128CNT_OFFSET:u64 = 0x00000120; /* Error-free 128-255 byte Frames Transmitted counter*/ +pub(crate) const FXMAC_TX256CNT_OFFSET:u64 = 0x00000124; /* Error-free 256-511 byte Frames transmitted counter */ +pub(crate) const FXMAC_TX512CNT_OFFSET:u64 = 0x00000128; /* Error-free 512-1023 byte Frames transmitted counter */ +pub(crate) const FXMAC_TX1024CNT_OFFSET:u64 = 0x0000012C; /* Error-free 1024-1518 byte Frames transmitted counter */ +pub(crate) const FXMAC_TX1519CNT_OFFSET:u64 = 0x00000130; /* Error-free larger than 1519 byte Frames transmitted counter */ +pub(crate) const FXMAC_TXURUNCNT_OFFSET:u64 = 0x00000134; /* TX under run error counter */ + +pub(crate) const FXMAC_SNGLCOLLCNT_OFFSET:u64 = 0x00000138; /* Single Collision Frame Counter */ +pub(crate) const FXMAC_MULTICOLLCNT_OFFSET:u64 = 0x0000013C; /* Multiple Collision Frame Counter */ +pub(crate) const FXMAC_EXCESSCOLLCNT_OFFSET:u64 = 0x00000140;/* Excessive Collision Frame Counter */ +pub(crate) const FXMAC_LATECOLLCNT_OFFSET:u64 = 0x00000144; /* Late Collision Frame Counter */ +pub(crate) const FXMAC_TXDEFERCNT_OFFSET:u64 = 0x00000148; /* Deferred Transmission Frame Counter */ +pub(crate) const FXMAC_TXCSENSECNT_OFFSET:u64 = 0x0000014C; /* Transmit Carrier Sense Error Counter */ + +pub(crate) const FXMAC_OCTRXL_OFFSET:u64 = 0x00000150;/* Octects Received register Low */ +pub(crate) const FXMAC_OCTRXH_OFFSET:u64 = 0x00000154;/* Octects Received register High */ + +pub(crate) const FXMAC_RXCNT_OFFSET:u64 = 0x00000158; /* Error-free Frames Received Counter */ +pub(crate) const FXMAC_RXBROADCNT_OFFSET:u64 = 0x0000015C; /* Error-free Broadcast Frames Received Counter */ +pub(crate) const FXMAC_RXMULTICNT_OFFSET:u64 = 0x00000160; /* Error-free Multicast Frames Received Counter */ +pub(crate) const FXMAC_RXPAUSECNT_OFFSET:u64 = 0x00000164; /* Pause Frames Received Counter */ +pub(crate) const FXMAC_RX64CNT_OFFSET:u64 = 0x00000168; /* Error-free 64 byte Frames Received Counter */ +pub(crate) const FXMAC_RX65CNT_OFFSET:u64 = 0x0000016C; /* Error-free 65-127 byte Frames Received Counter */ +pub(crate) const FXMAC_RX128CNT_OFFSET:u64 = 0x00000170; /* Error-free 128-255 byte Frames Received Counter */ +pub(crate) const FXMAC_RX256CNT_OFFSET:u64 = 0x00000174; /* Error-free 256-512 byte Frames Received Counter */ +pub(crate) const FXMAC_RX512CNT_OFFSET:u64 = 0x00000178; /* Error-free 512-1023 byte Frames Received Counter */ +pub(crate) const FXMAC_RX1024CNT_OFFSET:u64 = 0x0000017C; /* Error-free 1024-1518 byte Frames Received Counter */ +pub(crate) const FXMAC_RX1519CNT_OFFSET:u64 = 0x00000180; /* Error-free 1519-max byte Frames Received Counter */ +pub(crate) const FXMAC_RXUNDRCNT_OFFSET:u64 = 0x00000184; /* Undersize Frames Received Counter */ +pub(crate) const FXMAC_RXOVRCNT_OFFSET:u64 = 0x00000188; /* Oversize Frames Received Counter */ +pub(crate) const FXMAC_RXJABCNT_OFFSET:u64 = 0x0000018C; /* Jabbers Received Counter */ +pub(crate) const FXMAC_RXFCSCNT_OFFSET:u64 = 0x00000190; /* Frame Check Sequence Error Counter */ +pub(crate) const FXMAC_RXLENGTHCNT_OFFSET:u64 = 0x00000194;/* Length Field Error Counter */ +pub(crate) const FXMAC_RXSYMBCNT_OFFSET:u64 = 0x00000198; /* Symbol Error Counter */ +pub(crate) const FXMAC_RXALIGNCNT_OFFSET:u64 = 0x0000019C; /* Alignment Error Counter */ +pub(crate) const FXMAC_RXRESERRCNT_OFFSET:u64 = 0x000001A0;/* Receive Resource Error Counter */ +pub(crate) const FXMAC_RXORCNT_OFFSET:u64 = 0x000001A4; /* Receive Overrun Counter */ +pub(crate) const FXMAC_RXIPCCNT_OFFSET:u64 = 0x000001A8; /* IP header Checksum Error Counter */ +pub(crate) const FXMAC_RXTCPCCNT_OFFSET:u64 = 0x000001AC; /* TCP Checksum Error Counter */ +pub(crate) const FXMAC_RXUDPCCNT_OFFSET:u64 = 0x000001B0; /* UDP Checksum Error Counter */ +pub(crate) const FXMAC_LAST_OFFSET:u64 = 0x000001B4; /* Last statistic counter offset, for clearing */ + +pub(crate) const FXMAC_1588_SEC_OFFSET:u64 = 0x000001D0; /* 1588 second counter */ +pub(crate) const FXMAC_1588_NANOSEC_OFFSET:u64 = 0x000001D4; /* 1588 nanosecond counter */ +pub(crate) const FXMAC_1588_ADJ_OFFSET:u64 = 0x000001D8; /* 1588 nanosecond adjustment counter */ +pub(crate) const FXMAC_1588_INC_OFFSET:u64 = 0x000001DC; /* 1588 nanosecond increment counter */ +pub(crate) const FXMAC_PTP_TXSEC_OFFSET:u64 = 0x000001E0; /* 1588 PTP transmit second counter */ +pub(crate) const FXMAC_PTP_TXNANOSEC_OFFSET:u64 = 0x000001E4; /* 1588 PTP transmit nanosecond counter */ +pub(crate) const FXMAC_PTP_RXSEC_OFFSET:u64 = 0x000001E8; /* 1588 PTP receive second counter */ +pub(crate) const FXMAC_PTP_RXNANOSEC_OFFSET:u64 = 0x000001EC; /* 1588 PTP receive nanosecond counter */ +pub(crate) const FXMAC_PTPP_TXSEC_OFFSET:u64 = 0x000001F0; /* 1588 PTP peer transmit second counter */ +pub(crate) const FXMAC_PTPP_TXNANOSEC_OFFSET:u64 = 0x000001F4;/* 1588 PTP peer transmit nanosecond counter */ +pub(crate) const FXMAC_PTPP_RXSEC_OFFSET:u64 = 0x000001F8; /* 1588 PTP peer receive second counter */ +pub(crate) const FXMAC_PTPP_RXNANOSEC_OFFSET:u64 = 0x000001FC;/* 1588 PTP peer receive nanosecond counter */ + +pub(crate) const FXMAC_PCS_CONTROL_OFFSET:u64 = 0x00000200;/* All PCS registers */ + +pub(crate) const FXMAC_PCS_STATUS_OFFSET:u64 = 0x00000204;/* All PCS status */ + +pub(crate) const FXMAC_PCS_AN_LP_OFFSET:u64 = 0x00000214; /* All PCS link partner's base page */ + +pub(crate) const FXMAC_DESIGNCFG_DEBUG1_OFFSET:u64 = 0x00000280;/* Design Configuration Register 1 */ + +pub(crate) const FXMAC_DESIGNCFG_DEBUG2_OFFSET:u64 = 0x00000284;/* Design Configuration Register 2 */ + +pub(crate) const FXMAC_INTQ1_STS_OFFSET:u64 = 0x00000400;/* Interrupt Q1 Status reg */ + +pub(crate) const FXMAC_TXQ1BASE_OFFSET:u64 = 0x00000440;/* TX Q1 Base address reg */ +pub(crate) const FXMAC_RXQ1BASE_OFFSET:u64 = 0x00000480;/* RX Q1 Base address reg */ + +pub(crate) const FXMAC_RXBUFQ1_SIZE_OFFSET:u64 = 0x000004a0;/* Receive Buffer Size */ +// FXMAC_RXBUFQX_SIZE_OFFSET(x) (FXMAC_RXBUFQ1_SIZE_OFFSET + (x << 2)) +pub const fn FXMAC_RXBUFQX_SIZE_OFFSET(value: u64) -> u64 { + FXMAC_RXBUFQ1_SIZE_OFFSET + (value << 2) +} +pub(crate) const FXMAC_RXBUFQX_SIZE_MASK:u32 = GENMASK(7, 0); + +pub(crate) const FXMAC_MSBBUF_TXQBASE_OFFSET:u64 = 0x000004C8;/* MSB Buffer TX Q Base reg */ +pub(crate) const FXMAC_MSBBUF_RXQBASE_OFFSET:u64 = 0x000004D4;/* MSB Buffer RX Q Base reg */ +pub(crate) const FXMAC_TXQSEGALLOC_QLOWER_OFFSET:u64 = 0x000005A0;/* Transmit SRAM segment distribution */ +pub(crate) const FXMAC_INTQ1_IER_OFFSET:u64 = 0x00000600; /* Interrupt Q1 Enable reg */ +pub const fn FXMAC_INTQX_IER_SIZE_OFFSET(x: u64) -> u64 { + FXMAC_INTQ1_IER_OFFSET + (x << 2) +} + +pub(crate) const FXMAC_INTQ1_IDR_OFFSET:u64 = 0x00000620;/* Interrupt Q1 Disable reg */ +pub const fn FXMAC_INTQX_IDR_SIZE_OFFSET(x: u64) -> u64 { + FXMAC_INTQ1_IDR_OFFSET + (x << 2) +} + +pub(crate) const FXMAC_INTQ1_IMR_OFFSET:u64 = 0x00000640;/* Interrupt Q1 Mask reg */ + +pub(crate) const FXMAC_GEM_USX_CONTROL_OFFSET:u64 = 0x0A80; /* High speed PCS control register */ +pub(crate) const FXMAC_TEST_CONTROL_OFFSET:u64 = 0x0A84; /* USXGMII Test Control Register */ +pub(crate) const FXMAC_GEM_USX_STATUS_OFFSET:u64 = 0x0A88; /* USXGMII Status Register */ + +pub(crate) const FXMAC_GEM_SRC_SEL_LN:u32 = 0x1C04; +pub(crate) const FXMAC_GEM_DIV_SEL0_LN:u32 = 0x1C08; +pub(crate) const FXMAC_GEM_DIV_SEL1_LN:u32 = 0x1C0C; +pub(crate) const FXMAC_GEM_PMA_XCVR_POWER_STATE:u32 = 0x1C10; +pub(crate) const FXMAC_GEM_SPEED_MODE:u32 = 0x1C14; +pub(crate) const FXMAC_GEM_MII_SELECT:u32 = 0x1C18; +pub(crate) const FXMAC_GEM_SEL_MII_ON_RGMII:u32 = 0x1C1C; +pub(crate) const FXMAC_GEM_TX_CLK_SEL0:u32 = 0x1C20; +pub(crate) const FXMAC_GEM_TX_CLK_SEL1:u32 = 0x1C24; +pub(crate) const FXMAC_GEM_TX_CLK_SEL2:u32 = 0x1C28; +pub(crate) const FXMAC_GEM_TX_CLK_SEL3:u32 = 0x1C2C; +pub(crate) const FXMAC_GEM_RX_CLK_SEL0:u32 = 0x1C30; +pub(crate) const FXMAC_GEM_RX_CLK_SEL1:u32 = 0x1C34; +pub(crate) const FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL:u32 = 0x1C38; +pub(crate) const FXMAC_GEM_TX_CLK_SEL5:u32 = 0x1C3C; +pub(crate) const FXMAC_GEM_TX_CLK_SEL6:u32 = 0x1C40; +pub(crate) const FXMAC_GEM_RX_CLK_SEL4:u32 = 0x1C44; +pub(crate) const FXMAC_GEM_RX_CLK_SEL5:u32 = 0x1C48; +pub(crate) const FXMAC_GEM_TX_CLK_SEL3_0:u32 = 0x1C70; +pub(crate) const FXMAC_GEM_TX_CLK_SEL4_0:u32 = 0x1C74; +pub(crate) const FXMAC_GEM_RX_CLK_SEL3_0:u32 = 0x1C78; +pub(crate) const FXMAC_GEM_RX_CLK_SEL4_0:u32 = 0x1C7C; +pub(crate) const FXMAC_GEM_RGMII_TX_CLK_SEL0:u32 = 0x1C80; +pub(crate) const FXMAC_GEM_RGMII_TX_CLK_SEL1:u32 = 0x1C84; +pub(crate) const FXMAC_GEM_MODE_SEL_OFFSET:u64 = 0xDC00; +pub(crate) const FXMAC_LOOPBACK_SEL_OFFSET:u64 = 0xDC04; + +pub(crate) const FXMAC_TAIL_ENABLE:u64 = 0xe7c; /*Enable tail Register*/ +// FXMAC_TAIL_QUEUE(queue) (0x0e80 + ((queue) << 2)) + + +/** + * @name interrupts bit definitions + * Bits definitions are same in FXMAC_ISR_OFFSET, + * FXMAC_IER_OFFSET, FXMAC_IDR_OFFSET, and FXMAC_IMR_OFFSET + * @{ + */ +pub(crate) const FXMAC_IXR_PTPPSTX_MASK:u32 = BIT(25); /* PTP Pdelay_resp TXed */ +pub(crate) const FXMAC_IXR_PTPPDRTX_MASK:u32 = BIT(24); /* PTP Pdelay_req TXed */ +pub(crate) const FXMAC_IXR_PTPPSRX_MASK:u32 = BIT(23); /* PTP Pdelay_resp RXed */ +pub(crate) const FXMAC_IXR_PTPPDRRX_MASK:u32 = BIT(22); /* PTP Pdelay_req RXed */ + +pub(crate) const FXMAC_IXR_PTPSTX_MASK:u32 = BIT(21); /* PTP Sync TXed */ +pub(crate) const FXMAC_IXR_PTPDRTX_MASK:u32 = BIT(20); /* PTP Delay_req TXed */ +pub(crate) const FXMAC_IXR_PTPSRX_MASK:u32 = BIT(19); /* PTP Sync RXed */ +pub(crate) const FXMAC_IXR_PTPDRRX_MASK:u32 = BIT(18); /* PTP Delay_req RXed */ + +pub(crate) const FXMAC_IXR_PAUSETX_MASK:u32 = BIT(14); /* Pause frame transmitted */ +pub(crate) const FXMAC_IXR_PAUSEZERO_MASK:u32 = BIT(13); /* Pause time has reached zero */ +pub(crate) const FXMAC_IXR_PAUSENZERO_MASK:u32 = BIT(12); /* Pause frame received */ +pub(crate) const FXMAC_IXR_HRESPNOK_MASK:u32 = BIT(11); /* hresp not ok */ +pub(crate) const FXMAC_IXR_RXOVR_MASK:u32 = BIT(10); /* Receive overrun occurred */ +pub(crate) const FXMAC_IXR_LINKCHANGE_MASK:u32 = BIT(9); /* link status change */ +pub(crate) const FXMAC_IXR_TXCOMPL_MASK:u32 = BIT(7); /* Frame transmitted ok */ +pub(crate) const FXMAC_IXR_TXEXH_MASK:u32 = BIT(6); /* Transmit err occurred or no buffers*/ +pub(crate) const FXMAC_IXR_RETRY_MASK:u32 = BIT(5); /* Retry limit exceeded */ +pub(crate) const FXMAC_IXR_URUN_MASK:u32 = BIT(4); /* Transmit underrun */ +pub(crate) const FXMAC_IXR_TXUSED_MASK:u32 = BIT(3); /* Tx buffer used bit read */ +pub(crate) const FXMAC_IXR_RXUSED_MASK:u32 = BIT(2); /* Rx buffer used bit read */ +pub(crate) const FXMAC_IXR_RXCOMPL_MASK:u32 = BIT(1); /* Frame received ok */ +pub(crate) const FXMAC_IXR_MGMNT_MASK:u32 = BIT(0); /* PHY management complete */ +pub(crate) const FXMAC_IXR_ALL_MASK:u32 = GENMASK(31, 0); /* Everything! */ + +pub(crate) const FXMAC_IXR_TX_ERR_MASK:u32 = (FXMAC_IXR_TXEXH_MASK | FXMAC_IXR_RETRY_MASK | FXMAC_IXR_URUN_MASK); + +pub(crate) const FXMAC_IXR_RX_ERR_MASK:u32 = (FXMAC_IXR_HRESPNOK_MASK | FXMAC_IXR_RXUSED_MASK | FXMAC_IXR_RXOVR_MASK); + +pub(crate) const FXMAC_INTR_MASK:u32 = (FXMAC_IXR_LINKCHANGE_MASK | FXMAC_IXR_TX_ERR_MASK | FXMAC_IXR_RX_ERR_MASK | FXMAC_IXR_RXCOMPL_MASK | FXMAC_IXR_TXCOMPL_MASK); + +/** @name network control register bit definitions + * @{ + */ +pub(crate) const FXMAC_NWCTRL_ENABLE_HS_MAC_MASK:u32 = BIT(31); + +pub(crate) const FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK:u32 = BIT(29); /* 2.5G operation selected */ + +pub(crate) const FXMAC_NWCTRL_FLUSH_DPRAM_MASK:u32 = BIT(18); /* Flush a packet from Rx SRAM */ +pub(crate) const FXMAC_NWCTRL_ZEROPAUSETX_MASK:u32 = BIT(11); /* Transmit zero quantum pause frame */ +pub(crate) const FXMAC_NWCTRL_PAUSETX_MASK:u32 = BIT(11); /* Transmit pause frame */ +pub(crate) const FXMAC_NWCTRL_HALTTX_MASK:u32 = BIT(10); /* Halt transmission after current frame */ +pub(crate) const FXMAC_NWCTRL_STARTTX_MASK:u32 = BIT(9); /* Start tx (tx_go) */ + +pub(crate) const FXMAC_NWCTRL_STATWEN_MASK:u32 = BIT(7); /* Enable writing to stat counters */ +pub(crate) const FXMAC_NWCTRL_STATINC_MASK:u32 = BIT(6); /* Increment statistic registers */ +pub(crate) const FXMAC_NWCTRL_STATCLR_MASK:u32 = BIT(5); /* Clear statistic registers */ +pub(crate) const FXMAC_NWCTRL_MDEN_MASK:u32 = BIT(4); /* Enable MDIO port */ +pub(crate) const FXMAC_NWCTRL_TXEN_MASK:u32 = BIT(3); /* Enable transmit */ +pub(crate) const FXMAC_NWCTRL_RXEN_MASK:u32 = BIT(2); /* Enable receive */ +pub(crate) const FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK:u32 = BIT(1); /* Loopback local */ + + + +/** @name network configuration register bit definitions FXMAC_NWCFG_OFFSET + * @{ + */ +pub(crate) const FXMAC_NWCFG_BADPREAMBEN_MASK:u32 = BIT(29); /* disable rejection of non-standard preamble */ +pub(crate) const FXMAC_NWCFG_IPDSTRETCH_MASK:u32 = BIT(28); /* enable transmit IPG */ +pub(crate) const FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK:u32 = BIT(27); /* SGMII mode enable */ +pub(crate) const FXMAC_NWCFG_FCSIGNORE_MASK:u32 = BIT(26); /* disable rejection of FCS error */ +pub(crate) const FXMAC_NWCFG_HDRXEN_MASK:u32 = BIT(25); /* RX half duplex */ +pub(crate) const FXMAC_NWCFG_RXCHKSUMEN_MASK:u32 = BIT(24); /* enable RX checksum offload */ +pub(crate) const FXMAC_NWCFG_PAUSECOPYDI_MASK:u32 = BIT(23); /* Do not copy pause Frames to memory */ + +pub(crate) const FXMAC_NWCFG_DWIDTH_64_MASK:u32 = BIT(21); /* 64 bit Data bus width */ +pub(crate) const FXMAC_NWCFG_BUS_WIDTH_32_MASK:u32 = (0 << 21); +pub(crate) const FXMAC_NWCFG_BUS_WIDTH_64_MASK:u32 = (1 << 21); +pub(crate) const FXMAC_NWCFG_BUS_WIDTH_128_MASK:u32 = (2 << 21); + +pub(crate) const FXMAC_NWCFG_CLOCK_DIV224_MASK:u32 = (7 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV128_MASK:u32 = (6 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV96_MASK:u32 = (5 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV64_MASK:u32 = (4 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV48_MASK:u32 = (3 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV32_MASK:u32 = (2 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV16_MASK:u32 = (1 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV8_MASK:u32 = (0 << 18); +pub(crate) const FXMAC_NWCFG_RESET_MASK:u32 = BIT(19); /* reset value of mdc_clock_division*/ +pub(crate) const FXMAC_NWCFG_MDC_SHIFT_MASK:u32 = 18; /* shift bits for MDC */ +pub(crate) const FXMAC_NWCFG_MDCCLKDIV_MASK:u32 = GENMASK(20, 18); /* MDC Mask PCLK divisor */ + +pub(crate) const FXMAC_NWCFG_FCS_REMOVE_MASK:u32 = BIT(17); /* FCS remove - setting this bit will cause received frames to be written to memory without their frame check sequence (last 4 bytes). */ +pub(crate) const FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK:u32 = BIT(16); /* RX length error discard */ +// FXMAC_NWCFG_RXOFFS_MASK:u32 = GENMASK(15); /* RX buffer offset */ +pub(crate) const FXMAC_NWCFG_PAUSE_ENABLE_MASK:u32 = BIT(13); /* Pause enable - when set, transmission will pause if a non-zero 802.3 classic pause frame is received and PFC has not been negotiated. */ +pub(crate) const FXMAC_NWCFG_RETRYTESTEN_MASK:u32 = BIT(12); /* Retry test */ +pub(crate) const FXMAC_NWCFG_PCSSEL_MASK:u32 = BIT(11); /* PCS Select */ +pub(crate) const FXMAC_NWCFG_1000_MASK:u32 = BIT(10); /* Gigabit mode enable */ +pub(crate) const FXMAC_NWCFG_XTADDMACHEN_MASK:u32 = BIT(9); /* External address match enable */ +pub(crate) const FXMAC_NWCFG_1536RXEN_MASK:u32 = BIT(8); /* Enable 1536 byte frames reception */ +pub(crate) const FXMAC_NWCFG_UCASTHASHEN_MASK:u32 = BIT(7); /* Receive unicast hash frames */ +pub(crate) const FXMAC_NWCFG_MCASTHASHEN_MASK:u32 = BIT(6); /* Receive multicast hash frames */ +pub(crate) const FXMAC_NWCFG_BCASTDI_MASK:u32 = BIT(5); /* Do not receive broadcast frames */ +pub(crate) const FXMAC_NWCFG_COPYALLEN_MASK:u32 = BIT(4); /* Copy all frames */ +pub(crate) const FXMAC_NWCFG_JUMBO_MASK:u32 = BIT(3); /* Jumbo frames */ +pub(crate) const FXMAC_NWCFG_NVLANDISC_MASK:u32 = BIT(2); /* Receive only VLAN frames */ +pub(crate) const FXMAC_NWCFG_FDEN_MASK:u32 = BIT(1); /* full duplex */ +pub(crate) const FXMAC_NWCFG_100_MASK:u32 = BIT(0); /* 100 Mbps */ + + +/* Receive buffer descriptor status words bit positions. + * Receive buffer descriptor consists of two 32-bit registers, + * the first - word0 contains a 32-bit word aligned address pointing to the + * address of the buffer. The lower two bits make up the wrap bit indicating + * the last descriptor and the ownership bit to indicate it has been used by + * the xmac. + * The following register - word1, contains status information regarding why + * the frame was received (the filter match condition) as well as other + * useful info. + * @{ + */ +pub(crate) const FXMAC_RXBUF_BCAST_MASK:u32 = BIT(31); /* Broadcast frame */ +pub(crate) const FXMAC_RXBUF_HASH_MASK:u32 = GENMASK(30, 29); +pub(crate) const FXMAC_RXBUF_MULTIHASH_MASK:u32 = BIT(30); /* Multicast hashed frame */ +pub(crate) const FXMAC_RXBUF_UNIHASH_MASK:u32 = BIT(29); /* Unicast hashed frame */ +pub(crate) const FXMAC_RXBUF_EXH_MASK:u32 = BIT(27); /* buffer exhausted */ +pub(crate) const FXMAC_RXBUF_AMATCH_MASK:u32 = GENMASK(26, 25); /* Specific address \ +matched */ +pub(crate) const FXMAC_RXBUF_IDFOUND_MASK:u32 = BIT(24); /* Type ID matched */ +pub(crate) const FXMAC_RXBUF_IDMATCH_MASK:u32 = GENMASK(23, 22); /* ID matched mask */ +pub(crate) const FXMAC_RXBUF_VLAN_MASK:u32 = BIT(21); /* VLAN tagged */ +pub(crate) const FXMAC_RXBUF_PRI_MASK:u32 = BIT(20); /* Priority tagged */ +pub(crate) const FXMAC_RXBUF_VPRI_MASK:u32 = GENMASK(19, 17); /* Vlan priority */ +pub(crate) const FXMAC_RXBUF_CFI_MASK:u32 = BIT(16); /* CFI frame */ +pub(crate) const FXMAC_RXBUF_EOF_MASK:u32 = BIT(15); /* End of frame. */ +pub(crate) const FXMAC_RXBUF_SOF_MASK:u32 = BIT(14); /* Start of frame. */ +pub(crate) const FXMAC_RXBUF_FCS_STATUS_MASK:u32 = BIT(13); /* Status of fcs. */ +pub(crate) const FXMAC_RXBUF_LEN_MASK:u32 = GENMASK(12, 0); /* Mask for length field */ +pub(crate) const FXMAC_RXBUF_LEN_JUMBO_MASK:u32 = GENMASK(13, 0); /* Mask for jumbo length */ + +pub(crate) const FXMAC_RXBUF_WRAP_MASK:u32 = BIT(1); /* Wrap bit, last BD */ +pub(crate) const FXMAC_RXBUF_NEW_MASK:u32 = BIT(0); /* Used bit.. */ +pub(crate) const FXMAC_RXBUF_ADD_MASK:u32 = GENMASK(31, 2); /* Mask for address */ + +/* + * @} + */ + +/* Transmit buffer descriptor status words bit positions. + * Transmit buffer descriptor consists of two 32-bit registers, + * the first - word0 contains a 32-bit address pointing to the location of + * the transmit data. + * The following register - word1, consists of various information to control + * the xmac transmit process. After transmit, this is updated with status + * information, whether the frame was transmitted OK or why it had failed. + * @{ + */ +pub(crate) const FXMAC_TXBUF_USED_MASK:u32 = BIT(31); /* Used bit. */ +pub(crate) const FXMAC_TXBUF_WRAP_MASK:u32 = BIT(30); /* Wrap bit, last descriptor */ +pub(crate) const FXMAC_TXBUF_RETRY_MASK:u32 = BIT(29); /* Retry limit exceeded */ +pub(crate) const FXMAC_TXBUF_URUN_MASK:u32 = BIT(28); /* Transmit underrun occurred */ +pub(crate) const FXMAC_TXBUF_EXH_MASK:u32 = BIT(27); /* Buffers exhausted */ +pub(crate) const FXMAC_TXBUF_TCP_MASK:u32 = BIT(26); /* Late collision. */ +pub(crate) const FXMAC_TXBUF_NOCRC_MASK:u32 = BIT(16); /* No CRC */ +pub(crate) const FXMAC_TXBUF_LAST_MASK:u32 = BIT(15); /* Last buffer */ +pub(crate) const FXMAC_TXBUF_LEN_MASK:u32 = GENMASK(13, 0); /* Mask for length field */ +/* + * @} + */ + + +/** + * @name receive status register bit definitions + * @{ + */ +pub(crate) const FXMAC_RXSR_HRESPNOK_MASK:u32 = BIT(3); /* Receive hresp not OK */ +pub(crate) const FXMAC_RXSR_RXOVR_MASK:u32 = BIT(2); /* Receive overrun */ +pub(crate) const FXMAC_RXSR_FRAMERX_MASK:u32 = BIT(1); /* Frame received OK */ +pub(crate) const FXMAC_RXSR_BUFFNA_MASK:u32 = BIT(0); /* RX buffer used bit set */ + +pub(crate) const FXMAC_RXSR_ERROR_MASK:u32 = (FXMAC_RXSR_HRESPNOK_MASK | FXMAC_RXSR_RXOVR_MASK | FXMAC_RXSR_BUFFNA_MASK); + +pub(crate) const FXMAC_SR_ALL_MASK:u32 = GENMASK(31, 0); /* Mask for full register */ + +/** @name DMA control register bit definitions + * @{ + */ +pub(crate) const FXMAC_DMACR_ADDR_WIDTH_64:u32 = BIT(30); /* 64 bit address bus */ +pub(crate) const FXMAC_DMACR_TXEXTEND_MASK:u32 = BIT(29); /* Tx Extended desc mode */ +pub(crate) const FXMAC_DMACR_RXEXTEND_MASK:u32 = BIT(28); /* Rx Extended desc mode */ +pub(crate) const FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK:u32 = BIT(24); /* Auto Discard RX frames during lack of resource. */ +pub(crate) const FXMAC_DMACR_RXBUF_MASK:u32 = GENMASK(23, 16); /* Mask bit for RX buffer size */ +pub(crate) const FXMAC_DMACR_RXBUF_SHIFT:u32 = 16; /* Shift bit for RX buffer size */ +pub(crate) const FXMAC_DMACR_TCPCKSUM_MASK:u32 = BIT(11); /* enable/disable TX checksum offload */ +pub(crate) const FXMAC_DMACR_TXSIZE_MASK:u32 = BIT(10); /* TX buffer memory size bit[10] */ +pub(crate) const FXMAC_DMACR_RXSIZE_MASK:u32 = GENMASK(9, 8); /* RX buffer memory size bit[9:8] */ +pub(crate) const FXMAC_DMACR_ENDIAN_MASK:u32 = BIT(7); /* endian configuration */ +pub(crate) const FXMAC_DMACR_SWAP_MANAGEMENT_MASK:u32 = BIT(6); /* When clear, selects little endian mode */ +pub(crate) const FXMAC_DMACR_BLENGTH_MASK:u32 = GENMASK(4, 0); /* buffer burst length */ +pub(crate) const FXMAC_DMACR_SINGLE_AHB_AXI_BURST:u32 = BIT(0); /* single AHB_AXI bursts */ +pub(crate) const FXMAC_DMACR_INCR4_AHB_AXI_BURST:u32 = BIT(2); /* 4 bytes AHB_AXI bursts */ +pub(crate) const FXMAC_DMACR_INCR8_AHB_AXI_BURST:u32 = BIT(3); /* 8 bytes AHB_AXI bursts */ +pub(crate) const FXMAC_DMACR_INCR16_AHB_AXI_BURST:u32 = BIT(4); /* 16 bytes AHB_AXI bursts */ + +/* This register indicates module identification number and module revision. */ + +pub(crate) const FXMAC_REVISION_MODULE_MASK:u32 = GENMASK(15, 0); /* Module revision */ +pub(crate) const FXMAC_IDENTIFICATION_MASK:u32 = GENMASK(27, 16); /* Module identification number */ +pub(crate) const FXMAC_FIX_NUM_MASK:u32 = GENMASK(31, 28); /* Fix number - incremented for fix releases */ + +/** @name network status register bit definitaions + * @{ + */ +pub(crate) const FXMAC_NWSR_MDIOIDLE_MASK:u32 = BIT(2); /* PHY management idle */ +pub(crate) const FXMAC_NWSR_MDIO_MASK:u32 = BIT(1); /* Status of mdio_in */ +pub(crate) const FXMAC_NWSR_PCS_LINK_STATE_MASK:u32 = BIT(0); + +/** @name PHY Maintenance bit definitions + * @{ + */ +pub(crate) const FXMAC_PHYMNTNC_OP_MASK:u32 = (BIT(17) | BIT(30)); /* operation mask bits */ +pub(crate) const FXMAC_PHYMNTNC_OP_R_MASK:u32 = BIT(29); /* read operation */ +pub(crate) const FXMAC_PHYMNTNC_OP_W_MASK:u32 = BIT(28); /* write operation */ +pub(crate) const FXMAC_PHYMNTNC_ADDR_MASK:u32 = GENMASK(27, 23); /* Address bits */ +pub(crate) const FXMAC_PHYMNTNC_REG_MASK:u32 = GENMASK(22, 18); /* register bits */ +pub(crate) const FXMAC_PHYMNTNC_DATA_MASK:u32 = GENMASK(11, 0); /* data bits */ +pub(crate) const FXMAC_PHYMNTNC_PHAD_SHFT_MSK:u32 = 23; /* Shift bits for PHYAD */ +pub(crate) const FXMAC_PHYMNTNC_PREG_SHFT_MSK:u32 = 18; /* Shift bits for PHREG */ + +/** @name transmit status register bit definitions + * @{ + */ +pub(crate) const FXMAC_TXSR_HRESPNOK_MASK:u32 = BIT(8); /* Transmit hresp not OK */ +pub(crate) const FXMAC_TXSR_URUN_MASK:u32 = BIT(6); /* Transmit underrun */ +pub(crate) const FXMAC_TXSR_TXCOMPL_MASK:u32 = BIT(5); /* Transmit completed OK */ +pub(crate) const FXMAC_TXSR_BUFEXH_MASK:u32 = BIT(4); /* Transmit buffs exhausted mid frame */ +pub(crate) const FXMAC_TXSR_TXGO_MASK:u32 = BIT(3); /* Status of go flag */ +pub(crate) const FXMAC_TXSR_RXOVR_MASK:u32 = BIT(2); /* Retry limit exceeded */ +pub(crate) const FXMAC_TXSR_FRAMERX_MASK:u32 = BIT(1); /* Collision tx frame */ +pub(crate) const FXMAC_TXSR_USEDREAD_MASK:u32 = BIT(0); /* TX buffer used bit set */ + +pub(crate) const FXMAC_TXSR_ERROR_MASK:u32 = (FXMAC_TXSR_HRESPNOK_MASK | + FXMAC_TXSR_URUN_MASK | + FXMAC_TXSR_BUFEXH_MASK | + FXMAC_TXSR_RXOVR_MASK | + FXMAC_TXSR_FRAMERX_MASK | + FXMAC_TXSR_USEDREAD_MASK); +/** @name transmit SRAM segment allocation by queue 0 to 7 register bit definitions + * @{ + */ +pub(crate) const FXMAC_TXQSEGALLOC_QLOWER_JUMBO_MASK:u32 = BIT(2); /* 16 segments are distributed to queue 0*/ +/** + * @name Interrupt Q1 status register bit definitions + * @{ + */ +pub(crate) const FXMAC_INTQ1SR_TXCOMPL_MASK:u32 = BIT(7); /* Transmit completed OK */ +pub(crate) const FXMAC_INTQ1SR_TXERR_MASK:u32 = BIT(6); /* Transmit AMBA Error */ + +pub(crate) const FXMAC_INTQ1_IXR_ALL_MASK:u32 = (FXMAC_INTQ1SR_TXCOMPL_MASK | FXMAC_INTQ1SR_TXERR_MASK); + +/** + * @name Interrupt QUEUE status register bit definitions + * @{ + */ +pub(crate) const FXMAC_INTQUESR_TXCOMPL_MASK:u32 = BIT(7); /* Transmit completed OK */ +pub(crate) const FXMAC_INTQUESR_TXERR_MASK:u32 = BIT(6); /* Transmit AMBA Error */ +pub(crate) const FXMAC_INTQUESR_RCOMP_MASK:u32 = BIT(1); +pub(crate) const FXMAC_INTQUESR_RXUBR_MASK:u32 = BIT(2); + +pub(crate) const FXMAC_INTQUE_IXR_ALL_MASK:u32 = (FXMAC_INTQUESR_TXCOMPL_MASK | FXMAC_INTQUESR_TXERR_MASK); + +pub const fn FXMAC_QUEUE_REGISTER_OFFSET(base_addr: u64, queue_id: u32) -> u64 { + base_addr + (queue_id as u64 - 1) * 4 +} + +/* Design Configuration Register 1 - The GEM has many parameterisation options to configure the IP during compilation stage. */ + +pub(crate) const FXMAC_DESIGNCFG_DEBUG1_BUS_WIDTH_MASK:u32 = GENMASK(27, 25); +pub(crate) const FXMAC_DESIGNCFG_DEBUG1_BUS_IRQCOR_MASK:u32 = BIT(23); + +/*GEM hs mac config register bitfields*/ +pub(crate) const FXMAC_GEM_HSMACSPEED_OFFSET:u64 = 0; +pub(crate) const FXMAC_GEM_HSMACSPEED_SIZE:u32 = 3; +pub(crate) const FXMAC_GEM_HSMACSPEED_MASK:u32 = 0x7; + +/* Transmit buffer descriptor status words offset + * @{ + */ +pub(crate) const FXMAC_BD_ADDR_OFFSET:u64 = 0x00000000;/* word 0/addr of BDs */ +pub(crate) const FXMAC_BD_STAT_OFFSET:u64 = 4; /* word 1/status of BDs, 4 bytes */ +pub(crate) const FXMAC_BD_ADDR_HI_OFFSET:u32 = BIT(3); /* word 2/addr of BDs */ + +/** @name MAC address register word 1 mask + * @{ + */ +pub(crate) const FXMAC_GEM_SAB_MASK:u32 = GENMASK(15, 0); /* Address bits[47:32] bit[31:0] are in BOTTOM */ + +/* USXGMII control register FXMAC_GEM_USX_CONTROL_OFFSET */ +pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_100M:u32 = (0x0 << 14); /* 100M operation */ +pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_1G:u32 = (0x1 << 14); /* 1G operation */ +pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_2_5G:u32 = (0x2 << 14); /* 2.5G operation */ +pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_5G:u32 = (0x3 << 14); /* 5G operation */ +pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_10G:u32 = (0x4 << 14); /* 10G operation */ +pub(crate) const FXMAC_GEM_USX_SERDES_RATE_5G:u32 = (0x0 << 12); +pub(crate) const FXMAC_GEM_USX_SERDES_RATE_10G:u32 = (0x1 << 12); +pub(crate) const FXMAC_GEM_USX_TX_SCR_BYPASS:u32 = BIT(8); /* RX Scrambler Bypass. Set high to bypass the receive descrambler. */ +pub(crate) const FXMAC_GEM_USX_RX_SCR_BYPASS:u32 = BIT(9); /* TX Scrambler Bypass. Set high to bypass the transmit scrambler. */ +pub(crate) const FXMAC_GEM_USX_RX_SYNC_RESET:u32 = BIT(2); /* RX Reset. Set high to reset the receive datapath. When low the receive datapath is enabled. */ +pub(crate) const FXMAC_GEM_USX_TX_DATAPATH_EN:u32 = BIT(1); /* TX Datapath Enable. */ +pub(crate) const FXMAC_GEM_USX_SIGNAL_OK:u32 = BIT(0); /* Enable the USXGMII/BASE-R receive PCS. */ + +/* All PCS registers */ +pub(crate) const FXMAC_PCS_CONTROL_ENABLE_AUTO_NEG:u32 = BIT(12); /* Enable auto-negotiation - when set active high, autonegotiation operation is enabled. */ + + +/* FXMAC_PCS_STATUS_OFFSET */ +pub(crate) const FXMAC_PCS_STATUS_LINK_STATUS_OFFSET:u32 = 2; +pub(crate) const FXMAC_PCS_STATUS_LINK_STATUS:u32 = BIT(FXMAC_PCS_STATUS_LINK_STATUS_OFFSET); /* Link status - indicates the status of the physical connection to the link partner. When set to logic 1 the link is up, and when set to logic 0, the link is down. */ + +/* FXMAC_PCS_AN_LP_OFFSET */ +pub(crate) const FXMAC_PCS_AN_LP_SPEED_OFFSET:u64 = 10; +pub(crate) const FXMAC_PCS_AN_LP_SPEED:u32 = (0x3 << FXMAC_PCS_AN_LP_SPEED_OFFSET); /* SGMII 11 : Reserved 10 : 1000 Mbps 01 : 100Mbps 00 : 10 Mbps */ +pub(crate) const FXMAC_PCS_AN_LP_DUPLEX_OFFSET:u64 = 12; +pub(crate) const FXMAC_PCS_AN_LP_DUPLEX:u32 = (0x3 << FXMAC_PCS_AN_LP_DUPLEX_OFFSET); /* SGMII Bit 13: Reserved. read as 0. Bit 12 : 0 : half-duplex. 1: Full Duplex." */ +pub(crate) const FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_STATUS:u32 = (1 << 15); /* In sgmii mode, 0 is link down . 1 is link up */ +pub(crate) const FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_OFFSET:u64 = 15; + + +/* USXGMII Status Register */ +pub(crate) const FXMAC_GEM_USX_STATUS_BLOCK_LOCK:u32 = BIT(0); /* Block Lock. A value of one indicates that the PCS has achieved block synchronization. */ + +// aarch64 +pub(crate) const BITS_PER_LONG: u32 = 64; +pub const fn BIT(n: u32) -> u32 { + 1 << n +} + +pub const fn GENMASK(h:u32, l: u32) -> u32 { + ( + (!(0 as u64) - (1 << l) + 1) & + (!(0 as u64) >> (BITS_PER_LONG - 1 - h)) + ) as u32 +} + +//////////////////// +/// fxmac.h + +pub(crate) const FXMAC_PROMISC_OPTION:u32 = 0x00000001; +/* Accept all incoming packets. + * This option defaults to disabled (cleared) */ + +pub(crate) const FXMAC_FRAME1536_OPTION:u32 = 0x00000002; +/* Frame larger than 1516 support for Tx & Rx.x + * This option defaults to disabled (cleared) */ + +pub(crate) const FXMAC_VLAN_OPTION:u32 = 0x00000004; +/* VLAN Rx & Tx frame support. + * This option defaults to disabled (cleared) */ + +pub(crate) const FXMAC_FLOW_CONTROL_OPTION:u32 = 0x00000010; +/* Enable recognition of flow control frames on Rx + * This option defaults to enabled (set) */ + +pub(crate) const FXMAC_FCS_STRIP_OPTION:u32 = 0x00000020; +/* Strip FCS and PAD from incoming frames. Note: PAD from VLAN frames is not + * stripped. + * This option defaults to enabled (set) */ + +pub(crate) const FXMAC_FCS_INSERT_OPTION:u32 = 0x00000040; +/* Generate FCS field and add PAD automatically for outgoing frames. + * This option defaults to disabled (cleared) */ + +pub(crate) const FXMAC_LENTYPE_ERR_OPTION:u32 = 0x00000080; +/* Enable Length/Type error checking for incoming frames. When this option is + * set, the MAC will filter frames that have a mismatched type/length field + * and if FXMAC_REPORT_RXERR_OPTION is set, the user is notified when these + * types of frames are encountered. When this option is cleared, the MAC will + * allow these types of frames to be received. + * + * This option defaults to disabled (cleared) */ + +pub(crate) const FXMAC_TRANSMITTER_ENABLE_OPTION:u32 = 0x00000100; +/* Enable the transmitter. + * This option defaults to enabled (set) */ + +pub(crate) const FXMAC_RECEIVER_ENABLE_OPTION:u32 = 0x00000200; +/* Enable the receiver + * This option defaults to enabled (set) */ + +pub(crate) const FXMAC_BROADCAST_OPTION:u32 = 0x00000400; +/* Allow reception of the broadcast address + * This option defaults to enabled (set) */ + +pub(crate) const FXMAC_MULTICAST_OPTION:u32 = 0x00000800; +/* Allows reception of multicast addresses programmed into hash + * This option defaults to disabled (clear) */ + +pub(crate) const FXMAC_RX_CHKSUM_ENABLE_OPTION:u32 = 0x00001000; +/* Enable the RX checksum offload + * This option defaults to enabled (set) */ + +pub(crate) const FXMAC_TX_CHKSUM_ENABLE_OPTION:u32 = 0x00002000; +/* Enable the TX checksum offload + * This option defaults to enabled (set) */ + +pub(crate) const FXMAC_JUMBO_ENABLE_OPTION:u32 = 0x00004000; +pub(crate) const FXMAC_SGMII_ENABLE_OPTION:u32 = 0x00008000; + +pub(crate) const FXMAC_LOOPBACK_NO_MII_OPTION:u32 = 0x00010000; +pub(crate) const FXMAC_LOOPBACK_USXGMII_OPTION:u32 = 0x00020000; + +pub(crate) const FXMAC_UNICAST_OPTION:u32 = 0x00040000; + +pub(crate) const FXMAC_TAIL_PTR_OPTION:u32 = 0x00080000; + + +pub(crate) const FXMAC_DEFAULT_OPTIONS:u32 = + (FXMAC_FLOW_CONTROL_OPTION | + FXMAC_FCS_INSERT_OPTION | + FXMAC_FCS_STRIP_OPTION | + FXMAC_BROADCAST_OPTION | + FXMAC_LENTYPE_ERR_OPTION | + FXMAC_TRANSMITTER_ENABLE_OPTION | + FXMAC_RECEIVER_ENABLE_OPTION | + FXMAC_RX_CHKSUM_ENABLE_OPTION | + FXMAC_TX_CHKSUM_ENABLE_OPTION); + +/* The next few constants help upper layers determine the size of memory + * pools used for Ethernet buffers and descriptor lists. + */ +pub(crate) const FXMAC_MAC_ADDR_SIZE:u32 = 6; /* size of Ethernet header */ + +pub(crate) const FXMAC_MTU:u32 = 1500; /* max MTU size of Ethernet frame */ +pub(crate) const FXMAC_MTU_JUMBO:u32 = 10240; /* max MTU size of jumbo frame including Ip header + IP payload */ +pub(crate) const FXMAC_HDR_SIZE:u32 = 14; /* size of Ethernet header , DA + SA + TYPE*/ +pub(crate) const FXMAC_HDR_VLAN_SIZE:u32 = 18; /* size of Ethernet header with VLAN */ +pub(crate) const FXMAC_TRL_SIZE:u32 = 4; /* size of Ethernet trailer (FCS) */ + +pub(crate) const FXMAC_MAX_FRAME_SIZE:u32 = (FXMAC_MTU + FXMAC_HDR_SIZE + FXMAC_TRL_SIZE); +pub(crate) const FXMAC_MAX_FRAME_SIZE_JUMBO:u32 = (FXMAC_MTU_JUMBO + FXMAC_HDR_SIZE + FXMAC_TRL_SIZE); + +pub(crate) const FXMAC_MAX_VLAN_FRAME_SIZE:u32 = (FXMAC_MTU + FXMAC_HDR_SIZE + FXMAC_HDR_VLAN_SIZE + FXMAC_TRL_SIZE); +pub(crate) const FXMAC_MAX_VLAN_FRAME_SIZE_JUMBO:u32 = (FXMAC_MTU_JUMBO + FXMAC_HDR_SIZE + FXMAC_HDR_VLAN_SIZE + FXMAC_TRL_SIZE); + +/** @name Callback identifiers + * + * These constants are used as parameters to FXMAC_SetHandler() + * @{ + */ +pub(crate) const FXMAC_HANDLER_DMASEND:u32 = 1; /* 发送中断 */ +pub(crate) const FXMAC_HANDLER_DMARECV:u32 = 2; /* 接收中断 */ +pub(crate) const FXMAC_HANDLER_ERROR:u32 = 3; /* 异常中断 */ +pub(crate) const FXMAC_HANDLER_LINKCHANGE:u32 = 4; /* 连接状态 */ +pub(crate) const FXMAC_HANDLER_RESTART:u32 = 5; /* 发送描述符队列发生异常 */ +/*@}*/ + +pub(crate) const FXMAC_DMA_SG_IS_STARTED:u32 = 0; +pub(crate) const FXMAC_DMA_SG_IS_STOPED:u32 = 1; + +pub(crate) const FXMAC_SPEED_10:u32 = 10; +pub(crate) const FXMAC_SPEED_100:u32 = 100; +pub(crate) const FXMAC_SPEED_1000:u32 = 1000; +pub(crate) const FXMAC_SPEED_2500:u32 = 2500; +pub(crate) const FXMAC_SPEED_5000:u32 = 5000; +pub(crate) const FXMAC_SPEED_10000:u32 = 10000; +pub(crate) const FXMAC_SPEED_25000:u32 = 25000; + +/* Capability mask bits */ +pub(crate) const FXMAC_CAPS_ISR_CLEAR_ON_WRITE:u32 = 0x00000001; /* irq status parameters need to be written to clear after they have been read */ +pub(crate) const FXMAC_CAPS_TAILPTR:u32 = 0x00000002; /* use tail ptr */ + +/* +Direction identifiers +These are used by several functions and callbacks that need +to specify whether an operation specifies a send or receive channel. + +pub(crate) const FXMAC_PHY_INTERFACE_MODE_2500BASEX: u32 = 6; +pub(crate) const FXMAC_PHY_INTERFACE_MODE_5GBASER: u32 = 5; +pub(crate) const FXMAC_PHY_INTERFACE_MODE_USXGMII: u32 = 4; +pub(crate) const FXMAC_PHY_INTERFACE_MODE_XGMII: u32 = 3; +pub(crate) const FXMAC_PHY_INTERFACE_MODE_RGMII: u32 = 2; +pub(crate) const FXMAC_PHY_INTERFACE_MODE_RMII: u32 = 1; +pub(crate) const FXMAC_PHY_INTERFACE_MODE_SGMII: u32 = 0; +*/ \ No newline at end of file diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index ca1478e6a..d490b26ba 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -1,202 +1,1273 @@ +use core::cmp::min; +use core::ptr::{null_mut, null}; +use core::slice::from_raw_parts_mut; +use alloc::boxed::Box; +use alloc::vec::Vec; +use log::*; +use crate::fxmac_const::*; +use crate::fxmac_phy::*; +use crate::fxmac::*; +// fxmac_lwip_port.h +pub const FXMAX_RX_BDSPACE_LENGTH: usize = 0x20000; /* default set 128KB*/ +pub const FXMAX_TX_BDSPACE_LENGTH: usize = 0x20000; /* default set 128KB*/ -FError FXmacInitDma(FXmacLwipPort *instance_p) -{ - FXmacBd bdtemplate; - FXmacBdRing *rxringptr, *txringptr; - FXmacBd *rxbd; - struct pbuf *p; - FError status; - int i; - u32 bdindex = 0; - u32 *temp; - FXMAC_LWIP_PORT_XMAC_PRINT_I("%s,\r\n", __func__); +pub const FXMAX_RX_PBUFS_LENGTH: usize = 128; +pub const FXMAX_TX_PBUFS_LENGTH: usize = 128; - /* - * The BDs need to be allocated in uncached memory. Hence the 1 MB - * address range allocated for Bd_Space is made uncached - * by setting appropriate attributes in the translation table. - * The Bd_Space is aligned to 1MB and has a size of 1 MB. This ensures - * a reserved uncached area used only for BDs. - */ +pub const FXMAX_MAX_HARDWARE_ADDRESS_LENGTH: usize =6; - rxringptr = &FXMAC_GET_RXRING(instance_p->instance); - txringptr = &FXMAC_GET_TXRING(instance_p->instance); - FXMAC_LWIP_PORT_XMAC_PRINT_I("rxringptr: 0x%08x\r\n", rxringptr); - FXMAC_LWIP_PORT_XMAC_PRINT_I("txringptr: 0x%08x\r\n", txringptr); +/* configuration */ +pub const FXMAC_LWIP_PORT_CONFIG_JUMBO: u32 = BIT(0); +pub const FXMAC_LWIP_PORT_CONFIG_MULTICAST_ADDRESS_FILITER: u32 = BIT(1); /* Allow multicast address filtering */ +pub const FXMAC_LWIP_PORT_CONFIG_COPY_ALL_FRAMES: u32 =BIT(2); /* enable copy all frames */ +pub const FXMAC_LWIP_PORT_CONFIG_CLOSE_FCS_CHECK: u32 =BIT(3); /* close fcs check */ +pub const FXMAC_LWIP_PORT_CONFIG_UNICAST_ADDRESS_FILITER: u32 =BIT(5); /* Allow unicast address filtering */ - FXMAC_LWIP_PORT_XMAC_PRINT_I("rx_bdspace: %p \r\n", instance_p->buffer.rx_bdspace); - FXMAC_LWIP_PORT_XMAC_PRINT_I("tx_bdspace: %p \r\n", instance_p->buffer.tx_bdspace); +/* Phy */ +pub const FXMAC_PHY_SPEED_10M: u32 = 10; +pub const FXMAC_PHY_SPEED_100M: u32 = 100; +pub const FXMAC_PHY_SPEED_1000M: u32 = 1000; +pub const FXMAC_PHY_SPEED_10G: u32 = 10000; - /* Setup RxBD space. */ - FXMAC_BD_CLEAR(&bdtemplate); - /* Create the RxBD ring */ - status = FXmacBdRingCreate(rxringptr, (uintptr)instance_p->buffer.rx_bdspace, - (uintptr)instance_p->buffer.rx_bdspace, BD_ALIGNMENT, - FXMAX_RX_PBUFS_LENGTH); +pub const FXMAC_PHY_HALF_DUPLEX: u32 = 0; +pub const FXMAC_PHY_FULL_DUPLEX: u32 = 1; - if (status != FT_SUCCESS) - { - FXMAC_LWIP_PORT_XMAC_PRINT_E("Error setting up RxBD space\r\n"); - return ERR_IF; +pub const FXMAC_RECV_MAX_COUNT: u32 =10; + +/* frame queue */ +pub const PQ_QUEUE_SIZE: u32 =4096; + +/// Transmit buffer descriptor status words offset +/// word 0/addr of BDs +pub const FXMAC_BD_ADDR_OFFSET: u32 = 0; +/// word 1/status of BDs, 4 bytes +pub const FXMAC_BD_STAT_OFFSET: u32 = 4; +/// word 2/addr of BDs +pub const FXMAC_BD_ADDR_HI_OFFSET: u32 = 1 << 3; + +/// RX Used bit +pub const FXMAC_RXBUF_NEW_MASK: u32 = 1<<0; +/// RX Wrap bit, last BD +pub const FXMAC_RXBUF_WRAP_MASK: u32 = 1<<1; +/// Mask for address +pub const FXMAC_RXBUF_ADD_MASK: u32 = GENMASK(31, 2); +// FXMAC_RXBUF_ADD_MASK=0xff_ff_ff_fc + +/// TX Used bit +pub const FXMAC_TXBUF_USED_MASK: u32 = 1<<31; +/// TX Wrap bit, last descriptor +pub const FXMAC_TXBUF_WRAP_MASK: u32 = 1<<30; + +pub const ULONG64_HI_MASK: u64 = 0xFFFFFFFF_00000000; +pub const ULONG64_LO_MASK: u64 = !ULONG64_HI_MASK; + +/// Byte alignment of BDs +pub const BD_ALIGNMENT: u64 = FXMAC_DMABD_MINIMUM_ALIGNMENT*2; // 128 +pub const FXMAC_DMABD_MINIMUM_ALIGNMENT: u64 = 64; +pub const FXMAC_BD_NUM_WORDS: usize = 4; +pub type FXmacBd = [u32; FXMAC_BD_NUM_WORDS]; +// sizeof(uintptr)=8 +pub type uintptr = u64; + +/// Enable tail Register +//pub const FXMAC_TAIL_ENABLE: u32 = 0xe7c; + +/// send direction +pub const FXMAC_SEND: u32 = 1; +/// receive direction +pub const FXMAC_RECV: u32 = 2; + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct FXmacBdRing { + pub phys_base_addr: uintptr, + pub base_bd_addr: uintptr, + pub high_bd_addr: uintptr, + pub length: u32, + pub run_state: u32, + pub separation: u32, + pub free_head: *mut FXmacBd, + pub pre_head: *mut FXmacBd, + pub hw_head: *mut FXmacBd, + pub hw_tail: *mut FXmacBd, + pub post_head: *mut FXmacBd, + pub bda_restart: *mut FXmacBd, + pub hw_cnt: u32, + pub pre_cnt: u32, + pub free_cnt: u32, + pub post_cnt: u32, + pub all_cnt: u32, +} + +impl Default for FXmacBdRing { + fn default() -> Self { + Self { + phys_base_addr: 0, + base_bd_addr: 0, + high_bd_addr: 0, + length: 0, + run_state: 0, + separation: 0, + free_head: null_mut(), + pre_head: null_mut(), + hw_head: null_mut(), + hw_tail: null_mut(), + post_head: null_mut(), + bda_restart: null_mut(), + hw_cnt: 0, + pre_cnt: 0, + free_cnt: 0, + post_cnt: 0, + all_cnt: 0, + } } +} - status = FXmacBdRingClone(rxringptr, &bdtemplate, FXMAC_RECV); - if (status != FT_SUCCESS) - { - FXMAC_LWIP_PORT_XMAC_PRINT_E("Error initializing RxBD space\r\n"); - return ERR_IF; +pub struct FXmacNetifBuffer +{ + // 作为FXmacBdRing的基地址,并设置成一串多个BD + pub rx_bdspace: Box<[u8; FXMAX_RX_BDSPACE_LENGTH]>, // aligned(256); 接收bd 缓冲区 + pub tx_bdspace: Box<[u8; FXMAX_RX_BDSPACE_LENGTH]>, // aligned(256); 发送bd 缓冲区 + + // 保存收发数据包的内存基地址,收发数据包内存需要申请alloc + pub rx_pbufs_storage: [uintptr; FXMAX_RX_PBUFS_LENGTH], + pub tx_pbufs_storage: [uintptr; FXMAX_TX_PBUFS_LENGTH], +} + +impl Default for FXmacNetifBuffer { + fn default() -> Self { + Self { + rx_bdspace: Box::new([0; FXMAX_RX_BDSPACE_LENGTH]), + tx_bdspace: Box::new([0; FXMAX_RX_BDSPACE_LENGTH]), + rx_pbufs_storage: [0; FXMAX_RX_PBUFS_LENGTH], + tx_pbufs_storage: [0; FXMAX_TX_PBUFS_LENGTH], + } } +} - FXMAC_BD_CLEAR(&bdtemplate); - FXMAC_BD_SET_STATUS(&bdtemplate, FXMAC_TXBUF_USED_MASK); +pub struct FXmacLwipPort +{ + pub buffer: FXmacNetifBuffer, + // configuration + pub feature: u32, + pub hwaddr: [u8; FXMAX_MAX_HARDWARE_ADDRESS_LENGTH], + // Indicating how many receive interrupts have been triggered + pub recv_flg: u32, +} - /* Create the TxBD ring */ - status = FXmacBdRingCreate(txringptr, (uintptr)instance_p->buffer.tx_bdspace, - (uintptr)instance_p->buffer.tx_bdspace, BD_ALIGNMENT, - FXMAX_TX_PBUFS_LENGTH); +pub fn fxmac_bd_read(bd_ptr: u64, offset: u32) -> u32 { + read_reg((bd_ptr + offset as u64) as *const u32) +} +pub fn fxmac_bd_write(bd_ptr: u64, offset: u32, data: u32) +{ + // uintptr: u64 + write_reg((bd_ptr + offset as u64) as *mut u32, data); +} + +/// FXmacBdSetRxWrap +/// Set this bit to mark the last descriptor in the receive buffer descriptor list. +fn FXmacBdSetRxWrap(mut bdptr: u64) { + bdptr += FXMAC_BD_ADDR_OFFSET as u64; + let temp_ptr = bdptr as *mut u32; + if !temp_ptr.is_null() { + let mut data_value_rx: u32 = unsafe{*temp_ptr}; + data_value_rx |= FXMAC_RXBUF_WRAP_MASK; + unsafe { + temp_ptr.write_volatile(data_value_rx); + } + } +} - if (status != FT_SUCCESS) - { - return ERR_IF; +/// FXmacBdSetTxWrap +/// Sets this bit to mark the last descriptor in the transmit buffer descriptor list. +fn FXmacBdSetTxWrap(mut bdptr: u64) { + bdptr += FXMAC_BD_STAT_OFFSET as u64; + let temp_ptr = bdptr as *mut u32; + if !temp_ptr.is_null() { + let mut data_value_tx: u32 = unsafe{*temp_ptr}; + data_value_tx |= FXMAC_TXBUF_WRAP_MASK; + unsafe { + temp_ptr.write_volatile(data_value_tx); + } + } +} + +/// Reset BD ring head and tail pointers. +fn FXmacBdringPtrReset(ring_ptr: &mut FXmacBdRing, virtaddrloc: *mut FXmacBd) +{ + ring_ptr.free_head = virtaddrloc; + ring_ptr.pre_head = virtaddrloc; + ring_ptr.hw_head = virtaddrloc; + ring_ptr.hw_tail = virtaddrloc; + ring_ptr.post_head = virtaddrloc; +} + +/// Set the BD's address field (word 0). +fn fxmac_bd_set_address_rx(bd_ptr: u64, addr: u64) { + fxmac_bd_write((bd_ptr), FXMAC_BD_ADDR_OFFSET, + ((fxmac_bd_read(bd_ptr, FXMAC_BD_ADDR_OFFSET) & !FXMAC_RXBUF_ADD_MASK) | + (addr & ULONG64_LO_MASK) as u32)); + + fxmac_bd_write(bd_ptr, FXMAC_BD_ADDR_HI_OFFSET, ((addr & ULONG64_HI_MASK) >> 32) as u32); + + // For aarch64 +} + +/// Set the BD's address field (word 0). +fn fxmac_bd_set_address_tx(bd_ptr: u64, addr: u64) { + fxmac_bd_write((bd_ptr), FXMAC_BD_ADDR_OFFSET, (addr & ULONG64_LO_MASK) as u32); + + fxmac_bd_write(bd_ptr, FXMAC_BD_ADDR_HI_OFFSET, ((addr & ULONG64_HI_MASK) >> 32) as u32); + + // For aarch64 +} + +/// 将bdptr参数向前移动任意数量的bd,绕到环的开头。 +fn FXMAC_RING_SEEKAHEAD(ring_ptr: &mut FXmacBdRing, mut bdptr: *mut FXmacBd, num_bd: u32) +{ + // 第一个free BD + // bdptr = free_head + + let mut addr: u64 = bdptr as u64; + addr += (ring_ptr.separation * num_bd) as u64; + + if (addr > ring_ptr.high_bd_addr) || (bdptr as u64 > addr) + { + addr -= ring_ptr.length as u64; + } + bdptr = addr as *mut FXmacBd; +} + +/* +pub fn FXmacBdRingCreate() -> u32 { + + // 为DMA构建环形缓冲区内存 + + let alloc_tx_ring_pages = + ((MACB_TX_RING_SIZE * DMA_DESC_SIZE) + (M::PAGE_SIZE - 1)) / M::PAGE_SIZE; + let alloc_rx_ring_pages = + ((MACB_RX_RING_SIZE * DMA_DESC_SIZE) + (M::PAGE_SIZE - 1)) / M::PAGE_SIZE; + let tx_ring_dma = M::dma_alloc_coherent(alloc_tx_ring_pages); + let rx_ring_dma = M::dma_alloc_coherent(alloc_rx_ring_pages); + + let tx_ring = unsafe { + slice::from_raw_parts_mut( + phys_to_virt(tx_ring_dma) as *mut DmaDesc, + MACB_TX_RING_SIZE * DMA_DESC_SIZE / size_of::(), // 4096/16 = 256 个 dma_desc ? + ) + }; + + let rx_ring = unsafe { + slice::from_raw_parts_mut( + phys_to_virt(rx_ring_dma) as *mut DmaDesc, + MACB_RX_RING_SIZE * DMA_DESC_SIZE / size_of::(), + ) + }; + + let mut send_buffers = Vec::with_capacity(tx_ring.len()); + let mut recv_buffers = Vec::with_capacity(rx_ring.len()); + + // 一起申请所有RX内存 + let alloc_rx_buffer_pages = + ((MACB_RX_RING_SIZE * buffer_size) + (M::PAGE_SIZE - 1)) / M::PAGE_SIZE; + let rx_buffer_dma: usize = M::dma_alloc_coherent(alloc_rx_buffer_pages); + + info!("Set ring desc buffer for RX"); + let mut count = 0; + let mut paddr: u64 = rx_buffer_dma as u64; + for i in 0..MACB_RX_RING_SIZE { + if i == MACB_RX_RING_SIZE - 1 { + paddr |= 1 << MACB_RX_WRAP_OFFSET; + } + + if (config.hw_dma_cap & HW_DMA_CAP_64B) != 0 { + count = i * 2; + rx_ring[count + 1].addr = upper_32_bits(paddr); // Fill DmaDesc64.addrh + } else { + count = i; + } + rx_ring[count].ctrl = 0; + rx_ring[count].addr = lower_32_bits(paddr); + + recv_buffers.push(phys_to_virt(paddr as usize)); + paddr += buffer_size as u64; + + // sync memery, fence指令? } + flush_dcache_range(); // RX dma ring and buffer - /* We reuse the bd template, as the same one will work for both rx and tx. */ - status = FXmacBdRingClone(txringptr, &bdtemplate, FXMAC_SEND); - if (status != FT_SUCCESS) - { - return ERR_IF; + // 一起申请所有TX内存 + let alloc_tx_buffer_pages = + ((MACB_TX_RING_SIZE * buffer_size) + (M::PAGE_SIZE - 1)) / M::PAGE_SIZE; + let tx_buffer_dma: usize = M::dma_alloc_coherent(alloc_tx_buffer_pages); + info!("Set ring desc buffer for TX"); + count = 0; + paddr = tx_buffer_dma as u64; + for i in 0..MACB_TX_RING_SIZE { + if (config.hw_dma_cap & HW_DMA_CAP_64B) != 0 { + count = i * 2; + tx_ring[count + 1].addr = upper_32_bits(paddr); // Fill DmaDesc64.addrh + } else { + count = i; + } + tx_ring[count].addr = lower_32_bits(paddr); + + if i == MACB_TX_RING_SIZE - 1 { + tx_ring[count].ctrl = (1 << MACB_TX_USED_OFFSET) | (1 << MACB_TX_WRAP_OFFSET); + } else { + tx_ring[count].ctrl = (1 << MACB_TX_USED_OFFSET); + } + // Used – must be zero for the controller to read data to the transmit buffer. + // The controller sets this to one for the first buffer of a frame once it has been successfully transmitted. + // Software must clear this bit before the buffer can be used again. + + send_buffers.push(phys_to_virt(paddr as usize)); + paddr += buffer_size as u64; } + flush_dcache_range(); // TX dma ring + +} +*/ + + +pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 +{ + //let mut rxbd: FXmacBd = [0; FXMAC_BD_NUM_WORDS]; + + let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; + let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; + info!("rxringptr: {:p}", rxringptr); + info!("txringptr: {:p}", txringptr); + info!("rx_bdspace: {:p}", &instance_p.lwipport.buffer.rx_bdspace); + info!("tx_bdspace: {:p}", &instance_p.lwipport.buffer.tx_bdspace); + + // Setup RxBD space. + // 对BD域清零 + //let mut bdtemplate: FXmacBd = unsafe { core::mem::zeroed() }; + + // FXmacBd: The FXMAC_Bd is the type for buffer descriptors (BDs). + let mut bdtemplate: FXmacBd = [0; FXMAC_BD_NUM_WORDS]; + + // Create the RxBD ring, bdspace地址必须对齐128 + // 创建收包的环形缓冲区 + let mut status: u32 = FXmacBdRingCreate(rxringptr, &instance_p.lwipport.buffer.rx_bdspace as *const _ as u64, &instance_p.lwipport.buffer.rx_bdspace as *const _ as u64, BD_ALIGNMENT, FXMAX_RX_PBUFS_LENGTH as u32); + + // 将给定的BD, 克隆到list中的每个BD上 + status = FXmacBdRingClone(rxringptr, &mut bdtemplate, FXMAC_RECV); + + + let mut bdtemplate: [u32; FXMAC_BD_NUM_WORDS] = [0; FXMAC_BD_NUM_WORDS]; + + // FXMAC_BD_SET_STATUS() + // Set the BD's Status field (word 1). + fxmac_bd_write((&bdtemplate as *const _ as u64), FXMAC_BD_STAT_OFFSET, + fxmac_bd_read((&bdtemplate as *const _ as u64), FXMAC_BD_STAT_OFFSET) | (FXMAC_TXBUF_USED_MASK)); + + /* Create the TxBD ring */ + status = FXmacBdRingCreate(txringptr, &instance_p.lwipport.buffer.tx_bdspace as *const _ as u64, &instance_p.lwipport.buffer.tx_bdspace as *const _ as u64, BD_ALIGNMENT, FXMAX_TX_PBUFS_LENGTH as u32); + + /* We reuse the bd template, as the same one will work for both rx and tx. */ + status = FXmacBdRingClone(txringptr, &mut bdtemplate, FXMAC_SEND); /* * Allocate RX descriptors, 1 RxBD at a time. */ - FXMAC_LWIP_PORT_XMAC_PRINT_I("Allocate RX descriptors, 1 RxBD at a time."); - for (i = 0; i < FXMAX_RX_PBUFS_LENGTH; i++) + info!("Allocate RX descriptors, 1 RxBD at a time."); + for i in 0..FXMAX_RX_PBUFS_LENGTH { - if (instance_p->feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) + let max_frame_size = if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 + { info!("FXMAC_LWIP_PORT_CONFIG_JUMBO"); FXMAC_MAX_FRAME_SIZE_JUMBO } else { info!("NO CONFIG_JUMBO"); FXMAC_MAX_FRAME_SIZE }; + let alloc_rx_buffer_pages = (max_frame_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; + + let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = crate::utils::dma_alloc_coherent(alloc_rx_buffer_pages); + + let rxbd: *mut FXmacBd = null_mut(); +//let my_speed: Box = Box::new(88); +//rxbd = Box::into_raw(my_speed); +// OR +//let mut my_speed: i32 = 88; +//rxbd = &mut my_speed; + + // 在BD list中预留待设置的BD + status = FXmacBdRingAlloc(rxringptr, 1, rxbd); + if (status != 0) { - p = pbuf_alloc(PBUF_RAW, FXMAC_MAX_FRAME_SIZE_JUMBO, PBUF_RAM); + error!("FXmacInitDma: Error allocating RxBD"); + return status; } - else + + // 将一组BD排队到之前由FXmacBdRingAlloc分配了的硬件上 + status = FXmacBdRingToHw(rxringptr, 1, rxbd); + + let bdindex = FXMAC_BD_TO_INDEX(rxringptr, rxbd as u64); + + let mut temp = rxbd as *mut u32; + + let mut v = 0; + if bdindex == (FXMAX_RX_PBUFS_LENGTH - 1) as u32 { + // Marks last descriptor in receive buffer descriptor list + v |= FXMAC_RXBUF_WRAP_MASK; + } + unsafe{ + temp.write_volatile(v); + // Clear word 1 in descriptor + temp.add(1).write_volatile(0); + } + crate::utils::DSB(); + + // dc civac, virt_addr 通过虚拟地址清除和无效化cache + crate::utils::FCacheDCacheInvalidateRange(rx_mbufs_vaddr as u64, max_frame_size as u64); + + // Set the BD's address field (word 0) + // void *payload; 指向数据区域的指针,指向该pbuf管理的数据区域起始地址,可以是ROM或者RAM中的某个地址 + fxmac_bd_set_address_rx(rxbd as u64, rx_mbufs_dma as u64); + + instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize] = rx_mbufs_vaddr as u64; + } + + FXmacSetQueuePtr(instance_p.rx_bd_queue.bdring.phys_base_addr, 0, FXMAC_RECV); + + FXmacSetQueuePtr(instance_p.tx_bd_queue.bdring.phys_base_addr, 0, FXMAC_SEND); + + let FXMAC_TAIL_QUEUE = |queue: u64| 0x0e80 + (queue << 2); + if (instance_p.config.caps & FXMAC_CAPS_TAILPTR) != 0 + { + write_reg((instance_p.config.base_address + FXMAC_TAIL_QUEUE(0)) as *mut u32, (1<<31) | 0); + } + + 0 +} + +fn FXMAC_BD_TO_INDEX (ringptr: &mut FXmacBdRing, bdptr: u64) -> u32 { + ( (bdptr - ringptr.base_bd_addr as u64) / ringptr.separation as u64 ) as u32 +} + +/// 从bptr的列表中,获取下一个BD +fn FXMAC_BD_RING_NEXT(ring_ptr: &mut FXmacBdRing, bd_ptr: *mut FXmacBd) -> *mut FXmacBd { + if bd_ptr as u64 >= ring_ptr.high_bd_addr { + ring_ptr.base_bd_addr as *mut FXmacBd + }else{ + (bd_ptr as u64 + ring_ptr.separation as u64) as *mut FXmacBd + } +} + +/// Create the RxBD ring +/// 创建收包的环形缓冲区 +pub fn FXmacBdRingCreate(ring_ptr: &mut FXmacBdRing, phys_addr: u64, virt_addr: u64, alignment: u64, bd_count: u32) -> u32 + { + // uintptr: u64 + + // alignment=128, bd_count=128 + //let alignment = BD_ALIGNMENT; + //let bd_count = FXMAX_RX_PBUFS_LENGTH; + + //let virt_addr_loc = instance_p.buffer.rx_bdspace; + let virt_addr_loc: u64 = virt_addr; + + ring_ptr.all_cnt = 0; + ring_ptr.free_cnt = 0; + ring_ptr.hw_cnt = 0; + ring_ptr.pre_cnt = 0; + ring_ptr.post_cnt = 0; + + // 该地址必须对齐alignment=128 + assert!((virt_addr_loc % alignment) == 0); + assert!(bd_count > 0); + + // 相邻BD之间隔多少bytes + ring_ptr.separation = size_of::() as u32; + +// Initial ring setup: +// - Clear the entire space +// - Setup each BD's BDA field with the physical address of the next BD + let rxringptr = unsafe { from_raw_parts_mut(virt_addr_loc as *mut FXmacBd, bd_count as usize) }; + rxringptr.fill([0; FXMAC_BD_NUM_WORDS]); + + let mut bd_virt_addr = virt_addr_loc; + for i in 1..bd_count { + bd_virt_addr += ring_ptr.separation as u64; + } + + // Setup and initialize pointers and counters + + // BD list中第一个的虚拟地址 + ring_ptr.base_bd_addr = virt_addr_loc; + // BD list中最后一个的虚拟地址 + ring_ptr.high_bd_addr = bd_virt_addr; + // ring的总大小bytes + ring_ptr.length = (ring_ptr.high_bd_addr - ring_ptr.base_bd_addr) as u32 + ring_ptr.separation; + // 第一个free BD + ring_ptr.free_head = virt_addr_loc as *mut FXmacBd; + // 第一个pre-work BD + ring_ptr.pre_head = virt_addr_loc as *mut FXmacBd; + + // 可分配的free BD数量 + ring_ptr.free_cnt = bd_count; + // 总共的BD数 + ring_ptr.all_cnt = bd_count; + + ring_ptr.run_state = FXMAC_DMA_SG_IS_STOPED as u32; + ring_ptr.phys_base_addr = phys_addr; + ring_ptr.hw_head = virt_addr_loc as *mut FXmacBd; + ring_ptr.hw_tail = virt_addr_loc as *mut FXmacBd; + ring_ptr.post_head = virt_addr_loc as *mut FXmacBd; + ring_ptr.bda_restart = phys_addr as *mut FXmacBd; + + 0 + } + +/// 将给定的BD, 克隆到list中的每个BD上 +pub fn FXmacBdRingClone(ring_ptr: &mut FXmacBdRing, src_bd_ptr: &mut FXmacBd, direction: u32) -> u32 +{ + // Can't do this function with some of the BDs in use + assert!(ring_ptr.free_cnt == ring_ptr.all_cnt); + + let mut cur_bd = ring_ptr.base_bd_addr; + for i in 0..ring_ptr.all_cnt { + // 将所有BD逐个复制成bdtemplate + //let cur_bd_slice = unsafe { from_raw_parts_mut(cur_bd as *mut FXmacBd, 1) }; + //cur_bd_slice.copy_from_slice(src_bd_ptr); + unsafe{ *(cur_bd as *mut FXmacBd) }.copy_from_slice(src_bd_ptr); + + cur_bd += ring_ptr.separation as u64; + } + cur_bd -= ring_ptr.separation as u64; + + // 对最后一个BD描述符进行位标记 + if direction == FXMAC_RECV { + FXmacBdSetRxWrap(cur_bd); + } else { + FXmacBdSetTxWrap(cur_bd); + } + + 0 +} + + +/// 在BD list中预留待设置的BD +pub fn FXmacBdRingAlloc(ring_ptr: &mut FXmacBdRing, num_bd: u32, mut bd_set_ptr: *mut FXmacBd) -> u32 { +/* +let num_bd = 1; +let bd_set_ptr = &rxbd; +*/ + + if ring_ptr.free_cnt < num_bd { + error!("No Enough free BDs available for the request: {}", num_bd); + 4 + } else { + // 获取待设置的BD,并向前移动free BD + bd_set_ptr = ring_ptr.free_head as *mut FXmacBd; + + let b = ring_ptr.free_head; + FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr.free_head, num_bd); + assert!(b as usize != ring_ptr.free_head as usize); + + ring_ptr.free_cnt -= num_bd; + ring_ptr.pre_cnt += num_bd; + + 0 + } +} + + +/// 将一组BD排队到之前由FXmacBdRingAlloc分配了的硬件上 +pub fn FXmacBdRingToHw(ring_ptr: &mut FXmacBdRing, num_bd: u32, bd_set_ptr: *mut FXmacBd) -> u32 { + // uintptr: u64 + + let mut cur_bd_ptr: *mut FXmacBd = bd_set_ptr; + for i in 0..num_bd { + cur_bd_ptr = FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr); + } + + FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr.pre_head, num_bd); + + ring_ptr.pre_cnt -= num_bd; + ring_ptr.hw_tail = cur_bd_ptr; + ring_ptr.hw_cnt += num_bd; + + 0 +} + +pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_set_ptr: *mut FXmacBd) -> u32 { + + let mut cur_bd_ptr: *mut FXmacBd = ring_ptr.hw_head; + let mut status: u32 = 0; + let mut bd_str: u32 = 0; + let mut bd_count: u32 = 0; + let mut bd_partial_count: u32 = 0; + + if ring_ptr.hw_cnt == 0 { + warn!("No BDs in RX work group, there's nothing to search"); + + bd_set_ptr = null_mut(); + + status = 0; + }else{ + /* Starting at hw_head, keep moving forward in the list until: + * - A BD is encountered with its new/used bit set which means hardware has completed processing of that BD. + * - ring_ptr->hw_tail is reached and ring_ptr->hw_cnt is reached. + * - The number of requested BDs has been processed + */ + while (bd_count as usize) < bd_limit { + + // Read the status + + bd_str = fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_STAT_OFFSET); + + // FXMAC_BD_IS_RX_NEW, Determine the new bit of the receive BD + let bd_is_rx_new = (fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_ADDR_OFFSET) & FXMAC_RXBUF_NEW_MASK) != 0; + if !bd_is_rx_new == true { + break; + } + + bd_count += 1; + + /* hardware has processed this BD so check the "last" bit. If + * it is clear, then there are more BDs for the current packet. + * Keep a count of these partial packet BDs. + */ + if (bd_str & FXMAC_RXBUF_EOF_MASK) != 0 { + bd_partial_count = 0; + } + else + { + bd_partial_count+=1; + } + + // Move on to next BD in work group + cur_bd_ptr = FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr); + + } + + // 减去找到的任何partial packet BDs + bd_count -= bd_partial_count; + + /* If bd_count is non-zero then BDs were found to return. Set return + * parameters, update pointers and counters, return success + */ + if bd_count > 0 { + bd_set_ptr = ring_ptr.hw_head; + + ring_ptr.hw_cnt -= bd_count; + ring_ptr.post_cnt += bd_count; + FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr.hw_head, bd_count); + + info!("FXmacBdRingFromHwRx, Found BD={}", bd_count); + status = bd_count; + } else { + bd_set_ptr = null_mut(); + status = 0; + } + } + status +} + + +/// @name: FXmacBdRingFromHwTx +/// @msg: Returns a set of BD(s) that have been processed by hardware. The returned +/// BDs may be examined to determine the outcome of the DMA transaction(s). +/// Once the BDs have been examined, the user must call FXmacBdRingFree() +/// in the same order which they were retrieved here. +pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_set_ptr: *mut FXmacBd) -> u32 { + let mut bd_str: u32 = 0; + let mut bd_count: u32 = 0; + let mut bd_partial_count: u32 = 0; + let mut status: u32 = 0; + + let mut bd_limitLoc: u32 = bd_limit as u32; + let mut cur_bd_ptr: *mut FXmacBd = ring_ptr.hw_head; + + /* If no BDs in work group, then there's nothing to search */ + if ring_ptr.hw_cnt == 0 + { + warn!("No BDs in TX work group, then there's nothing to search"); + bd_set_ptr = null_mut(); + status = 0; + } else { + + if bd_limitLoc > ring_ptr.hw_cnt { - p = pbuf_alloc(PBUF_RAW, FXMAC_MAX_FRAME_SIZE, PBUF_POOL); + bd_limitLoc = ring_ptr.hw_cnt; + } + /* Starting at hw_head, keep moving forward in the list until: + * - A BD is encountered with its new/used bit set which means + * hardware has not completed processing of that BD. + * - ring_ptr->hw_tail is reached and ring_ptr->hw_cnt is reached. + * - The number of requested BDs has been processed + */ + while bd_count < bd_limitLoc { + // Read the status + bd_str = fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_STAT_OFFSET); + + if (bd_str & FXMAC_TXBUF_USED_MASK) != 0 + { + bd_count += 1; + bd_partial_count += 1; + } + + /* hardware has processed this BD so check the "last" bit. + * If it is clear, then there are more BDs for the current + * packet. Keep a count of these partial packet BDs. + */ + if (bd_str & FXMAC_TXBUF_LAST_MASK) != 0 + { + bd_partial_count = 0; + } + + /* Move on to next BD in work group */ + cur_bd_ptr = FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr); + } + + /* Subtract off any partial packet BDs found */ + bd_count -= bd_partial_count; + + /* If bd_count is non-zero then BDs were found to return. Set return + * parameters, update pointers and counters, return success + */ + if bd_count > 0 { + bd_set_ptr = ring_ptr.hw_head; + + ring_ptr.hw_cnt -= bd_count; + ring_ptr.post_cnt += bd_count; + FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr.hw_head, bd_count); + + info!("FXmacBdRingFromHwTx, Found BD={}", bd_count); + status = bd_count; + } else { + bd_set_ptr = null_mut(); + status = 0; } + } + + status +} + +pub fn FXmacLwipPortTx(instance: &mut FXmac, pbuf: Vec>) -> u32 +{ + // 发送网络包时注意屏蔽下中断 - if (!p) + // check if space is available to send + let freecnt = (instance.tx_bd_queue.bdring).free_cnt; + if freecnt <= 5 { + //let txring = &mut (instance.tx_bd_queue.bdring); + FXmacProcessSentBds(instance); + } + + if (instance.tx_bd_queue.bdring).free_cnt != 0 { + FXmacSgsend(instance, pbuf) + }else{ + error!(" TX packets dropped, no space"); + 3 // FREERTOS_XMAC_NO_VALID_SPACE + } +} + +/// 发包函数 +pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { + let mut status: u32 = 0; + let mut bdindex: u32 = 0; + let mut max_fr_size: u32 = 0; + + let mut last_txbd: *mut FXmacBd = null_mut(); + let txbdset: *mut FXmacBd = null_mut(); + let txring: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; + + // Count the number of pbufs: p + let n_pbufs: u32 = p.len() as u32; + + /* obtain as many BD's */ + status = FXmacBdRingAlloc(txring, n_pbufs, txbdset); + + let mut txbd: *mut FXmacBd = txbdset; + for q in &p { + bdindex = FXMAC_BD_TO_INDEX(txring, txbd as u64); + + if (instance_p.lwipport.buffer.tx_pbufs_storage[bdindex as usize] != 0) { -#if LINK_STATS - lwip_stats.link.memerr++; - lwip_stats.link.drop++; -#endif - FXMAC_LWIP_PORT_XMAC_PRINT_E("unable to alloc pbuf in InitDma\r\n"); - return ERR_IF; - } - status = FXmacBdRingAlloc(rxringptr, 1, &rxbd); - if (status != FT_SUCCESS) + panic!("PBUFS not available"); + } + + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + //FCacheDCacheFlushRange((uintptr)q->payload, (uintptr)q->len); + + fxmac_bd_set_address_tx(txbd as u64, q.as_ptr() as u64); + + if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 { - FXMAC_LWIP_PORT_XMAC_PRINT_E("InitDma: Error allocating RxBD\r\n"); - pbuf_free(p); - return ERR_IF; + max_fr_size = FXMAC_MAX_FRAME_SIZE_JUMBO; } - /* Enqueue to HW */ - status = FXmacBdRingToHw(rxringptr, 1, rxbd); - if (status != FT_SUCCESS) + else { - FXMAC_LWIP_PORT_XMAC_PRINT_E("Error: committing RxBD to HW\r\n"); - pbuf_free(p); - FXmacBdRingUnAlloc(rxringptr, 1, rxbd); - return ERR_IF; + max_fr_size = FXMAC_MAX_FRAME_SIZE; } - bdindex = FXMAC_BD_TO_INDEX(rxringptr, rxbd); - temp = (u32 *)rxbd; - *temp = 0; - if (bdindex == (FXMAX_RX_PBUFS_LENGTH - 1)) + let t_txbd = txbd as u64; + if q.len() > max_fr_size as usize { - *temp = 0x00000002; /* Marks last descriptor in receive buffer descriptor list */ + // FXMAC_BD_SET_LENGTH: 设置BD的发送长度(以bytes为单位)。每次BD交给硬件时,都必须设置该长度 + // FXMAC_BD_SET_LENGTH(txbd, max_fr_size & 0x3FFF); + fxmac_bd_write(t_txbd, FXMAC_BD_STAT_OFFSET, + ((fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) | (max_fr_size & 0x3FFF))); + } else { + // FXMAC_BD_SET_LENGTH(txbd, q->len & 0x3FFF); + fxmac_bd_write(t_txbd, FXMAC_BD_STAT_OFFSET, + ((fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) | (q.len() as u32 & 0x3FFF))); } - temp++; - *temp = 0; /* Clear word 1 in descriptor */ - DSB(); - if (instance_p->feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) + instance_p.lwipport.buffer.tx_pbufs_storage[bdindex as usize] = q.as_ptr() as u64; + + // 增加该pbuf的引用计数。 + //pbuf_ref(q); + + last_txbd = txbd; + + // 告诉DMA当前包不是以该BD结束 + //FXMAC_BD_CLEAR_LAST(txbd); + let t_txbd = txbd as u64; + fxmac_bd_write(t_txbd, FXMAC_BD_STAT_OFFSET, + fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LAST_MASK ); + + txbd = FXMAC_BD_RING_NEXT(txring, txbd); + } + // 告诉DMA 该BD标志着当前数据包的结束 + //FXMAC_BD_SET_LAST(last_txbd); + let t_txbd = last_txbd as u64; + fxmac_bd_write(t_txbd, FXMAC_BD_STAT_OFFSET, + fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) | FXMAC_TXBUF_LAST_MASK ); + + /* The bdindex always points to the first free_head in tx_bdrings */ + if (instance_p.config.caps & FXMAC_CAPS_TAILPTR) != 0 + { + bdindex = FXMAC_BD_TO_INDEX(txring, txbd as u64); + } + + // The used bit for the 1st BD should be cleared at the end after clearing out used bits for other fragments. + let mut txbd = txbdset; + let FXMAC_BD_CLEAR_TX_USED = |bd_ptr: u64| + fxmac_bd_write(bd_ptr, FXMAC_BD_STAT_OFFSET, fxmac_bd_read(bd_ptr, FXMAC_BD_STAT_OFFSET) & (!FXMAC_TXBUF_USED_MASK)); + + for q in 1..p.len() { + txbd = FXMAC_BD_RING_NEXT(txring, txbd); + FXMAC_BD_CLEAR_TX_USED(txbd as u64); + crate::utils::DSB(); + } + FXMAC_BD_CLEAR_TX_USED(txbdset as u64); // 最后清第一个BD + crate::utils::DSB(); + + status = FXmacBdRingToHw(txring, n_pbufs, txbdset); + + let FXMAC_TAIL_QUEUE = |queue: u64| 0x0e80 + (queue << 2); + if (instance_p.config.caps & FXMAC_CAPS_TAILPTR) != 0 + { + write_reg((instance_p.config.base_address + FXMAC_TAIL_QUEUE(0)) as *mut u32, (1 << 31) | bdindex); + } + + // Start transmit + let value = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32) | FXMAC_NWCTRL_STARTTX_MASK; + write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, value); + + 0 +} + +/// 收包函数 +pub fn FXmacRecvHandler(instance_p: &mut FXmac) { + let mut recv_packets = Vec::new(); + + let mut rxbdset: *mut FXmacBd = null_mut(); + //let rxring: &mut FXmacBdRing = &mut (instance_p.rx_bd_queue.bdring); + + /* If Reception done interrupt is asserted, call RX call back function + to handle the processed BDs and then raise the according flag.*/ + let regval: u32 = read_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *const u32); + write_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, regval); + + loop { + // Returns a set of BD(s) that have been processed by hardware. + let bd_processed: u32 = FXmacBdRingFromHwRx(&mut instance_p.rx_bd_queue.bdring, FXMAX_RX_PBUFS_LENGTH, rxbdset); + assert!(!rxbdset.is_null()); + if bd_processed <= 0 { - FCacheDCacheInvalidateRange((uintptr)p->payload, (uintptr)FXMAC_MAX_FRAME_SIZE_JUMBO); + break; } - else + + let mut curbdptr: *mut FXmacBd = rxbdset; + for k in 0..bd_processed { + + let rxring: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; + + // Adjust the buffer size to the actual number of bytes received. + let rx_bytes: u32 = + if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 + { + //FXMAC_GET_RX_FRAME_SIZE(curbdptr) + fxmac_bd_read(curbdptr as u64, FXMAC_BD_STAT_OFFSET) & 0x00003FFF + + } else { + //FXMAC_BD_GET_LENGTH(curbdptr) + fxmac_bd_read(curbdptr as u64, FXMAC_BD_STAT_OFFSET) & FXMAC_RXBUF_LEN_MASK + + }; + + let bdindex: u32 = FXMAC_BD_TO_INDEX(rxring, curbdptr as u64); + let mbuf = unsafe { from_raw_parts_mut(instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize] as *mut u8, rx_bytes as usize) }; + info!("RX PKT {} <<<<<<<<<", rx_bytes); + + // Copy mbuf into a new Vec + recv_packets.push(mbuf.to_vec()); + + /* + The value of hash_match indicates the hash result of the received packet + 0: No hash match + 1: Unicast hash match + 2: Multicast hash match + 3: Reserved, the value is not legal + */ + // FXMAC_BD_GET_HASH_MATCH(bd_ptr) + let mut hash_match: u32 = (fxmac_bd_read(curbdptr as u64, FXMAC_BD_STAT_OFFSET) & FXMAC_RXBUF_HASH_MASK) >> 29; + debug!("hash_match is {:#x}", hash_match); + + // Invalidate RX frame before queuing to handle + // L1 cache prefetch conditions on any architecture. + crate::utils::FCacheDCacheInvalidateRange(instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize], rx_bytes as u64); + + /* store it in the receive queue, + * where it'll be processed by a different handler + */ + //if (FXmacPqEnqueue(&instance_p->recv_q, (void *)p) < 0) + + //instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize] = 0; + + // Just clear 64 bits header + mbuf[..min(64, rx_bytes as usize)].fill(0); + + curbdptr = FXMAC_BD_RING_NEXT(rxring, curbdptr); + } + + // free up the BD's + FXmacBdRingFree(&mut instance_p.rx_bd_queue.bdring, bd_processed); + + SetupRxBds(instance_p); + } + +} + +pub fn SetupRxBds(instance_p: &mut FXmac) { + let rxring: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; + + let mut status: u32 = 0; + let mut rxbd: *mut FXmacBd = null_mut(); + + // let alloc_tx_buffer_pages: usize = ((TX_RING_SIZE * MBUF_SIZE) + (PAGE_SIZE - 1)) / PAGE_SIZE; + // let alloc_rx_buffer_pages: usize = ((RX_RING_SIZE * MBUF_SIZE) + (PAGE_SIZE - 1)) / PAGE_SIZE; + + let mut freebds: u32 = rxring.free_cnt; + + while freebds > 0 { + freebds -= 1; + + let max_frame_size = if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 { + FXMAC_MAX_FRAME_SIZE_JUMBO + } else { + FXMAC_MAX_FRAME_SIZE + }; + let alloc_rx_buffer_pages: usize = (max_frame_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; + + let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = crate::utils::dma_alloc_coherent(alloc_rx_buffer_pages); + + status = FXmacBdRingAlloc(rxring, 1, rxbd); + + status = FXmacBdRingToHw(rxring, 1, rxbd); + + + crate::utils::FCacheDCacheInvalidateRange(rx_mbufs_vaddr as u64, max_frame_size as u64); + + let bdindex: u32 = FXMAC_BD_TO_INDEX(rxring, rxbd as u64); + let mut temp = rxbd as *mut u32; + let mut v = 0; + if bdindex == (FXMAX_RX_PBUFS_LENGTH - 1) as u32 { + // Mask last descriptor in receive buffer list + v = 0x2; + } + unsafe { + temp.write_volatile(v); + // Clear word 1 in descriptor + temp.add(1).write_volatile(0); + } + crate::utils::DSB(); + + // 设置BD的地址字段(word 0) + fxmac_bd_set_address_rx(rxbd as u64, rx_mbufs_dma as u64); + + instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize] = rx_mbufs_vaddr as u64; + } +} + +pub fn ethernetif_input_to_recv_packets(instance_p: &mut FXmac) +{ + if(instance_p.lwipport.recv_flg > 0) { - FCacheDCacheInvalidateRange((uintptr)p->payload, (uintptr)FXMAC_MAX_FRAME_SIZE); + info!("ethernetif_input_to_recv_packets, fxmac_port->recv_flg={}", instance_p.lwipport.recv_flg); + + // 也许需要屏蔽中断的临界区来保护 + instance_p.lwipport.recv_flg -= 1; + + write_reg((instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, instance_p.mask); + + FXmacRecvHandler(instance_p); } - FXMAC_BD_SET_ADDRESS_RX(rxbd, (uintptr)p->payload); - instance_p->buffer.rx_pbufs_storage[bdindex] = (uintptr)p; + { + // move received packet into a new pbuf + //p = low_level_input(netif); + // IP or ARP packet + // full packet send to tcpip thread to process } - - FXmacSetQueuePtr(&(instance_p->instance), instance_p->instance.tx_bd_queue.bdring.phys_base_addr, 0, (u16)FXMAC_SEND); - FXmacSetQueuePtr(&(instance_p->instance), instance_p->instance.rx_bd_queue.bdring.phys_base_addr, 0, (u16)FXMAC_RECV); - - if ((instance_p->instance).config.caps & FXMAC_CAPS_TAILPTR) - { - FXMAC_WRITEREG32((instance_p->instance).config.base_address, FXMAC_TAIL_QUEUE(0), BIT(31)|0); +} + +/// FXmacBdRingFree, Frees a set of BDs that had been previously retrieved with +pub fn FXmacBdRingFree(ring_ptr: &mut FXmacBdRing, num_bd: u32) -> u32 { + + // if no bds to process, simply return. + if 0 == num_bd { + 0 + } else { + /* Update pointers and counters */ + ring_ptr.free_cnt += num_bd; + ring_ptr.post_cnt -= num_bd; + FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr.post_head, num_bd); + + 0 } +} + +/// Reset Tx and Rx DMA pointers after FXmacStop +pub fn ResetDma(instance_p: &mut FXmac) +{ + info!("Resetting DMA"); + + let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; + let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; - return 0; + FXmacBdringPtrReset(txringptr, &instance_p.lwipport.buffer.tx_bdspace as *const _ as *mut FXmacBd); + FXmacBdringPtrReset(rxringptr, &instance_p.lwipport.buffer.rx_bdspace as *const _ as *mut FXmacBd); + + FXmacSetQueuePtr(instance_p.tx_bd_queue.bdring.phys_base_addr, 0, FXMAC_SEND); + FXmacSetQueuePtr(instance_p.rx_bd_queue.bdring.phys_base_addr, 0, FXMAC_RECV); } -/* Reset Tx and Rx DMA pointers after FXmacStop */ -void ResetDma(FXmacLwipPort *instance_p) +/// Handle DMA interrupt error +pub fn FXmacHandleDmaTxError(instance_p: &mut FXmac) { - FXMAC_LWIP_PORT_XMAC_PRINT_I("%s,\r\n", __func__); + panic!("Failed to handle DMA interrupt error"); + /* + FreeTxRxPbufs(instance_p); + FXmacCfgInitialize(&instance_p->instance, &instance_p->instance.config); // -> FXmacReset() - FXmacBdRing *txringptr = &FXMAC_GET_TXRING(instance_p->instance); - FXmacBdRing *rxringptr = &FXMAC_GET_RXRING(instance_p->instance); + /* initialize the mac */ + FXmacInitOnError(instance_p); /* need to set mac filter address */ + let mut dmacrreg: u32 = read_reg((instance_p.config.base_address + FXMAC_DMACR_OFFSET) as *const u32); + dmacrreg = dmacrreg | FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK; /* force_discard_on_err */ + write_reg((instance_p.config.base_address + FXMAC_DMACR_OFFSET) as *mut u32, dmacrreg); + FXmacSetupIsr(instance_p); + FXmacInitDma(instance_p); - FXmacBdringPtrReset(txringptr, instance_p->buffer.tx_bdspace); - FXmacBdringPtrReset(rxringptr, instance_p->buffer.rx_bdspace); + FXmacStart(&instance_p->instance); + */ +} + +pub fn FXmacHandleTxErrors(instance_p: &mut FXmac) +{ + let mut netctrlreg: u32 = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + netctrlreg = netctrlreg & !FXMAC_NWCTRL_TXEN_MASK; + write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, netctrlreg); + FreeOnlyTxPbufs(instance_p); + + CleanDmaTxdescs(instance_p); + netctrlreg = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + netctrlreg = netctrlreg | FXMAC_NWCTRL_TXEN_MASK; + write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, netctrlreg); +} + +fn CleanDmaTxdescs(instance_p: &mut FXmac) +{ + info!("Clean DMA TX DESCs"); + let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; + + let mut bdtemplate: FXmacBd = [0; FXMAC_BD_NUM_WORDS]; + + //FXMAC_BD_SET_STATUS(&bdtemplate, FXMAC_TXBUF_USED_MASK); + fxmac_bd_write((&mut bdtemplate as *mut _ as u64), FXMAC_BD_STAT_OFFSET, + fxmac_bd_read((&mut bdtemplate as *mut _ as u64), FXMAC_BD_STAT_OFFSET) | (FXMAC_TXBUF_USED_MASK)); + + let tx_bdspace_ptr = &mut instance_p.lwipport.buffer.tx_bdspace as *mut _ as u64; + FXmacBdRingCreate(txringptr, tx_bdspace_ptr, tx_bdspace_ptr, BD_ALIGNMENT, FXMAX_TX_BDSPACE_LENGTH as u32); + FXmacBdRingClone(txringptr, &mut bdtemplate, FXMAC_SEND); +} + +fn FreeOnlyTxPbufs(instance_p: &mut FXmac) +{ + for index in 0..FXMAX_TX_PBUFS_LENGTH + { + if (instance_p.lwipport.buffer.tx_pbufs_storage[index] != 0) + { + let pbuf = instance_p.lwipport.buffer.tx_pbufs_storage[index]; + let pages = (FXMAC_MAX_FRAME_SIZE as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; + crate::utils::dma_free_coherent(pbuf as usize, pages); - FXmacSetQueuePtr(&(instance_p->instance), instance_p->instance.tx_bd_queue.bdring.phys_base_addr, 0, (u16)FXMAC_SEND); - FXmacSetQueuePtr(&(instance_p->instance), instance_p->instance.rx_bd_queue.bdring.phys_base_addr, 0, (u16)FXMAC_RECV); + instance_p.lwipport.buffer.tx_pbufs_storage[index] = 0; + } + } } +/// FXmacProcessSentBds, 释放发送队列q参数 +pub fn FXmacProcessSentBds(instance_p: &mut FXmac) +{ + let txring: &mut FXmacBdRing = &mut (instance_p.tx_bd_queue.bdring); + let txbdset: *mut FXmacBd = null_mut(); + loop { + /* obtain processed BD's */ + let n_bds: u32 = FXmacBdRingFromHwTx(txring, FXMAX_TX_PBUFS_LENGTH, txbdset); + if n_bds == 0 { + error!("FXmacProcessSentBds have not found BD"); + return; + } + /* free the processed BD's */ + let mut n_pbufs_freed: u32 = n_bds; + let mut curbdpntr: *mut FXmacBd = txbdset; + while n_pbufs_freed > 0 { + let bdindex = FXMAC_BD_TO_INDEX(txring, curbdpntr as u64) as usize; + + let mut v = 0; + if bdindex == (FXMAX_TX_PBUFS_LENGTH - 1) { + v = 0xC0000000; /* Word 1 ,used/Wrap – marks last descriptor in transmit buffer descriptor list.*/ + } else { + v = 0x80000000; /* Word 1 , Used – must be zero for GEM to read data to the transmit buffer.*/ + } + + let mut temp = curbdpntr as *mut u32; + // Word 0 + unsafe{ + temp.write_volatile(0); + temp.add(1).write_volatile(v); + } + crate::utils::DSB(); + + let pbuf = instance_p.lwipport.buffer.tx_pbufs_storage[bdindex]; + if pbuf != 0 { + // pbuf_free(p); + // ?todo xiaoluoyuan@163.com + let pages = (FXMAC_MAX_FRAME_SIZE as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; + // Deallocate DMA memory by virtual address + crate::utils::dma_free_coherent(pbuf as usize, pages); + } + + instance_p.lwipport.buffer.tx_pbufs_storage[bdindex] = 0; + + let b = curbdpntr; + curbdpntr = FXMAC_BD_RING_NEXT(txring, curbdpntr); -static void FXmacSetupIsr(FXmacLwipPort *instance_p) + assert!(curbdpntr as usize != b as usize); + + n_pbufs_freed -= 1; + crate::utils::DSB(); + } + + FXmacBdRingFree(txring, n_bds); + } +} + +pub fn FXmacSendHandler(instance: &mut FXmac) { - FXMAC_LWIP_PORT_XMAC_PRINT_I("%s,\r\n", __func__); + //let txringptr: FXmacBdRing = instance.tx_bd_queue.bdring; + let regval: u32 = read_reg((instance.config.base_address + FXMAC_TXSR_OFFSET) as *const u32); - u32 cpu_id; - GetCpuId(&cpu_id); - InterruptSetTargetCpus(instance_p->instance.config.queue_irq_num[0], cpu_id); - /* Setup callbacks */ - FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_DMASEND, FXmacSendHandler, instance_p); - FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_DMARECV, FXmacRecvIsrHandler, instance_p); - FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_ERROR, FXmacErrorHandler, instance_p); - FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_LINKCHANGE, FXmacLinkChange, instance_p); + // 清除中断状态位来停止中断 + write_reg((instance.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, regval); - InterruptSetPriority(instance_p->instance.config.queue_irq_num[0], IRQ_PRIORITY_VALUE_12); - // setup interrupt handler - InterruptInstall(instance_p->instance.config.queue_irq_num[0], FXmacLwipPortIntrHandler, &instance_p->instance, "fxmac"); - InterruptUmask(instance_p->instance.config.queue_irq_num[0]); + // If Transmit done interrupt is asserted, process completed BD's + // 释放发送队列q参数 + FXmacProcessSentBds(instance); } -void CleanDmaTxdescs(FXmacLwipPort *instance_p) +pub fn FXmacLinkChange(instance: &mut FXmac) { - FXmacBd bdtemplate; - FXmacBdRing *txringptr; + if instance.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII + { + let mut link: u32 = 0; + let mut link_status: u32 = 0; + + let ctrl: u32 = read_reg((instance.config.base_address + FXMAC_PCS_AN_LP_OFFSET) as *const u32); + let link: u32 = (ctrl & FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_STATUS) >> FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_OFFSET; + + match link { + 0 => { + info!("link status is down"); + link_status = FXMAC_LINKDOWN; + } + 1 => { + info!("link status is up"); + link_status = FXMAC_LINKUP; + } + _ => { + error!("link status is error {:#x}", link); + } + } - FXMAC_LWIP_PORT_XMAC_PRINT_I("%s,\r\n", __func__); + if link_status == FXMAC_LINKUP + { + if link_status != instance.link_status + { + instance.link_status = FXMAC_NEGOTIATING; + info!("need NEGOTIATING"); + } + } + else + { + instance.link_status = FXMAC_LINKDOWN; + } + } +} - txringptr = &FXMAC_GET_TXRING((instance_p->instance)); - FXMAC_BD_CLEAR(&bdtemplate); - FXMAC_BD_SET_STATUS(&bdtemplate, FXMAC_TXBUF_USED_MASK); +/* phy */ - FXmacBdRingCreate(txringptr, (uintptr)instance_p->buffer.tx_bdspace, - (uintptr)instance_p->buffer.tx_bdspace, BD_ALIGNMENT, - sizeof(instance_p->buffer.tx_bdspace)); +/** + * @name: phy_link_detect + * @msg: 获取当前link status + * @note: + * @param {FXmac} *fxmac_p + * @param {u32} phy_addr + * @return {*} 1 is link up , 0 is link down + */ +pub fn phy_link_detect(xmac_p: &mut FXmac, phy_addr: u32) -> u32 +{ + let mut status: u16 = 0; - FXmacBdRingClone(txringptr, &bdtemplate, FXMAC_SEND); + // Read Phy Status register twice to get the confirmation of the current link status. + let mut ret: u32 = FXmacPhyRead(xmac_p, phy_addr, PHY_STATUS_REG_OFFSET, &mut status); + + if status & PHY_STAT_LINK_STATUS != 0 { + return 1; + } + + 0 } +pub fn phy_autoneg_status(xmac_p: &mut FXmac, phy_addr: u32) -> u32 +{ + let mut status: u16 = 0; + + // Read Phy Status register twice to get the confirmation of the current link status. + FXmacPhyRead(xmac_p, phy_addr, PHY_STATUS_REG_OFFSET, &mut status); + + + if status & PHY_STATUS_AUTONEGOTIATE_COMPLETE != 0 { + return 1; + } + + 0 +} diff --git a/src/fxmac_intr.rs b/src/fxmac_intr.rs new file mode 100644 index 000000000..e184c8029 --- /dev/null +++ b/src/fxmac_intr.rs @@ -0,0 +1,438 @@ +use core::sync::atomic::AtomicPtr; +use core::sync::atomic::Ordering; + +use alloc::boxed::Box; + +use crate::fxmac_const::*; +use crate::fxmac::*; +use crate::fxmac_dma::*; + +/* XMAC */ +pub const FXMAC_NUM:u32 = 4; +pub const FXMAC0_ID:u32 = 0; +pub const FXMAC1_ID:u32 = 1; +pub const FXMAC2_ID:u32 = 2; +pub const FXMAC3_ID:u32 = 3; +pub const FXMAC0_BASE_ADDR:u32 = 0x3200C000; +pub const FXMAC1_BASE_ADDR:u32 = 0x3200E000; +pub const FXMAC2_BASE_ADDR:u32 = 0x32010000; +pub const FXMAC3_BASE_ADDR:u32 = 0x32012000; +pub const FXMAC0_MODE_SEL_BASE_ADDR:u32 = 0x3200DC00; +pub const FXMAC0_LOOPBACK_SEL_BASE_ADDR:u32 = 0x3200DC04; +pub const FXMAC1_MODE_SEL_BASE_ADDR:u32 = 0x3200FC00; +pub const FXMAC1_LOOPBACK_SEL_BASE_ADDR:u32 = 0x3200FC04; +pub const FXMAC2_MODE_SEL_BASE_ADDR:u32 = 0x32011C00; +pub const FXMAC2_LOOPBACK_SEL_BASE_ADDR:u32 = 0x32011C04; +pub const FXMAC3_MODE_SEL_BASE_ADDR:u32 = 0x32013C00; +pub const FXMAC3_LOOPBACK_SEL_BASE_ADDR:u32 = 0x32013C04; +pub const FXMAC0_PCLK:u32 = 50000000; +pub const FXMAC1_PCLK:u32 = 50000000; +pub const FXMAC2_PCLK:u32 = 50000000; +pub const FXMAC3_PCLK:u32 = 50000000; +pub const FXMAC0_HOTPLUG_IRQ_NUM:u32 = (53 + 30); +pub const FXMAC1_HOTPLUG_IRQ_NUM:u32 = (54 + 30); +pub const FXMAC2_HOTPLUG_IRQ_NUM:u32 = (55 + 30); +pub const FXMAC3_HOTPLUG_IRQ_NUM:u32 = (56 + 30); +pub const FXMAC_QUEUE_MAX_NUM:u32 = 16; // #define FXMAC_QUEUE_MAX_NUM 16U +pub const FXMAC0_QUEUE0_IRQ_NUM:u32 = (57 + 30); +pub const FXMAC0_QUEUE1_IRQ_NUM:u32 = (58 + 30); +pub const FXMAC0_QUEUE2_IRQ_NUM:u32 = (59 + 30); +pub const FXMAC0_QUEUE3_IRQ_NUM:u32 = (60 + 30); +pub const FXMAC0_QUEUE4_IRQ_NUM:u32 = (30 + 30); +pub const FXMAC0_QUEUE5_IRQ_NUM:u32 = (31 + 30); +pub const FXMAC0_QUEUE6_IRQ_NUM:u32 = (32 + 30); +pub const FXMAC0_QUEUE7_IRQ_NUM:u32 = (33 + 30); +pub const FXMAC1_QUEUE0_IRQ_NUM:u32 = (61 + 30); +pub const FXMAC1_QUEUE1_IRQ_NUM:u32 = (62 + 30); +pub const FXMAC1_QUEUE2_IRQ_NUM:u32 = (63 + 30); +pub const FXMAC1_QUEUE3_IRQ_NUM:u32 = (64 + 30); +pub const FXMAC2_QUEUE0_IRQ_NUM:u32 = (66 + 30); +pub const FXMAC2_QUEUE1_IRQ_NUM:u32 = (67 + 30); +pub const FXMAC2_QUEUE2_IRQ_NUM:u32 = (68 + 30); +pub const FXMAC2_QUEUE3_IRQ_NUM:u32 = (69 + 30); +pub const FXMAC3_QUEUE0_IRQ_NUM:u32 = (70 + 30); +pub const FXMAC3_QUEUE1_IRQ_NUM:u32 = (71 + 30); +pub const FXMAC3_QUEUE2_IRQ_NUM:u32 = (72 + 30); +pub const FXMAC3_QUEUE3_IRQ_NUM:u32 = (73 + 30); +//pub const FXMAC_PHY_MAX_NUM:u32 = 32; +// #define FXMAC_CLK_TYPE_0 + +pub fn FXmacErrorHandler(instance_p: &mut FXmac, direction: u8, error_word: u32) +{ + info!("FXmacErrorHandler, direction={}, error_word={}", direction, error_word); + if error_word != 0 + { + match direction as u32 + { + FXMAC_RECV => { + if (error_word & FXMAC_RXSR_HRESPNOK_MASK) != 0 + { + error!("Receive DMA error"); + FXmacHandleDmaTxError(instance_p); + } + if (error_word & FXMAC_RXSR_RXOVR_MASK) != 0 + { + error!("Receive over run"); + FXmacRecvHandler(instance_p); + } + if (error_word & FXMAC_RXSR_BUFFNA_MASK) != 0 + { + error!("Receive buffer not available"); + FXmacRecvHandler(instance_p); + } + } + FXMAC_SEND => { + if (error_word & FXMAC_TXSR_HRESPNOK_MASK) != 0 + { + error!("Transmit DMA error"); + FXmacHandleDmaTxError(instance_p); + } + if (error_word & FXMAC_TXSR_URUN_MASK) != 0 + { + error!("Transmit under run"); + FXmacHandleTxErrors(instance_p); + } + if (error_word & FXMAC_TXSR_BUFEXH_MASK) != 0 + { + error!("Transmit buffer exhausted"); + FXmacHandleTxErrors(instance_p); + } + if (error_word & FXMAC_TXSR_RXOVR_MASK) != 0 + { + error!("Transmit retry excessed limits"); + FXmacHandleTxErrors(instance_p); + } + if (error_word & FXMAC_TXSR_FRAMERX_MASK) != 0 + { + error!("Transmit collision"); + FXmacProcessSentBds(instance_p); + } + } + _ => { error!("FXmacErrorHandler failed, unknown direction={}", direction); } + } + } +} + +pub fn FXmacRecvIsrHandler(instance: &mut FXmac) { + info!("Recv Isr"); + write_reg((instance.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_RXCOMPL_MASK); + // instance_p->recv_flg ++; + + ethernetif_input_to_recv_packets(instance); +} + +pub static XMAC: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + +pub fn xmac_intr_handler(intr: u64) { + + info!("Handling xmac intr ..."); + let xmac = XMAC.load(Ordering::Relaxed); +if !xmac.is_null() { + let xmac_ptr = unsafe{ &mut (*xmac)}; + // maybe irq num + let vector = xmac_ptr.config.queue_irq_num[0] ; + FXmacIntrHandler(vector as i32, xmac_ptr); + info!("xmac intr is already handled"); +} else { + error!("static FXmac has not been initialized"); +} + +} + +/// FXmacIntrHandler, 中断处理函数. vector是中断号, 目前中断只支持单queue的情况 +/// FXMAC_HANDLER_DMARECV 接收中断 => instance_p->recv_irq_handler = FXmacRecvIsrHandler; +/// FXMAC_HANDLER_DMASEND 发送中断 => instance_p->send_irq_handler = FXmacSendHandler; +/// FXMAC_HANDLER_ERROR 异常中断 => instance_p->error_irq_handler = FXmacErrorHandler; +/// FXMAC_HANDLER_LINKCHANGE 连接状态 => instance_p->link_change_handler = FXmacLinkChange; +/// +pub fn FXmacIntrHandler(vector: i32, instance_p: &mut FXmac) { + assert!(instance_p.is_ready == FT_COMPONENT_IS_READY as u32); + + // 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number + let tx_queue_id = instance_p.tx_bd_queue.queue_id; + // 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number + let rx_queue_id = instance_p.rx_bd_queue.queue_id; + + assert!((rx_queue_id < FXMAC_QUEUE_MAX_NUM) && (tx_queue_id < FXMAC_QUEUE_MAX_NUM)); + + /* This ISR will try to handle as many interrupts as it can in a single + * call. However, in most of the places where the user's error handler + * is called, this ISR exits because it is expected that the user will + * reset the device in nearly all instances. + */ + let mut reg_isr: u32 = read_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *const u32); + + info!("<<<<<<<<< IRQ num vector={}, Interrupt Status ISR={:#x}, tx_queue_id={}, rx_queue_id={}", vector, reg_isr, tx_queue_id, rx_queue_id); + + if vector as u32 == instance_p.config.queue_irq_num[tx_queue_id as usize] { + if tx_queue_id == 0 + { + if (reg_isr & FXMAC_IXR_TXCOMPL_MASK) != 0 + { + // Clear TX status register TX complete indication but preserve error bits if there is any + write_reg((instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, FXMAC_TXSR_TXCOMPL_MASK | FXMAC_TXSR_USEDREAD_MASK); + + FXmacSendHandler(instance_p); + + /* add */ + if(instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 + { + write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_TXCOMPL_MASK); + + } + } + + /* Transmit error conditions interrupt */ + if ((reg_isr & FXMAC_IXR_TX_ERR_MASK) != 0) && + ((reg_isr & FXMAC_IXR_TXCOMPL_MASK) == 0) + { + /* Clear TX status register */ + let reg_txsr: u32 = read_reg((instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *const u32); + + write_reg((instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, reg_txsr); + + FXmacErrorHandler(instance_p, FXMAC_SEND as u8, reg_txsr); + + /* add */ + if(instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 + { + write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_TX_ERR_MASK); + } + } + + /* add restart */ + if (reg_isr & FXMAC_IXR_TXUSED_MASK) != 0 + { + /* add */ + if(instance_p.caps& FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 + { + write_reg((instance_p.config.base_address+FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_TXUSED_MASK); + + } + + /* + if (instance_p->restart_handler) + { + instance_p->restart_handler(instance_p->restart_args); + } + */ + } + + /* link changed */ + if (reg_isr & FXMAC_IXR_LINKCHANGE_MASK) != 0 + { + + FXmacLinkChange(instance_p); + + if(instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 + { + write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_LINKCHANGE_MASK); + } + } + } + else /* use queue number more than 0 */ + { + reg_isr = read_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id)) as *const u32); + + /* Transmit Q1 complete interrupt */ + if ((reg_isr & FXMAC_INTQUESR_TXCOMPL_MASK) != 0) + { + /* Clear TX status register TX complete indication but preserve + * error bits if there is any */ + write_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id)) as *mut u32, FXMAC_INTQUESR_TXCOMPL_MASK); + write_reg((instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, (FXMAC_TXSR_TXCOMPL_MASK | FXMAC_TXSR_USEDREAD_MASK) as u32); + + FXmacSendHandler(instance_p); + } + + /* Transmit Q1 error conditions interrupt */ + if (((reg_isr & FXMAC_INTQ1SR_TXERR_MASK) != 0) && + ((reg_isr & FXMAC_INTQ1SR_TXCOMPL_MASK) != 0)) + { + /* Clear Interrupt Q1 status register */ + write_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id)) as *mut u32, reg_isr); + + FXmacErrorHandler(instance_p, FXMAC_SEND as u8, reg_isr); + } + } + } + + if vector as u32 == instance_p.config.queue_irq_num[rx_queue_id as usize] + { + if rx_queue_id == 0 + { + /* Receive complete interrupt */ + if (reg_isr & FXMAC_IXR_RXCOMPL_MASK) != 0 + { + /* Clear RX status register RX complete indication but preserve + * error bits if there is any */ + write_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, (FXMAC_RXSR_FRAMERX_MASK | FXMAC_RXSR_BUFFNA_MASK) as u32); + FXmacRecvIsrHandler(instance_p); + + /* add */ + if(instance_p.caps& FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 + { + write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_RXCOMPL_MASK); + } + } + + /* Receive error conditions interrupt */ + if ((reg_isr & FXMAC_IXR_RX_ERR_MASK) != 0) + { + /* Clear RX status register */ + let mut reg_rxsr: u32 = read_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *const u32); + write_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, reg_rxsr); + + if ((reg_isr & FXMAC_IXR_RXUSED_MASK) != 0) + { + let reg_ctrl: u32 = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + + let mut reg_temp: u32 = reg_ctrl | FXMAC_NWCTRL_FLUSH_DPRAM_MASK as u32; + reg_temp &= (!FXMAC_NWCTRL_RXEN_MASK) as u32; + write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_temp); + + /* add */ + reg_temp = reg_ctrl | FXMAC_NWCTRL_RXEN_MASK as u32; + write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_temp); + + if(instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 + { + write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_RXUSED_MASK); + } + } + + /* add */ + if ((reg_isr & FXMAC_IXR_RXOVR_MASK) != 0) + { + if(instance_p.caps& FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 + { + write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_RXOVR_MASK); + } + } + + /* add */ + if ((reg_isr & FXMAC_IXR_HRESPNOK_MASK) != 0) + { + if(instance_p.caps& FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 + { + write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_HRESPNOK_MASK); + } + } + + + if (reg_rxsr != 0) + { + FXmacErrorHandler(instance_p, FXMAC_RECV as u8, reg_rxsr); + } + } + } + else /* use queue number more than 0 */ + { + reg_isr = read_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)) as *const u32); + + /* Receive complete interrupt */ + if ((reg_isr & FXMAC_INTQUESR_RCOMP_MASK) != 0) + { + /* Clear RX status register RX complete indication but preserve + * error bits if there is any */ + write_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)) as *mut u32, FXMAC_INTQUESR_RCOMP_MASK); + FXmacRecvIsrHandler(instance_p); + } + + /* Receive error conditions interrupt */ + if (reg_isr & FXMAC_IXR_RX_ERR_MASK) != 0 + { + + let mut reg_ctrl: u32 = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + reg_ctrl &= !(FXMAC_NWCTRL_RXEN_MASK as u32); + + write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_ctrl); + + /* Clear RX status register */ + let mut reg_rxsr = read_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET)as *const u32); + write_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, reg_rxsr); + + if ((reg_isr & FXMAC_IXR_RXUSED_MASK) != 0) + { + reg_ctrl = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + reg_ctrl |= FXMAC_NWCTRL_FLUSH_DPRAM_MASK; + + write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_ctrl); + } + + /* Clear RX status register RX complete indication but preserve + * error bits if there is any */ + write_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)) as *mut u32, FXMAC_INTQUESR_RXUBR_MASK); + FXmacRecvIsrHandler(instance_p); + + if reg_rxsr != 0 + { + FXmacErrorHandler(instance_p, FXMAC_RECV as u8, reg_rxsr); + } + } + } + } + } + + /** + * @name: FXmacQueueIrqDisable + * @msg: Disable queue irq + * @param {FXmac} *instance_p a pointer to the instance to be worked on. + * @param {u32} queue_num queue number + * @param {u32} mask is interrupt disable value mask + */ + pub fn FXmacQueueIrqDisable(instance_p: &mut FXmac, queue_num: u32, mask: u32) + { + assert!(instance_p.is_ready == FT_COMPONENT_IS_READY as u32); + assert!(instance_p.config.max_queue_num > queue_num); + + if queue_num == 0 { + write_reg((instance_p.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, mask & FXMAC_IXR_ALL_MASK); + + } else { + write_reg((instance_p.config.base_address + FXMAC_INTQX_IDR_SIZE_OFFSET(queue_num as u64)) as *mut u32, mask & FXMAC_IXR_ALL_MASK); + + } + } + + /// FXmacQueueIrqEnable, Enable queue irq + pub fn FXmacQueueIrqEnable(instance_p: &mut FXmac, queue_num: u32, mask: u32) + { + assert!(instance_p.is_ready == FT_COMPONENT_IS_READY as u32); + assert!(instance_p.config.max_queue_num > queue_num); + + if queue_num == 0 { + write_reg((instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, mask & FXMAC_IXR_ALL_MASK); + + } else { + write_reg((instance_p.config.base_address + FXMAC_INTQX_IER_SIZE_OFFSET(queue_num as u64)) as *mut u32, mask & FXMAC_IXR_ALL_MASK); + + } + } + + /// 网卡中断设置 +pub fn FXmacSetupIsr(instance: &mut FXmac) +{ + // 获取当前CPU ID: 0, 1, 2, 3, 4, 5, 6, 7 + //let cpu_id: u32 = get_cpu_id(); + // 路由中断到指定的cpu,或所有的cpu + + // Setup callbacks, 为指定类型设置回调函数 + /* + FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_DMASEND, FXmacSendHandler, instance_p); + FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_DMARECV, FXmacRecvIsrHandler, instance_p); + FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_ERROR, FXmacErrorHandler, instance_p); + FXmacSetHandler(&instance_p->instance, FXMAC_HANDLER_LINKCHANGE, FXmacLinkChange, instance_p); + */ + + //let IRQ_PRIORITY_VALUE_0 = 0x0; + //let IRQ_PRIORITY_VALUE_12 = 0xc; + // 设置中断优先级为IRQ_PRIORITY_VALUE_12 + + // setup interrupt handler, 该函数将自定义中断回调函数注册到对应的中断ID + // 使能对应中断 + let irq_num = instance.config.queue_irq_num[0] as usize; + info!("register callback function for irq: {}", irq_num); + crate::utils::dma_request_irq(irq_num, xmac_intr_handler); + +} \ No newline at end of file diff --git a/src/fxmac_phy.rs b/src/fxmac_phy.rs index a9b58d46c..be374ef60 100644 --- a/src/fxmac_phy.rs +++ b/src/fxmac_phy.rs @@ -1,3 +1,60 @@ +use crate::fxmac_const::*; +use crate::fxmac::*; + +pub const PHY_CONTROL_REG_OFFSET: u32 = 0; +pub const PHY_STATUS_REG_OFFSET: u32 = 1; +pub const PHY_IDENTIFIER_1_REG: u32 = 2; +pub const PHY_IDENTIFIER_2_REG: u32 = 3; +pub const PHY_AUTONEGO_ADVERTISE_REG: u32 = 4; +pub const PHY_PARTNER_ABILITIES_1_REG_OFFSET: u32 = 5; +pub const PHY_PARTNER_ABILITIES_2_REG_OFFSET: u32 = 8; +pub const PHY_PARTNER_ABILITIES_3_REG_OFFSET: u32 = 10; +pub const PHY_1000_ADVERTISE_REG_OFFSET: u32 = 9; +pub const PHY_MMD_ACCESS_CONTROL_REG: u32 = 13; +pub const PHY_MMD_ACCESS_ADDRESS_DATA_REG: u32 = 14; +pub const PHY_SPECIFIC_STATUS_REG: u32 = 17; +pub const PHY_CONTROL_FULL_DUPLEX_MASK: u16 = 0x0100; +pub const PHY_CONTROL_LINKSPEED_MASK: u16 = 0x0040; +pub const PHY_CONTROL_LINKSPEED_1000M: u16 = 0x0040; +pub const PHY_CONTROL_LINKSPEED_100M: u16 = 0x2000; +pub const PHY_CONTROL_LINKSPEED_10M: u16 = 0x0000; +pub const PHY_CONTROL_RESET_MASK: u16 = 0x8000; +pub const PHY_CONTROL_LOOPBACK_MASK: u16 = 0x4000; +pub const PHY_CONTROL_AUTONEGOTIATE_ENABLE: u16 = 0x1000; +pub const PHY_CONTROL_AUTONEGOTIATE_RESTART: u16 = 0x0200; +pub const PHY_STATUS_AUTONEGOTIATE_COMPLETE: u16 = 0x0020; +pub const PHY_STAT_LINK_STATUS: u16 = 0x0004; +pub const PHY_AUTOADVERTISE_ASYMMETRIC_PAUSE_MASK: u16 = 0x0800; +pub const PHY_AUTOADVERTISE_PAUSE_MASK: u16 = 0x0400; +pub const PHY_AUTOADVERTISE_AUTONEG_ERROR_MASK: u16 = 0x8000; + +/* Advertisement control register. */ +pub const PHY_AUTOADVERTISE_10HALF: u16 = 0x0020; /* Try for 10mbps half-duplex */ +pub const PHY_AUTOADVERTISE_1000XFULL: u16 = 0x0020; /* Try for 1000BASE-X full-duplex */ +pub const PHY_AUTOADVERTISE_10FULL: u16 = 0x0040; /* Try for 10mbps full-duplex */ +pub const PHY_AUTOADVERTISE_1000XHALF: u16 = 0x0040; /* Try for 1000BASE-X half-duplex */ +pub const PHY_AUTOADVERTISE_100HALF: u16 = 0x0080; /* Try for 100mbps half-duplex */ +pub const PHY_AUTOADVERTISE_1000XPAUSE: u16 = 0x0080; /* Try for 1000BASE-X pause */ +pub const PHY_AUTOADVERTISE_100FULL: u16 = 0x0100; /* Try for 100mbps full-duplex */ +pub const PHY_AUTOADVERTISE_1000XPSE_ASYM: u16 = 0x0100; /* Try for 1000BASE-X asym pause */ +pub const PHY_AUTOADVERTISE_100BASE4: u16 = 0x0200; /* Try for 100mbps 4k packets */ +pub const PHY_AUTOADVERTISE_100_AND_10: u16 = (PHY_AUTOADVERTISE_10FULL | PHY_AUTOADVERTISE_100FULL | PHY_AUTOADVERTISE_10HALF | PHY_AUTOADVERTISE_100HALF); +pub const PHY_AUTOADVERTISE_100: u16 = (PHY_AUTOADVERTISE_100FULL | PHY_AUTOADVERTISE_100HALF); +pub const PHY_AUTOADVERTISE_10: u16 = (PHY_AUTOADVERTISE_10FULL | PHY_AUTOADVERTISE_10HALF); +pub const PHY_AUTOADVERTISE_1000: u16 = 0x0300; +pub const PHY_SPECIFIC_STATUS_SPEED_1000M: u16 = (2 << 14); +pub const PHY_SPECIFIC_STATUS_SPEED_100M: u16 = (1 << 14); +pub const PHY_SPECIFIC_STATUS_SPEED_0M: u16 = (0 << 14); + +pub const FT_SUCCESS: u32 = 0; +pub const XMAC_PHY_RESET_ENABLE: u32 = 1; +pub const XMAC_PHY_RESET_DISABLE: u32 = 0; +pub const FXMAC_PHY_AUTONEGOTIATION_DISABLE: u32 = 0; +pub const FXMAC_PHY_AUTONEGOTIATION_ENABLE: u32 = 1; +pub const FXMAC_PHY_MODE_HALFDUPLEX: u32 = 0; +pub const FXMAC_PHY_MODE_FULLDUPLEX: u32 = 1; +pub const FXMAC_PHY_MAX_NUM:u32 = 32; + /* * Write data to the specified PHY register. The Ethernet driver does not * require the device to be stopped before writing to the PHY. Although it is @@ -9,20 +66,20 @@ * Prior to PHY access with this function, the user should have setup the MDIO * clock with FXmacSetMdioDivisor(). */ -fn FXmacPhyWrite(instance_p &mut FXmac, phy_address: u32, register_num: u32, phy_data: u16) -> u32 +pub fn FXmacPhyWrite(instance_p: &mut FXmac, phy_address: u32, register_num: u32, phy_data: u16) -> u32 { - let mgtcr: mut u32 = 0; - let ipisr: mut u32 = 0; - let ip_write_temp: mut u32 = 0; - let status: mut u32 = 0; + let mut mgtcr: u32 = 0; + let mut ipisr: u32 = 0; + let mut ip_write_temp: u32 = 0; + let mut status: u32 = 0; debug!("FXmacPhyWrite, phy_address={:#x}, register_num={}, phy_data={:#x}", phy_address, register_num, phy_data); /* Make sure no other PHY operation is currently in progress */ - if (!(read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32) & - FXMAC_NWSR_MDIOIDLE_MASK)) == 1 + if (read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32) & + FXMAC_NWSR_MDIOIDLE_MASK) == 0 { - status = FXMAC_ERR_PHY_BUSY; + status = 6; // FXMAC_ERR_PHY_BUSY; error!("FXmacPhyRead error: PHY busy!"); }else{ /* Construct mgtcr mask for the operation */ @@ -42,23 +99,23 @@ fn FXmacPhyWrite(instance_p &mut FXmac, phy_address: u32, register_num: u32, phy } } - status = FT_SUCCESS; - } - + status = 0; // FT_SUCCESS; + } + status } -fn FXmacPhyRead(instance_p &mut FXmac, phy_address: u32, register_num: u32, phydat_aptr: &mut u16) -> u32 +pub fn FXmacPhyRead(instance_p: &mut FXmac, phy_address: u32, register_num: u32, phydat_aptr: &mut u16) -> u32 { - let mgtcr: mut u32 = 0; - let ipisr: mut u32 = 0; - let IpReadTemp: mut u32 = 0; - let status: mut u32 = 0; + let mut mgtcr: u32 = 0; + let mut ipisr: u32 = 0; + let mut IpReadTemp: u32 = 0; + let mut status: u32 = 0; /* Make sure no other PHY operation is currently in progress */ - if (!(read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32) & FXMAC_NWSR_MDIOIDLE_MASK)) == 1 + if (read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32) & FXMAC_NWSR_MDIOIDLE_MASK) == 0 { - status = FXMAC_ERR_PHY_BUSY; + status = 6; error!("FXmacPhyRead error: PHY busy!"); }else{ /* Construct mgtcr mask for the operation */ @@ -79,11 +136,12 @@ fn FXmacPhyRead(instance_p &mut FXmac, phy_address: u32, register_num: u32, phyd } // Read data - phydat_aptr = read_reg((instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *const u32) as u16; + *phydat_aptr = read_reg((instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *const u32) as u16; + debug!("FXmacPhyRead, phy_address={:#x}, register_num={}, phydat_aptr={:#x}", phy_address, register_num, phydat_aptr); - status = FT_SUCCESS; - } + status = 0; + } status } @@ -92,13 +150,14 @@ fn FXmacPhyRead(instance_p &mut FXmac, phy_address: u32, register_num: u32, phyd /// FXmacPhyInit /// Setup the PHYs for proper speed setting. pub fn FXmacPhyInit( - mut instance_p: &mut FXmac, - mut speed: u32, - mut duplex_mode: u32, - mut autonegotiation_en: u32, - mut reset_flag: u32, + instance_p: &mut FXmac, + reset_flag: u32, ) -> u32 { + let speed = instance_p.config.speed; + let duplex_mode = instance_p.config.duplex; + let autonegotiation_en = instance_p.config.auto_neg; info!("FXmacPhyInit, speed={}, duplex_mode={}, autonegotiation_en={}, reset_flag={}", + speed, duplex_mode, autonegotiation_en, reset_flag, @@ -107,7 +166,7 @@ pub fn FXmacPhyInit( let mut phy_addr: u32 = 0; if FXmacDetect(instance_p, &mut phy_addr) != 0 { error!("Phy is not found."); - return FXMAC_PHY_IS_NOT_FOUND; + return 7; //FXMAC_PHY_IS_NOT_FOUND; } info!("Setting phy addr is {}", phy_addr); instance_p.phy_address = phy_addr; @@ -131,15 +190,15 @@ pub fn FXmacPhyInit( } instance_p.link_status = FXMAC_LINKUP; - FT_SUCCESS + 0 //FT_SUCCESS } -fn FXmacDetect(instance_p: &mut FXmac, phy_addr_p: &mut u32) -> u32 +pub fn FXmacDetect(instance_p: &mut FXmac, phy_addr_p: &mut u32) -> u32 { - let phy_addr: mut u32 = 0; - let phy_reg: mut u16 = 0; - let phy_id1_reg: mut u16 = 0; - let phy_id2_reg: mut u16 = 0; + let mut phy_addr: u32 = 0; + let mut phy_reg: u16 = 0; + let mut phy_id1_reg: u16 = 0; + let mut phy_id2_reg: u16 = 0; for phy_addr in 0..FXMAC_PHY_MAX_NUM { @@ -153,29 +212,29 @@ fn FXmacDetect(instance_p: &mut FXmac, phy_addr_p: &mut u32) -> u32 if (phy_reg != 0xffff) { - ret = FXmacPhyRead(instance_p, phy_addr, PHY_IDENTIFIER_1_REG, &phy_id1_reg); - ret |= FXmacPhyRead(instance_p, phy_addr, PHY_IDENTIFIER_2_REG, &phy_id2_reg); + ret = FXmacPhyRead(instance_p, phy_addr, PHY_IDENTIFIER_1_REG, &mut phy_id1_reg); + ret |= FXmacPhyRead(instance_p, phy_addr, PHY_IDENTIFIER_2_REG, &mut phy_id2_reg); info!("Phy id1 reg is {:#x}, Phy id2 reg is {:#x}", phy_id1_reg , phy_id2_reg); if ((ret == FT_SUCCESS) && (phy_id2_reg != 0) && (phy_id2_reg != 0xffff) && (phy_id1_reg != 0xffff)) { *phy_addr_p = phy_addr; - phy_addr_b = phy_addr; + //phy_addr_b = phy_addr; info!("Phy addr is {:#x}", phy_addr); return FT_SUCCESS; } } } - FXMAC_PHY_IS_NOT_FOUND + 7 //FXMAC_PHY_IS_NOT_FOUND } /// FXmacPhyReset: Perform phy software reset - fn FXmacPhyReset(instance_p: &mut FXmac, phy_addr: u32) -> u32 +pub fn FXmacPhyReset(instance_p: &mut FXmac, phy_addr: u32) -> u32 { - let control: mut u16 = 0; + let mut control: u16 = 0; - let ret: mut u32 = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &mut control); if (ret != FT_SUCCESS) { error!("FXmacPhyReset, read PHY_CONTROL_REG_OFFSET is error"); @@ -193,7 +252,7 @@ fn FXmacDetect(instance_p: &mut FXmac, phy_addr_p: &mut u32) -> u32 loop { - ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &mut control); if (ret != FT_SUCCESS) { error!("FXmacPhyReset, read PHY_CONTROL_REG_OFFSET is error"); @@ -209,17 +268,17 @@ fn FXmacDetect(instance_p: &mut FXmac, phy_addr_p: &mut u32) -> u32 } -fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 +pub fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 { - let temp: mut u16 = 0; - let temp2: mut u16 = 0; - let control: mut u16 = 0; - let status: mut u16 = 0; - let negotitation_timeout_cnt: mut u32 = 0; + let mut temp: u16 = 0; + let mut temp2: u16 = 0; + let mut control: u16 = 0; + let mut status: u16 = 0; + let mut negotitation_timeout_cnt: u32 = 0; info!("Start phy auto negotiation."); - let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &mut control); if (ret != FT_SUCCESS) { error!("FXmacGetIeeePhySpeed,read PHY_CONTROL_REG_OFFSET is error"); @@ -236,10 +295,11 @@ fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 } info!("Waiting for phy to complete auto negotiation."); - do{ - FDriverMdelay(50); + loop{ + // 睡眠50毫秒 + crate::utils::msdelay(50); - ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &status); + ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &mut status); if (ret != FT_SUCCESS) { error!("FXmacGetIeeePhySpeed,read PHY_STATUS_REG_OFFSET is error"); @@ -247,10 +307,11 @@ fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 } - if (negotitation_timeout_cnt++ >= 0xff) + negotitation_timeout_cnt += 1; + if (negotitation_timeout_cnt >= 0xff) { error!("Auto negotiation is error."); - return FXMAC_PHY_AUTO_AUTONEGOTIATION_FAILED; + return 8;//FXMAC_PHY_AUTO_AUTONEGOTIATION_FAILED; } if (status & PHY_STATUS_AUTONEGOTIATE_COMPLETE) != 0 { @@ -260,7 +321,7 @@ fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 info!("Auto negotiation complete."); - ret = FXmacPhyRead(instance_p, phy_addr, PHY_SPECIFIC_STATUS_REG, &temp); + ret = FXmacPhyRead(instance_p, phy_addr, PHY_SPECIFIC_STATUS_REG, &mut temp); if (ret != FT_SUCCESS) { error!("FXmacGetIeeePhySpeed,read PHY_SPECIFIC_STATUS_REG is error"); @@ -268,7 +329,7 @@ fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 } info!("Temp is {:#x}", temp); - ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &temp2); + ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &mut temp2); if (ret != FT_SUCCESS) { error!("FXmacGetIeeePhySpeed,read PHY_STATUS_REG_OFFSET is error"); @@ -307,15 +368,15 @@ fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 FT_SUCCESS } -fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: u32, duplex_mode: u32) -> u32 +pub fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: u32, duplex_mode: u32) -> u32 { - let control: mut u16 = 0;; - let autonereg: mut u16 = 0;; - let specific_reg: mut u16 = 0; + let mut control: u16 = 0;; + let mut autonereg: u16 = 0;; + let mut specific_reg: u16 = 0; - info("Manual setting, phy_addr is {:#x},speed {}, duplex_mode is {}.", phy_addr, speed, duplex_mode); + info!("Manual setting, phy_addr is {:#x},speed {}, duplex_mode is {}.", phy_addr, speed, duplex_mode); - let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_AUTONEGO_ADVERTISE_REG, &autonereg); + let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_AUTONEGO_ADVERTISE_REG, &mut autonereg); if (ret != FT_SUCCESS) { error!("FXmacConfigureIeeePhySpeed, read PHY_AUTONEGO_ADVERTISE_REG is error."); @@ -331,7 +392,7 @@ fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: u32, return ret; } - ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &mut control); if (ret != FT_SUCCESS) { error!("FXmacConfigureIeeePhySpeed, read PHY_AUTONEGO_ADVERTISE_REG is error."); @@ -372,18 +433,19 @@ fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: u32, return ret; } - FDriverMdelay(1500); + //FDriverMdelay(1500); + crate::utils::msdelay(1500); info!("Manual selection completed."); - ret = FXmacPhyRead(instance_p, phy_addr, PHY_SPECIFIC_STATUS_REG, &specific_reg); + ret = FXmacPhyRead(instance_p, phy_addr, PHY_SPECIFIC_STATUS_REG, &mut specific_reg); if (ret != FT_SUCCESS) { error!("FXmacConfigureIeeePhySpeed, read PHY_SPECIFIC_STATUS_REG is error."); return ret; } - info!("Specific reg is 0x%x.", specific_reg); + info!("Specific reg is {:#x}", specific_reg); if (specific_reg & (1 << 13)) != 0 { diff --git a/src/lib.rs b/src/lib.rs index 10aa79ccf..c32f3fa67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,11 +8,16 @@ extern crate alloc; -mod mii_const; -mod macb_const; +#[macro_use] +extern crate log; +//mod mii_const; +mod fxmac_const; + +mod utils; mod fxmac_phy; mod fxmac_dma; +mod fxmac_intr; pub mod fxmac; #[cfg(test)] diff --git a/src/macb_const.rs b/src/macb_const.rs deleted file mode 100644 index 454c8b8a4..000000000 --- a/src/macb_const.rs +++ /dev/null @@ -1,894 +0,0 @@ -//cadence/macb.h -//Atmel MACB Ethernet Controller driver - -pub(crate) const MACB_GREGS_NBR: u32 = 16; -pub(crate) const MACB_GREGS_VERSION: u32 = 2; -pub(crate) const MACB_MAX_QUEUES: u32 = 8; - -/* MACB register offsets */ -pub(crate) const MACB_NCR: u32 = 0x0000; /* Network Control */ -pub(crate) const MACB_NCFGR: u32 = 0x0004; /* Network Config */ -pub(crate) const MACB_NSR: u32 = 0x0008; /* Network Status */ -pub(crate) const MACB_TAR: u32 = 0x000c; /* AT91RM9200 only */ -pub(crate) const MACB_TCR: u32 = 0x0010; /* AT91RM9200 only */ -pub(crate) const MACB_TSR: u32 = 0x0014; /* Transmit Status */ -pub(crate) const MACB_RBQP: u32 = 0x0018; /* RX Q Base Address */ -pub(crate) const MACB_TBQP: u32 = 0x001c; /* TX Q Base Address */ -pub(crate) const MACB_RSR: u32 = 0x0020; /* Receive Status */ -pub(crate) const MACB_ISR: u32 = 0x0024; /* Interrupt Status */ -pub(crate) const MACB_IER: u32 = 0x0028; /* Interrupt Enable */ -pub(crate) const MACB_IDR: u32 = 0x002c; /* Interrupt Disable */ -pub(crate) const MACB_IMR: u32 = 0x0030; /* Interrupt Mask */ -pub(crate) const MACB_MAN: u32 = 0x0034; /* PHY Maintenance */ -pub(crate) const MACB_PTR: u32 = 0x0038; -pub(crate) const MACB_PFR: u32 = 0x003c; -pub(crate) const MACB_FTO: u32 = 0x0040; -pub(crate) const MACB_SCF: u32 = 0x0044; -pub(crate) const MACB_MCF: u32 = 0x0048; -pub(crate) const MACB_FRO: u32 = 0x004c; -pub(crate) const MACB_FCSE: u32 = 0x0050; -pub(crate) const MACB_ALE: u32 = 0x0054; -pub(crate) const MACB_DTF: u32 = 0x0058; -pub(crate) const MACB_LCOL: u32 = 0x005c; -pub(crate) const MACB_EXCOL: u32 = 0x0060; -pub(crate) const MACB_TUND: u32 = 0x0064; -pub(crate) const MACB_CSE: u32 = 0x0068; -pub(crate) const MACB_RRE: u32 = 0x006c; -pub(crate) const MACB_ROVR: u32 = 0x0070; -pub(crate) const MACB_RSE: u32 = 0x0074; -pub(crate) const MACB_ELE: u32 = 0x0078; -pub(crate) const MACB_RJA: u32 = 0x007c; -pub(crate) const MACB_USF: u32 = 0x0080; -pub(crate) const MACB_STE: u32 = 0x0084; -pub(crate) const MACB_RLE: u32 = 0x0088; -pub(crate) const MACB_TPF: u32 = 0x008c; -pub(crate) const MACB_HRB: u32 = 0x0090; -pub(crate) const MACB_HRT: u32 = 0x0094; -pub(crate) const MACB_SA1B: u32 = 0x0098; -pub(crate) const MACB_SA1T: u32 = 0x009c; -pub(crate) const MACB_SA2B: u32 = 0x00a0; -pub(crate) const MACB_SA2T: u32 = 0x00a4; -pub(crate) const MACB_SA3B: u32 = 0x00a8; -pub(crate) const MACB_SA3T: u32 = 0x00ac; -pub(crate) const MACB_SA4B: u32 = 0x00b0; -pub(crate) const MACB_SA4T: u32 = 0x00b4; -pub(crate) const MACB_TID: u32 = 0x00b8; -pub(crate) const MACB_TPQ: u32 = 0x00bc; -pub(crate) const MACB_USRIO: u32 = 0x00c0; -pub(crate) const MACB_WOL: u32 = 0x00c4; -pub(crate) const MACB_MID: u32 = 0x00fc; -pub(crate) const MACB_TBQPH: u32 = 0x04C8; -pub(crate) const MACB_RBQPH: u32 = 0x04D4; - -/* GEM register offsets. */ -pub(crate) const GEM_NCR: u32 = 0x0000; /* Network Control */ -pub(crate) const GEM_NCFGR: u32 = 0x0004; /* Network Config */ -pub(crate) const GEM_USRIO: u32 = 0x000c; /* User IO */ -pub(crate) const GEM_DMACFG: u32 = 0x0010; /* DMA Configuration */ -pub(crate) const GEM_JML: u32 = 0x0048; /* Jumbo Max Length */ -pub(crate) const GEM_HS_MAC_CONFIG: u32 = 0x0050; /* GEM high speed config */ -pub(crate) const GEM_HRB: u32 = 0x0080; /* Hash Bottom */ -pub(crate) const GEM_HRT: u32 = 0x0084; /* Hash Top */ -pub(crate) const GEM_SA1B: u32 = 0x0088; /* Specific1 Bottom */ -pub(crate) const GEM_SA1T: u32 = 0x008C; /* Specific1 Top */ -pub(crate) const GEM_SA2B: u32 = 0x0090; /* Specific2 Bottom */ -pub(crate) const GEM_SA2T: u32 = 0x0094; /* Specific2 Top */ -pub(crate) const GEM_SA3B: u32 = 0x0098; /* Specific3 Bottom */ -pub(crate) const GEM_SA3T: u32 = 0x009C; /* Specific3 Top */ -pub(crate) const GEM_SA4B: u32 = 0x00A0; /* Specific4 Bottom */ -pub(crate) const GEM_SA4T: u32 = 0x00A4; /* Specific4 Top */ -pub(crate) const GEM_WOL: u32 = 0x00b8; /* Wake on LAN */ -pub(crate) const GEM_EFTSH: u32 = 0x00e8; /* PTP Event Frame Transmitted Seconds Register 47:32 */ -pub(crate) const GEM_EFRSH: u32 = 0x00ec; /* PTP Event Frame Received Seconds Register 47:32 */ -pub(crate) const GEM_PEFTSH: u32 = 0x00f0; /* PTP Peer Event Frame Transmitted Seconds Register 47:32 */ -pub(crate) const GEM_PEFRSH: u32 = 0x00f4; /* PTP Peer Event Frame Received Seconds Register 47:32 */ -pub(crate) const GEM_OTX: u32 = 0x0100; /* Octets transmitted */ -pub(crate) const GEM_OCTTXL: u32 = 0x0100; /* Octets transmitted [31:0] */ -pub(crate) const GEM_OCTTXH: u32 = 0x0104; /* Octets transmitted [47:32] */ -pub(crate) const GEM_TXCNT: u32 = 0x0108; /* Frames Transmitted counter */ -pub(crate) const GEM_TXBCCNT: u32 = 0x010c; /* Broadcast Frames counter */ -pub(crate) const GEM_TXMCCNT: u32 = 0x0110; /* Multicast Frames counter */ -pub(crate) const GEM_TXPAUSECNT: u32 = 0x0114; /* Pause Frames Transmitted Counter */ -pub(crate) const GEM_TX64CNT: u32 = 0x0118; /* 64 byte Frames TX counter */ -pub(crate) const GEM_TX65CNT: u32 = 0x011c; /* 65-127 byte Frames TX counter */ -pub(crate) const GEM_TX128CNT: u32 = 0x0120; /* 128-255 byte Frames TX counter */ -pub(crate) const GEM_TX256CNT: u32 = 0x0124; /* 256-511 byte Frames TX counter */ -pub(crate) const GEM_TX512CNT: u32 = 0x0128; /* 512-1023 byte Frames TX counter */ -pub(crate) const GEM_TX1024CNT: u32 = 0x012c; /* 1024-1518 byte Frames TX counter */ -pub(crate) const GEM_TX1519CNT: u32 = 0x0130; /* 1519+ byte Frames TX counter */ -pub(crate) const GEM_TXURUNCNT: u32 = 0x0134; /* TX under run error counter */ -pub(crate) const GEM_SNGLCOLLCNT: u32 = 0x0138; /* Single Collision Frame Counter */ -pub(crate) const GEM_MULTICOLLCNT: u32 = 0x013c; /* Multiple Collision Frame Counter */ -pub(crate) const GEM_EXCESSCOLLCNT: u32 = 0x0140; /* Excessive Collision Frame Counter */ -pub(crate) const GEM_LATECOLLCNT: u32 = 0x0144; /* Late Collision Frame Counter */ -pub(crate) const GEM_TXDEFERCNT: u32 = 0x0148; /* Deferred Transmission Frame Counter */ -pub(crate) const GEM_TXCSENSECNT: u32 = 0x014c; /* Carrier Sense Error Counter */ -pub(crate) const GEM_ORX: u32 = 0x0150; /* Octets received */ -pub(crate) const GEM_OCTRXL: u32 = 0x0150; /* Octets received [31:0] */ -pub(crate) const GEM_OCTRXH: u32 = 0x0154; /* Octets received [47:32] */ -pub(crate) const GEM_RXCNT: u32 = 0x0158; /* Frames Received Counter */ -pub(crate) const GEM_RXBROADCNT: u32 = 0x015c; /* Broadcast Frames Received Counter */ -pub(crate) const GEM_RXMULTICNT: u32 = 0x0160; /* Multicast Frames Received Counter */ -pub(crate) const GEM_RXPAUSECNT: u32 = 0x0164; /* Pause Frames Received Counter */ -pub(crate) const GEM_RX64CNT: u32 = 0x0168; /* 64 byte Frames RX Counter */ -pub(crate) const GEM_RX65CNT: u32 = 0x016c; /* 65-127 byte Frames RX Counter */ -pub(crate) const GEM_RX128CNT: u32 = 0x0170; /* 128-255 byte Frames RX Counter */ -pub(crate) const GEM_RX256CNT: u32 = 0x0174; /* 256-511 byte Frames RX Counter */ -pub(crate) const GEM_RX512CNT: u32 = 0x0178; /* 512-1023 byte Frames RX Counter */ -pub(crate) const GEM_RX1024CNT: u32 = 0x017c; /* 1024-1518 byte Frames RX Counter */ -pub(crate) const GEM_RX1519CNT: u32 = 0x0180; /* 1519+ byte Frames RX Counter */ -pub(crate) const GEM_RXUNDRCNT: u32 = 0x0184; /* Undersize Frames Received Counter */ -pub(crate) const GEM_RXOVRCNT: u32 = 0x0188; /* Oversize Frames Received Counter */ -pub(crate) const GEM_RXJABCNT: u32 = 0x018c; /* Jabbers Received Counter */ -pub(crate) const GEM_RXFCSCNT: u32 = 0x0190; /* Frame Check Sequence Error Counter */ -pub(crate) const GEM_RXLENGTHCNT: u32 = 0x0194; /* Length Field Error Counter */ -pub(crate) const GEM_RXSYMBCNT: u32 = 0x0198; /* Symbol Error Counter */ -pub(crate) const GEM_RXALIGNCNT: u32 = 0x019c; /* Alignment Error Counter */ -pub(crate) const GEM_RXRESERRCNT: u32 = 0x01a0; /* Receive Resource Error Counter */ -pub(crate) const GEM_RXORCNT: u32 = 0x01a4; /* Receive Overrun Counter */ -pub(crate) const GEM_RXIPCCNT: u32 = 0x01a8; /* IP header Checksum Error Counter */ -pub(crate) const GEM_RXTCPCCNT: u32 = 0x01ac; /* TCP Checksum Error Counter */ -pub(crate) const GEM_RXUDPCCNT: u32 = 0x01b0; /* UDP Checksum Error Counter */ -pub(crate) const GEM_TISUBN: u32 = 0x01bc; /* 1588 Timer Increment Sub-ns */ -pub(crate) const GEM_TSH: u32 = 0x01c0; /* 1588 Timer Seconds High */ -pub(crate) const GEM_TSL: u32 = 0x01d0; /* 1588 Timer Seconds Low */ -pub(crate) const GEM_TN: u32 = 0x01d4; /* 1588 Timer Nanoseconds */ -pub(crate) const GEM_TA: u32 = 0x01d8; /* 1588 Timer Adjust */ -pub(crate) const GEM_TI: u32 = 0x01dc; /* 1588 Timer Increment */ -pub(crate) const GEM_EFTSL: u32 = 0x01e0; /* PTP Event Frame Tx Seconds Low */ -pub(crate) const GEM_EFTN: u32 = 0x01e4; /* PTP Event Frame Tx Nanoseconds */ -pub(crate) const GEM_EFRSL: u32 = 0x01e8; /* PTP Event Frame Rx Seconds Low */ -pub(crate) const GEM_EFRN: u32 = 0x01ec; /* PTP Event Frame Rx Nanoseconds */ -pub(crate) const GEM_PEFTSL: u32 = 0x01f0; /* PTP Peer Event Frame Tx Secs Low */ -pub(crate) const GEM_PEFTN: u32 = 0x01f4; /* PTP Peer Event Frame Tx Ns */ -pub(crate) const GEM_PEFRSL: u32 = 0x01f8; /* PTP Peer Event Frame Rx Sec Low */ -pub(crate) const GEM_PEFRN: u32 = 0x01fc; /* PTP Peer Event Frame Rx Ns */ -pub(crate) const GEM_PCSCNTRL: u32 = 0x0200; /* PCS Control */ -pub(crate) const GEM_PCSSTS: u32 = 0x0204; /* PCS Status */ -pub(crate) const GEM_PCSPHYTOPID: u32 = 0x0208; /* PCS PHY Top ID */ -pub(crate) const GEM_PCSPHYBOTID: u32 = 0x020c; /* PCS PHY Bottom ID */ -pub(crate) const GEM_PCSANADV: u32 = 0x0210; /* PCS AN Advertisement */ -pub(crate) const GEM_PCSANLPBASE: u32 = 0x0214; /* PCS AN Link Partner Base */ -pub(crate) const GEM_PCSANEXP: u32 = 0x0218; /* PCS AN Expansion */ -pub(crate) const GEM_PCSANNPTX: u32 = 0x021c; /* PCS AN Next Page TX */ -pub(crate) const GEM_PCSANNPLP: u32 = 0x0220; /* PCS AN Next Page LP */ -pub(crate) const GEM_PCSANEXTSTS: u32 = 0x023c; /* PCS AN Extended Status */ -pub(crate) const GEM_DCFG1: u32 = 0x0280; /* Design Config 1 */ -pub(crate) const GEM_DCFG2: u32 = 0x0284; /* Design Config 2 */ -pub(crate) const GEM_DCFG3: u32 = 0x0288; /* Design Config 3 */ -pub(crate) const GEM_DCFG4: u32 = 0x028c; /* Design Config 4 */ -pub(crate) const GEM_DCFG5: u32 = 0x0290; /* Design Config 5 */ -pub(crate) const GEM_DCFG6: u32 = 0x0294; /* Design Config 6 */ -pub(crate) const GEM_DCFG7: u32 = 0x0298; /* Design Config 7 */ -pub(crate) const GEM_DCFG8: u32 = 0x029C; /* Design Config 8 */ -pub(crate) const GEM_DCFG10: u32 = 0x02A4; /* Design Config 10 */ -pub(crate) const GEM_DCFG12: u32 = 0x02AC; /* Design Config 12 */ -pub(crate) const GEM_USX_CONTROL: u32 = 0x0A80; /* High speed PCS control register */ -pub(crate) const GEM_USX_STATUS: u32 = 0x0A88; /* High speed PCS status register */ - -pub(crate) const GEM_TXBDCTRL: u32 = 0x04cc; /* TX Buffer Descriptor control register */ -pub(crate) const GEM_RXBDCTRL: u32 = 0x04d0; /* RX Buffer Descriptor control register */ - -/* Screener Type 2 match registers */ -pub(crate) const GEM_SCRT2: u32 = 0x540; - -/* EtherType registers */ -pub(crate) const GEM_ETHT: u32 = 0x06E0; - -/* Type 2 compare registers */ -pub(crate) const GEM_T2CMPW0: u32 = 0x0700; -pub(crate) const GEM_T2CMPW1: u32 = 0x0704; -//pub(crate) const T2CMP_OFST(t2idx) (t2idx * 2) - -/* -/* type 2 compare registers - * each location requires 3 compare regs - */ -pub(crate) const GEM_IP4SRC_CMP(idx) (idx * 3) -pub(crate) const GEM_IP4DST_CMP(idx) (idx * 3 + 1) -pub(crate) const GEM_PORT_CMP(idx) (idx * 3 + 2) - -/* Which screening type 2 EtherType register will be used (0 - 7) */ -pub(crate) const SCRT2_ETHT 0 - -pub(crate) const GEM_ISR(hw_q) (0x0400 + ((hw_q) << 2)) -pub(crate) const GEM_TBQP(hw_q) (0x0440 + ((hw_q) << 2)) -pub(crate) const GEM_TBQPH(hw_q) (0x04C8) -pub(crate) const GEM_RBQP(hw_q) (0x0480 + ((hw_q) << 2)) -pub(crate) const GEM_RBQS(hw_q) (0x04A0 + ((hw_q) << 2)) -pub(crate) const GEM_RBQPH(hw_q) (0x04D4) -pub(crate) const GEM_IER(hw_q) (0x0600 + ((hw_q) << 2)) -pub(crate) const GEM_IDR(hw_q) (0x0620 + ((hw_q) << 2)) -pub(crate) const GEM_IMR(hw_q) (0x0640 + ((hw_q) << 2)) - -*/ - -/* Bitfields in NCR */ -pub(crate) const MACB_LB_OFFSET: u32 = 0; /* reserved */ -pub(crate) const MACB_LB_SIZE: u32 = 1; -pub(crate) const MACB_LLB_OFFSET: u32 = 1; /* Loop back local */ -pub(crate) const MACB_LLB_SIZE: u32 = 1; -pub(crate) const MACB_RE_OFFSET: u32 = 2; /* Receive enable */ -pub(crate) const MACB_RE_SIZE: u32 = 1; -pub(crate) const MACB_TE_OFFSET: u32 = 3; /* Transmit enable */ -pub(crate) const MACB_TE_SIZE: u32 = 1; -pub(crate) const MACB_MPE_OFFSET: u32 = 4; /* Management port enable */ -pub(crate) const MACB_MPE_SIZE: u32 = 1; -pub(crate) const MACB_CLRSTAT_OFFSET: u32 = 5; /* Clear stats regs */ -pub(crate) const MACB_CLRSTAT_SIZE: u32 = 1; -pub(crate) const MACB_INCSTAT_OFFSET: u32 = 6; /* Incremental stats regs */ -pub(crate) const MACB_INCSTAT_SIZE: u32 = 1; -pub(crate) const MACB_WESTAT_OFFSET: u32 = 7; /* Write enable stats regs */ -pub(crate) const MACB_WESTAT_SIZE: u32 = 1; -pub(crate) const MACB_BP_OFFSET: u32 = 8; /* Back pressure */ -pub(crate) const MACB_BP_SIZE: u32 = 1; -pub(crate) const MACB_TSTART_OFFSET: u32 = 9; /* Start transmission */ -pub(crate) const MACB_TSTART_SIZE: u32 = 1; -pub(crate) const MACB_THALT_OFFSET: u32 = 10; /* Transmit halt */ -pub(crate) const MACB_THALT_SIZE: u32 = 1; -pub(crate) const MACB_NCR_TPF_OFFSET: u32 = 11; /* Transmit pause frame */ -pub(crate) const MACB_NCR_TPF_SIZE: u32 = 1; -pub(crate) const MACB_TZQ_OFFSET: u32 = 12; /* Transmit zero quantum pause frame */ -pub(crate) const MACB_TZQ_SIZE: u32 = 1; -pub(crate) const MACB_SRTSM_OFFSET: u32 = 15; /* Store Receive Timestamp to Memory */ -pub(crate) const MACB_OSSMODE_OFFSET: u32 = 24; /* Enable One Step Synchro Mode */ -pub(crate) const MACB_OSSMODE_SIZE: u32 = 1; -pub(crate) const MACB_MIIONRGMII_OFFSET: u32 = 28; /* MII Usage on RGMII Interface */ -pub(crate) const MACB_MIIONRGMII_SIZE: u32 = 1; - -/* Bitfields in NCFGR */ -pub(crate) const MACB_SPD_OFFSET: u32 = 0; /* Speed */ -pub(crate) const MACB_SPD_SIZE: u32 = 1; -pub(crate) const MACB_FD_OFFSET: u32 = 1; /* Full duplex */ -pub(crate) const MACB_FD_SIZE: u32 = 1; -pub(crate) const MACB_BIT_RATE_OFFSET: u32 = 2; /* Discard non-VLAN frames */ -pub(crate) const MACB_BIT_RATE_SIZE: u32 = 1; -pub(crate) const MACB_JFRAME_OFFSET: u32 = 3; /* reserved */ -pub(crate) const MACB_JFRAME_SIZE: u32 = 1; -pub(crate) const MACB_CAF_OFFSET: u32 = 4; /* Copy all frames */ -pub(crate) const MACB_CAF_SIZE: u32 = 1; -pub(crate) const MACB_NBC_OFFSET: u32 = 5; /* No broadcast */ -pub(crate) const MACB_NBC_SIZE: u32 = 1; -pub(crate) const MACB_NCFGR_MTI_OFFSET: u32 = 6; /* Multicast hash enable */ -pub(crate) const MACB_NCFGR_MTI_SIZE: u32 = 1; -pub(crate) const MACB_UNI_OFFSET: u32 = 7; /* Unicast hash enable */ -pub(crate) const MACB_UNI_SIZE: u32 = 1; -pub(crate) const MACB_BIG_OFFSET: u32 = 8; /* Receive 1536 byte frames */ -pub(crate) const MACB_BIG_SIZE: u32 = 1; -pub(crate) const MACB_EAE_OFFSET: u32 = 9; /* External address match enable */ -pub(crate) const MACB_EAE_SIZE: u32 = 1; -pub(crate) const MACB_CLK_OFFSET: u32 = 10; -pub(crate) const MACB_CLK_SIZE: u32 = 2; -pub(crate) const MACB_RTY_OFFSET: u32 = 12; /* Retry test */ -pub(crate) const MACB_RTY_SIZE: u32 = 1; -pub(crate) const MACB_PAE_OFFSET: u32 = 13; /* Pause enable */ -pub(crate) const MACB_PAE_SIZE: u32 = 1; -pub(crate) const MACB_RM9200_RMII_OFFSET: u32 = 13; /* AT91RM9200 only */ -pub(crate) const MACB_RM9200_RMII_SIZE: u32 = 1; /* AT91RM9200 only */ -pub(crate) const MACB_RBOF_OFFSET: u32 = 14; /* Receive buffer offset */ -pub(crate) const MACB_RBOF_SIZE: u32 = 2; -pub(crate) const MACB_RLCE_OFFSET: u32 = 16; /* Length field error frame discard */ -pub(crate) const MACB_RLCE_SIZE: u32 = 1; -pub(crate) const MACB_DRFCS_OFFSET: u32 = 17; /* FCS remove */ -pub(crate) const MACB_DRFCS_SIZE: u32 = 1; -pub(crate) const MACB_EFRHD_OFFSET: u32 = 18; -pub(crate) const MACB_EFRHD_SIZE: u32 = 1; -pub(crate) const MACB_IRXFCS_OFFSET: u32 = 19; -pub(crate) const MACB_IRXFCS_SIZE: u32 = 1; - -/* GEM specific NCR bitfields. */ -pub(crate) const GEM_ENABLE_HS_MAC_OFFSET: u32 = 31; -pub(crate) const GEM_ENABLE_HS_MAC_SIZE: u32 = 1; - -/* GEM specific NCFGR bitfields. */ -pub(crate) const GEM_FD_OFFSET: u32 = 1; /* Full duplex */ -pub(crate) const GEM_FD_SIZE: u32 = 1; -pub(crate) const GEM_GBE_OFFSET: u32 = 10; /* Gigabit mode enable */ -pub(crate) const GEM_GBE_SIZE: u32 = 1; -pub(crate) const GEM_PCSSEL_OFFSET: u32 = 11; -pub(crate) const GEM_PCSSEL_SIZE: u32 = 1; -pub(crate) const GEM_PAE_OFFSET: u32 = 13; /* Pause enable */ -pub(crate) const GEM_PAE_SIZE: u32 = 1; -pub(crate) const GEM_CLK_OFFSET: u32 = 18; /* MDC clock division */ -pub(crate) const GEM_CLK_SIZE: u32 = 3; -pub(crate) const GEM_DBW_OFFSET: u32 = 21; /* Data bus width */ -pub(crate) const GEM_DBW_SIZE: u32 = 2; -pub(crate) const GEM_RXCOEN_OFFSET: u32 = 24; -pub(crate) const GEM_RXCOEN_SIZE: u32 = 1; -pub(crate) const GEM_SGMIIEN_OFFSET: u32 = 27; -pub(crate) const GEM_SGMIIEN_SIZE: u32 = 1; - -/* Constants for data bus width. */ -pub(crate) const GEM_DBW32: u32 = 0; /* 32 bit AMBA AHB data bus width */ -pub(crate) const GEM_DBW64: u32 = 1; /* 64 bit AMBA AHB data bus width */ -pub(crate) const GEM_DBW128: u32 = 2; /* 128 bit AMBA AHB data bus width */ - -/* Bitfields in DMACFG. */ -pub(crate) const GEM_FBLDO_OFFSET: u32 = 0; /* fixed burst length for DMA */ -pub(crate) const GEM_FBLDO_SIZE: u32 = 5; -pub(crate) const GEM_ENDIA_DESC_OFFSET: u32 = 6; /* endian swap mode for management descriptor access */ -pub(crate) const GEM_ENDIA_DESC_SIZE: u32 = 1; -pub(crate) const GEM_ENDIA_PKT_OFFSET: u32 = 7; /* endian swap mode for packet data access */ -pub(crate) const GEM_ENDIA_PKT_SIZE: u32 = 1; -pub(crate) const GEM_RXBMS_OFFSET: u32 = 8; /* RX packet buffer memory size select */ -pub(crate) const GEM_RXBMS_SIZE: u32 = 2; -pub(crate) const GEM_TXPBMS_OFFSET: u32 = 10; /* TX packet buffer memory size select */ -pub(crate) const GEM_TXPBMS_SIZE: u32 = 1; -pub(crate) const GEM_TXCOEN_OFFSET: u32 = 11; /* TX IP/TCP/UDP checksum gen offload */ -pub(crate) const GEM_TXCOEN_SIZE: u32 = 1; -pub(crate) const GEM_RXBS_OFFSET: u32 = 16; /* DMA receive buffer size */ -pub(crate) const GEM_RXBS_SIZE: u32 = 8; -pub(crate) const GEM_DDRP_OFFSET: u32 = 24; /* disc_when_no_ahb */ -pub(crate) const GEM_DDRP_SIZE: u32 = 1; -pub(crate) const GEM_RXEXT_OFFSET: u32 = 28; /* RX extended Buffer Descriptor mode */ -pub(crate) const GEM_RXEXT_SIZE: u32 = 1; -pub(crate) const GEM_TXEXT_OFFSET: u32 = 29; /* TX extended Buffer Descriptor mode */ -pub(crate) const GEM_TXEXT_SIZE: u32 = 1; -pub(crate) const GEM_ADDR64_OFFSET: u32 = 30; /* Address bus width - 64b or 32b */ -pub(crate) const GEM_ADDR64_SIZE: u32 = 1; - -/* Bitfields in NSR */ -pub(crate) const MACB_NSR_LINK_OFFSET: u32 = 0; /* pcs_link_state */ -pub(crate) const MACB_NSR_LINK_SIZE: u32 = 1; -pub(crate) const MACB_MDIO_OFFSET: u32 = 1; /* status of the mdio_in pin */ -pub(crate) const MACB_MDIO_SIZE: u32 = 1; -pub(crate) const MACB_IDLE_OFFSET: u32 = 2; /* The PHY management logic is idle */ -pub(crate) const MACB_IDLE_SIZE: u32 = 1; - -/* Bitfields in TSR */ -pub(crate) const MACB_UBR_OFFSET: u32 = 0; /* Used bit read */ -pub(crate) const MACB_UBR_SIZE: u32 = 1; -pub(crate) const MACB_COL_OFFSET: u32 = 1; /* Collision occurred */ -pub(crate) const MACB_COL_SIZE: u32 = 1; -pub(crate) const MACB_TSR_RLE_OFFSET: u32 = 2; /* Retry limit exceeded */ -pub(crate) const MACB_TSR_RLE_SIZE: u32 = 1; -pub(crate) const MACB_TGO_OFFSET: u32 = 3; /* Transmit go */ -pub(crate) const MACB_TGO_SIZE: u32 = 1; -pub(crate) const MACB_BEX_OFFSET: u32 = 4; /* TX frame corruption due to AHB error */ -pub(crate) const MACB_BEX_SIZE: u32 = 1; -pub(crate) const MACB_RM9200_BNQ_OFFSET: u32 = 4; /* AT91RM9200 only */ -pub(crate) const MACB_RM9200_BNQ_SIZE: u32 = 1; /* AT91RM9200 only */ -pub(crate) const MACB_COMP_OFFSET: u32 = 5; /* Trnasmit complete */ -pub(crate) const MACB_COMP_SIZE: u32 = 1; -pub(crate) const MACB_UND_OFFSET: u32 = 6; /* Trnasmit under run */ -pub(crate) const MACB_UND_SIZE: u32 = 1; - -/* Bitfields in RSR */ -pub(crate) const MACB_BNA_OFFSET: u32 = 0; /* Buffer not available */ -pub(crate) const MACB_BNA_SIZE: u32 = 1; -pub(crate) const MACB_REC_OFFSET: u32 = 1; /* Frame received */ -pub(crate) const MACB_REC_SIZE: u32 = 1; -pub(crate) const MACB_OVR_OFFSET: u32 = 2; /* Receive overrun */ -pub(crate) const MACB_OVR_SIZE: u32 = 1; - -/* Bitfields in ISR/IER/IDR/IMR */ -pub(crate) const MACB_MFD_OFFSET: u32 = 0; /* Management frame sent */ -pub(crate) const MACB_MFD_SIZE: u32 = 1; -pub(crate) const MACB_RCOMP_OFFSET: u32 = 1; /* Receive complete */ -pub(crate) const MACB_RCOMP_SIZE: u32 = 1; -pub(crate) const MACB_RXUBR_OFFSET: u32 = 2; /* RX used bit read */ -pub(crate) const MACB_RXUBR_SIZE: u32 = 1; -pub(crate) const MACB_TXUBR_OFFSET: u32 = 3; /* TX used bit read */ -pub(crate) const MACB_TXUBR_SIZE: u32 = 1; -pub(crate) const MACB_ISR_TUND_OFFSET: u32 = 4; /* Enable TX buffer under run interrupt */ -pub(crate) const MACB_ISR_TUND_SIZE: u32 = 1; -pub(crate) const MACB_ISR_RLE_OFFSET: u32 = 5; /* EN retry exceeded/late coll interrupt */ -pub(crate) const MACB_ISR_RLE_SIZE: u32 = 1; -pub(crate) const MACB_TXERR_OFFSET: u32 = 6; /* EN TX frame corrupt from error interrupt */ -pub(crate) const MACB_TXERR_SIZE: u32 = 1; -pub(crate) const MACB_RM9200_TBRE_OFFSET: u32 = 6; /* EN may send new frame interrupt (RM9200) */ -pub(crate) const MACB_RM9200_TBRE_SIZE: u32 = 1; -pub(crate) const MACB_TCOMP_OFFSET: u32 = 7; /* Enable transmit complete interrupt */ -pub(crate) const MACB_TCOMP_SIZE: u32 = 1; -pub(crate) const MACB_ISR_LINK_OFFSET: u32 = 9; /* Enable link change interrupt */ -pub(crate) const MACB_ISR_LINK_SIZE: u32 = 1; -pub(crate) const MACB_ISR_ROVR_OFFSET: u32 = 10; /* Enable receive overrun interrupt */ -pub(crate) const MACB_ISR_ROVR_SIZE: u32 = 1; -pub(crate) const MACB_HRESP_OFFSET: u32 = 11; /* Enable hrsep not OK interrupt */ -pub(crate) const MACB_HRESP_SIZE: u32 = 1; -pub(crate) const MACB_PFR_OFFSET: u32 = 12; /* Enable pause frame w/ quantum interrupt */ -pub(crate) const MACB_PFR_SIZE: u32 = 1; -pub(crate) const MACB_PTZ_OFFSET: u32 = 13; /* Enable pause time zero interrupt */ -pub(crate) const MACB_PTZ_SIZE: u32 = 1; -pub(crate) const MACB_WOL_OFFSET: u32 = 14; /* Enable wake-on-lan interrupt */ -pub(crate) const MACB_WOL_SIZE: u32 = 1; -pub(crate) const MACB_DRQFR_OFFSET: u32 = 18; /* PTP Delay Request Frame Received */ -pub(crate) const MACB_DRQFR_SIZE: u32 = 1; -pub(crate) const MACB_SFR_OFFSET: u32 = 19; /* PTP Sync Frame Received */ -pub(crate) const MACB_SFR_SIZE: u32 = 1; -pub(crate) const MACB_DRQFT_OFFSET: u32 = 20; /* PTP Delay Request Frame Transmitted */ -pub(crate) const MACB_DRQFT_SIZE: u32 = 1; -pub(crate) const MACB_SFT_OFFSET: u32 = 21; /* PTP Sync Frame Transmitted */ -pub(crate) const MACB_SFT_SIZE: u32 = 1; -pub(crate) const MACB_PDRQFR_OFFSET: u32 = 22; /* PDelay Request Frame Received */ -pub(crate) const MACB_PDRQFR_SIZE: u32 = 1; -pub(crate) const MACB_PDRSFR_OFFSET: u32 = 23; /* PDelay Response Frame Received */ -pub(crate) const MACB_PDRSFR_SIZE: u32 = 1; -pub(crate) const MACB_PDRQFT_OFFSET: u32 = 24; /* PDelay Request Frame Transmitted */ -pub(crate) const MACB_PDRQFT_SIZE: u32 = 1; -pub(crate) const MACB_PDRSFT_OFFSET: u32 = 25; /* PDelay Response Frame Transmitted */ -pub(crate) const MACB_PDRSFT_SIZE: u32 = 1; -pub(crate) const MACB_SRI_OFFSET: u32 = 26; /* TSU Seconds Register Increment */ -pub(crate) const MACB_SRI_SIZE: u32 = 1; -pub(crate) const GEM_WOL_OFFSET: u32 = 28; /* Enable wake-on-lan interrupt */ -pub(crate) const GEM_WOL_SIZE: u32 = 1; - -/* Timer increment fields */ -pub(crate) const MACB_TI_CNS_OFFSET: u32 = 0; -pub(crate) const MACB_TI_CNS_SIZE: u32 = 8; -pub(crate) const MACB_TI_ACNS_OFFSET: u32 = 8; -pub(crate) const MACB_TI_ACNS_SIZE: u32 = 8; -pub(crate) const MACB_TI_NIT_OFFSET: u32 = 16; -pub(crate) const MACB_TI_NIT_SIZE: u32 = 8; - -/* Bitfields in MAN */ -pub(crate) const MACB_DATA_OFFSET: u32 = 0; /* data */ -pub(crate) const MACB_DATA_SIZE: u32 = 16; -pub(crate) const MACB_CODE_OFFSET: u32 = 16; /* Must be written to 10 */ -pub(crate) const MACB_CODE_SIZE: u32 = 2; -pub(crate) const MACB_REGA_OFFSET: u32 = 18; /* Register address */ -pub(crate) const MACB_REGA_SIZE: u32 = 5; -pub(crate) const MACB_PHYA_OFFSET: u32 = 23; /* PHY address */ -pub(crate) const MACB_PHYA_SIZE: u32 = 5; -pub(crate) const MACB_RW_OFFSET: u32 = 28; /* Operation. 10 is read. 01 is write. */ -pub(crate) const MACB_RW_SIZE: u32 = 2; -pub(crate) const MACB_SOF_OFFSET: u32 = 30; /* Must be written to 1 for Clause 22 */ -pub(crate) const MACB_SOF_SIZE: u32 = 2; - -/* Bitfields in USRIO (AVR32) */ -pub(crate) const MACB_MII_OFFSET: u32 = 0; -pub(crate) const MACB_MII_SIZE: u32 = 1; -pub(crate) const MACB_EAM_OFFSET: u32 = 1; -pub(crate) const MACB_EAM_SIZE: u32 = 1; -pub(crate) const MACB_TX_PAUSE_OFFSET: u32 = 2; -pub(crate) const MACB_TX_PAUSE_SIZE: u32 = 1; -pub(crate) const MACB_TX_PAUSE_ZERO_OFFSET: u32 = 3; -pub(crate) const MACB_TX_PAUSE_ZERO_SIZE: u32 = 1; - -/* Bitfields in USRIO (AT91) */ -pub(crate) const MACB_RMII_OFFSET: u32 = 0; -pub(crate) const MACB_RMII_SIZE: u32 = 1; -pub(crate) const GEM_RGMII_OFFSET: u32 = 0; /* GEM gigabit mode */ -pub(crate) const GEM_RGMII_SIZE: u32 = 1; -pub(crate) const MACB_CLKEN_OFFSET: u32 = 1; -pub(crate) const MACB_CLKEN_SIZE: u32 = 1; - -/* Bitfields in WOL */ -pub(crate) const MACB_IP_OFFSET: u32 = 0; -pub(crate) const MACB_IP_SIZE: u32 = 16; -pub(crate) const MACB_MAG_OFFSET: u32 = 16; -pub(crate) const MACB_MAG_SIZE: u32 = 1; -pub(crate) const MACB_ARP_OFFSET: u32 = 17; -pub(crate) const MACB_ARP_SIZE: u32 = 1; -pub(crate) const MACB_SA1_OFFSET: u32 = 18; -pub(crate) const MACB_SA1_SIZE: u32 = 1; -pub(crate) const MACB_WOL_MTI_OFFSET: u32 = 19; -pub(crate) const MACB_WOL_MTI_SIZE: u32 = 1; - -/* Bitfields in MID */ -pub(crate) const MACB_IDNUM_OFFSET: u32 = 16; -pub(crate) const MACB_IDNUM_SIZE: u32 = 12; -pub(crate) const MACB_REV_OFFSET: u32 = 0; -pub(crate) const MACB_REV_SIZE: u32 = 16; - -/* Bitfield in HS_MAC_CONFIG */ -pub(crate) const GEM_HS_MAC_SPEED_OFFSET: u32 = 0; -pub(crate) const GEM_HS_MAC_SPEED_SIZE: u32 = 3; - -/* Bitfields in PCSCNTRL */ -pub(crate) const GEM_PCSAUTONEG_OFFSET: u32 = 12; -pub(crate) const GEM_PCSAUTONEG_SIZE: u32 = 1; - -/* Bitfields in DCFG1. */ -pub(crate) const GEM_IRQCOR_OFFSET: u32 = 23; -pub(crate) const GEM_IRQCOR_SIZE: u32 = 1; -pub(crate) const GEM_DBWDEF_OFFSET: u32 = 25; -pub(crate) const GEM_DBWDEF_SIZE: u32 = 3; -pub(crate) const GEM_NO_PCS_OFFSET: u32 = 0; -pub(crate) const GEM_NO_PCS_SIZE: u32 = 1; - -/* Bitfields in DCFG2. */ -pub(crate) const GEM_RX_PKT_BUFF_OFFSET: u32 = 20; -pub(crate) const GEM_RX_PKT_BUFF_SIZE: u32 = 1; -pub(crate) const GEM_TX_PKT_BUFF_OFFSET: u32 = 21; -pub(crate) const GEM_TX_PKT_BUFF_SIZE: u32 = 1; - -/* Bitfields in DCFG5. */ -pub(crate) const GEM_TSU_OFFSET: u32 = 8; -pub(crate) const GEM_TSU_SIZE: u32 = 1; - -/* Bitfields in DCFG6. */ -pub(crate) const GEM_PBUF_LSO_OFFSET: u32 = 27; -pub(crate) const GEM_PBUF_LSO_SIZE: u32 = 1; -pub(crate) const GEM_DAW64_OFFSET: u32 = 23; -pub(crate) const GEM_DAW64_SIZE: u32 = 1; - -/* Bitfields in DCFG8. */ -pub(crate) const GEM_T1SCR_OFFSET: u32 = 24; -pub(crate) const GEM_T1SCR_SIZE: u32 = 8; -pub(crate) const GEM_T2SCR_OFFSET: u32 = 16; -pub(crate) const GEM_T2SCR_SIZE: u32 = 8; -pub(crate) const GEM_SCR2ETH_OFFSET: u32 = 8; -pub(crate) const GEM_SCR2ETH_SIZE: u32 = 8; -pub(crate) const GEM_SCR2CMP_OFFSET: u32 = 0; -pub(crate) const GEM_SCR2CMP_SIZE: u32 = 8; - -/* Bitfields in DCFG10 */ -pub(crate) const GEM_TXBD_RDBUFF_OFFSET: u32 = 12; -pub(crate) const GEM_TXBD_RDBUFF_SIZE: u32 = 4; -pub(crate) const GEM_RXBD_RDBUFF_OFFSET: u32 = 8; -pub(crate) const GEM_RXBD_RDBUFF_SIZE: u32 = 4; - -/* Bitfields in DCFG12. */ -pub(crate) const GEM_HIGH_SPEED_OFFSET: u32 = 26; -pub(crate) const GEM_HIGH_SPEED_SIZE: u32 = 1; - -/* Bitfields in USX_CONTROL. */ -pub(crate) const GEM_USX_CTRL_SPEED_OFFSET: u32 = 14; -pub(crate) const GEM_USX_CTRL_SPEED_SIZE: u32 = 3; -pub(crate) const GEM_SERDES_RATE_OFFSET: u32 = 12; -pub(crate) const GEM_SERDES_RATE_SIZE: u32 = 2; -pub(crate) const GEM_RX_SCR_BYPASS_OFFSET: u32 = 9; -pub(crate) const GEM_RX_SCR_BYPASS_SIZE: u32 = 1; -pub(crate) const GEM_TX_SCR_BYPASS_OFFSET: u32 = 8; -pub(crate) const GEM_TX_SCR_BYPASS_SIZE: u32 = 1; -pub(crate) const GEM_TX_EN_OFFSET: u32 = 1; -pub(crate) const GEM_TX_EN_SIZE: u32 = 1; -pub(crate) const GEM_SIGNAL_OK_OFFSET: u32 = 0; -pub(crate) const GEM_SIGNAL_OK_SIZE: u32 = 1; - -/* Bitfields in USX_STATUS. */ -pub(crate) const GEM_USX_BLOCK_LOCK_OFFSET: u32 = 0; -pub(crate) const GEM_USX_BLOCK_LOCK_SIZE: u32 = 1; - -/* Bitfields in TISUBN */ -pub(crate) const GEM_SUBNSINCR_OFFSET: u32 = 0; -pub(crate) const GEM_SUBNSINCRL_OFFSET: u32 = 24; -pub(crate) const GEM_SUBNSINCRL_SIZE: u32 = 8; -pub(crate) const GEM_SUBNSINCRH_OFFSET: u32 = 0; -pub(crate) const GEM_SUBNSINCRH_SIZE: u32 = 16; -pub(crate) const GEM_SUBNSINCR_SIZE: u32 = 24; - -/* Bitfields in TI */ -pub(crate) const GEM_NSINCR_OFFSET: u32 = 0; -pub(crate) const GEM_NSINCR_SIZE: u32 = 8; - -/* Bitfields in TSH */ -pub(crate) const GEM_TSH_OFFSET: u32 = 0; /* TSU timer value (s). MSB [47:32] of seconds timer count */ -pub(crate) const GEM_TSH_SIZE: u32 = 16; - -/* Bitfields in TSL */ -pub(crate) const GEM_TSL_OFFSET: u32 = 0; /* TSU timer value (s). LSB [31:0] of seconds timer count */ -pub(crate) const GEM_TSL_SIZE: u32 = 32; - -/* Bitfields in TN */ -pub(crate) const GEM_TN_OFFSET: u32 = 0; /* TSU timer value (ns) */ -pub(crate) const GEM_TN_SIZE: u32 = 30; - -/* Bitfields in TXBDCTRL */ -pub(crate) const GEM_TXTSMODE_OFFSET: u32 = 4; /* TX Descriptor Timestamp Insertion mode */ -pub(crate) const GEM_TXTSMODE_SIZE: u32 = 2; - -/* Bitfields in RXBDCTRL */ -pub(crate) const GEM_RXTSMODE_OFFSET: u32 = 4; /* RX Descriptor Timestamp Insertion mode */ -pub(crate) const GEM_RXTSMODE_SIZE: u32 = 2; - -/* Bitfields in SCRT2 */ -pub(crate) const GEM_QUEUE_OFFSET: u32 = 0; /* Queue Number */ -pub(crate) const GEM_QUEUE_SIZE: u32 = 4; -pub(crate) const GEM_VLANPR_OFFSET: u32 = 4; /* VLAN Priority */ -pub(crate) const GEM_VLANPR_SIZE: u32 = 3; -pub(crate) const GEM_VLANEN_OFFSET: u32 = 8; /* VLAN Enable */ -pub(crate) const GEM_VLANEN_SIZE: u32 = 1; -pub(crate) const GEM_ETHT2IDX_OFFSET: u32 = 9; /* Index to screener type 2 EtherType register */ -pub(crate) const GEM_ETHT2IDX_SIZE: u32 = 3; -pub(crate) const GEM_ETHTEN_OFFSET: u32 = 12; /* EtherType Enable */ -pub(crate) const GEM_ETHTEN_SIZE: u32 = 1; -pub(crate) const GEM_CMPA_OFFSET: u32 = 13; /* Compare A - Index to screener type 2 Compare register */ -pub(crate) const GEM_CMPA_SIZE: u32 = 5; -pub(crate) const GEM_CMPAEN_OFFSET: u32 = 18; /* Compare A Enable */ -pub(crate) const GEM_CMPAEN_SIZE: u32 = 1; -pub(crate) const GEM_CMPB_OFFSET: u32 = 19; /* Compare B - Index to screener type 2 Compare register */ -pub(crate) const GEM_CMPB_SIZE: u32 = 5; -pub(crate) const GEM_CMPBEN_OFFSET: u32 = 24; /* Compare B Enable */ -pub(crate) const GEM_CMPBEN_SIZE: u32 = 1; -pub(crate) const GEM_CMPC_OFFSET: u32 = 25; /* Compare C - Index to screener type 2 Compare register */ -pub(crate) const GEM_CMPC_SIZE: u32 = 5; -pub(crate) const GEM_CMPCEN_OFFSET: u32 = 30; /* Compare C Enable */ -pub(crate) const GEM_CMPCEN_SIZE: u32 = 1; - -/* Bitfields in ETHT */ -pub(crate) const GEM_ETHTCMP_OFFSET: u32 = 0; /* EtherType compare value */ -pub(crate) const GEM_ETHTCMP_SIZE: u32 = 16; - -/* Bitfields in T2CMPW0 */ -pub(crate) const GEM_T2CMP_OFFSET: u32 = 16; /* 0xFFFF0000 compare value */ -pub(crate) const GEM_T2CMP_SIZE: u32 = 16; -pub(crate) const GEM_T2MASK_OFFSET: u32 = 0; /* 0x0000FFFF compare value or mask */ -pub(crate) const GEM_T2MASK_SIZE: u32 = 16; - -/* Bitfields in T2CMPW1 */ -pub(crate) const GEM_T2DISMSK_OFFSET: u32 = 9; /* disable mask */ -pub(crate) const GEM_T2DISMSK_SIZE: u32 = 1; -pub(crate) const GEM_T2CMPOFST_OFFSET: u32 = 7; /* compare offset */ -pub(crate) const GEM_T2CMPOFST_SIZE: u32 = 2; -pub(crate) const GEM_T2OFST_OFFSET: u32 = 0; /* offset value */ -pub(crate) const GEM_T2OFST_SIZE: u32 = 7; - -/* Offset for screener type 2 compare values (T2CMPOFST). - * Note the offset is applied after the specified point, - * e.g. GEM_T2COMPOFST_ETYPE denotes the EtherType field, so an offset - * of 12 bytes from this would be the source IP address in an IP header - */ -pub(crate) const GEM_T2COMPOFST_SOF: u32 = 0; -pub(crate) const GEM_T2COMPOFST_ETYPE: u32 = 1; -pub(crate) const GEM_T2COMPOFST_IPHDR: u32 = 2; -pub(crate) const GEM_T2COMPOFST_TCPUDP: u32 = 3; - -/* offset from EtherType to IP address */ -pub(crate) const ETYPE_SRCIP_OFFSET: u32 = 12; -pub(crate) const ETYPE_DSTIP_OFFSET: u32 = 16; - -/* offset from IP header to port */ -pub(crate) const IPHDR_SRCPORT_OFFSET: u32 = 0; -pub(crate) const IPHDR_DSTPORT_OFFSET: u32 = 2; - -/* Transmit DMA buffer descriptor Word 1 */ -pub(crate) const GEM_DMA_TXVALID_OFFSET: u32 = 23; /* timestamp has been captured in the Buffer Descriptor */ -pub(crate) const GEM_DMA_TXVALID_SIZE: u32 = 1; - -/* Receive DMA buffer descriptor Word 0 */ -pub(crate) const GEM_DMA_RXVALID_OFFSET: u32 = 2; /* indicates a valid timestamp in the Buffer Descriptor */ -pub(crate) const GEM_DMA_RXVALID_SIZE: u32 = 1; - -/* DMA buffer descriptor Word 2 (32 bit addressing) or Word 4 (64 bit addressing) */ -pub(crate) const GEM_DMA_SECL_OFFSET: u32 = 30; /* Timestamp seconds[1:0] */ -pub(crate) const GEM_DMA_SECL_SIZE: u32 = 2; -pub(crate) const GEM_DMA_NSEC_OFFSET: u32 = 0; /* Timestamp nanosecs [29:0] */ -pub(crate) const GEM_DMA_NSEC_SIZE: u32 = 30; - -/* DMA buffer descriptor Word 3 (32 bit addressing) or Word 5 (64 bit addressing) */ - -/* New hardware supports 12 bit precision of timestamp in DMA buffer descriptor. - * Old hardware supports only 6 bit precision but it is enough for PTP. - * Less accuracy is used always instead of checking hardware version. - */ -/* -pub(crate) const GEM_DMA_SECH_OFFSET 0 /* Timestamp seconds[5:2] */ -pub(crate) const GEM_DMA_SECH_SIZE 4 -pub(crate) const GEM_DMA_SEC_WIDTH (GEM_DMA_SECH_SIZE + GEM_DMA_SECL_SIZE) -pub(crate) const GEM_DMA_SEC_TOP (1 << GEM_DMA_SEC_WIDTH) -pub(crate) const GEM_DMA_SEC_MASK (GEM_DMA_SEC_TOP - 1) -*/ - -/* Bitfields in ADJ */ -pub(crate) const GEM_ADDSUB_OFFSET: u32 = 31; -pub(crate) const GEM_ADDSUB_SIZE: u32 = 1; -/* Constants for CLK */ -pub(crate) const MACB_CLK_DIV8: u32 = 0; -pub(crate) const MACB_CLK_DIV16: u32 = 1; -pub(crate) const MACB_CLK_DIV32: u32 = 2; -pub(crate) const MACB_CLK_DIV64: u32 = 3; - -/* GEM specific constants for CLK. */ -pub(crate) const GEM_CLK_DIV8: u32 = 0; -pub(crate) const GEM_CLK_DIV16: u32 = 1; -pub(crate) const GEM_CLK_DIV32: u32 = 2; -pub(crate) const GEM_CLK_DIV48: u32 = 3; -pub(crate) const GEM_CLK_DIV64: u32 = 4; -pub(crate) const GEM_CLK_DIV96: u32 = 5; -pub(crate) const GEM_CLK_DIV128: u32 = 6; -pub(crate) const GEM_CLK_DIV224: u32 = 7; - -/* Constants for MAN register */ -pub(crate) const MACB_MAN_C22_SOF: u32 = 1; -pub(crate) const MACB_MAN_C22_WRITE: u32 = 1; -pub(crate) const MACB_MAN_C22_READ: u32 = 2; -pub(crate) const MACB_MAN_C22_CODE: u32 = 2; - -pub(crate) const MACB_MAN_C45_SOF: u32 = 0; -pub(crate) const MACB_MAN_C45_ADDR: u32 = 0; -pub(crate) const MACB_MAN_C45_WRITE: u32 = 1; -pub(crate) const MACB_MAN_C45_POST_READ_INCR: u32 = 2; -pub(crate) const MACB_MAN_C45_READ: u32 = 3; -pub(crate) const MACB_MAN_C45_CODE: u32 = 2; - -/* Capability mask bits */ -pub(crate) const MACB_CAPS_ISR_CLEAR_ON_WRITE: u32 = 0x00000001; -pub(crate) const MACB_CAPS_USRIO_HAS_CLKEN: u32 = 0x00000002; -pub(crate) const MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII: u32 = 0x00000004; -pub(crate) const MACB_CAPS_NO_GIGABIT_HALF: u32 = 0x00000008; -pub(crate) const MACB_CAPS_USRIO_DISABLED: u32 = 0x00000010; -pub(crate) const MACB_CAPS_JUMBO: u32 = 0x00000020; -pub(crate) const MACB_CAPS_GEM_HAS_PTP: u32 = 0x00000040; -pub(crate) const MACB_CAPS_BD_RD_PREFETCH: u32 = 0x00000080; -pub(crate) const MACB_CAPS_NEEDS_RSTONUBR: u32 = 0x00000100; -pub(crate) const MACB_CAPS_MIIONRGMII: u32 = 0x00000200; -pub(crate) const MACB_CAPS_CLK_HW_CHG: u32 = 0x04000000; -pub(crate) const MACB_CAPS_MACB_IS_EMAC: u32 = 0x08000000; -pub(crate) const MACB_CAPS_FIFO_MODE: u32 = 0x10000000; -pub(crate) const MACB_CAPS_GIGABIT_MODE_AVAILABLE: u32 = 0x20000000; -pub(crate) const MACB_CAPS_SG_DISABLED: u32 = 0x40000000; -pub(crate) const MACB_CAPS_MACB_IS_GEM: u32 = 0x80000000; -pub(crate) const MACB_CAPS_PCS: u32 = 0x01000000; -pub(crate) const MACB_CAPS_HIGH_SPEED: u32 = 0x02000000; - -/* LSO settings */ -pub(crate) const MACB_LSO_UFO_ENABLE: u32 = 0x01; -pub(crate) const MACB_LSO_TSO_ENABLE: u32 = 0x02; - -/* -/* Bit manipulation macros */ -pub(crate) const MACB_BIT(name) \ - (1 << MACB_##name##_OFFSET) -pub(crate) const MACB_BF(name,value) \ - (((value) & ((1 << MACB_##name##_SIZE) - 1)) \ - << MACB_##name##_OFFSET) -pub(crate) const MACB_BFEXT(name,value)\ - (((value) >> MACB_##name##_OFFSET) \ - & ((1 << MACB_##name##_SIZE) - 1)) -pub(crate) const MACB_BFINS(name,value,old) \ - (((old) & ~(((1 << MACB_##name##_SIZE) - 1) \ - << MACB_##name##_OFFSET)) \ - | MACB_BF(name,value)) - -pub(crate) const GEM_BIT(name) \ - (1 << GEM_##name##_OFFSET) -pub(crate) const GEM_BF(name, value) \ - (((value) & ((1 << GEM_##name##_SIZE) - 1)) \ - << GEM_##name##_OFFSET) -pub(crate) const GEM_BFEXT(name, value)\ - (((value) >> GEM_##name##_OFFSET) \ - & ((1 << GEM_##name##_SIZE) - 1)) -pub(crate) const GEM_BFINS(name, value, old) \ - (((old) & ~(((1 << GEM_##name##_SIZE) - 1) \ - << GEM_##name##_OFFSET)) \ - | GEM_BF(name, value)) - -/* Register access macros */ -pub(crate) const macb_readl(port, reg) (port)->macb_reg_readl((port), MACB_##reg) -pub(crate) const macb_writel(port, reg, value) (port)->macb_reg_writel((port), MACB_##reg, (value)) -pub(crate) const gem_readl(port, reg) (port)->macb_reg_readl((port), GEM_##reg) -pub(crate) const gem_writel(port, reg, value) (port)->macb_reg_writel((port), GEM_##reg, (value)) -pub(crate) const queue_readl(queue, reg) (queue)->bp->macb_reg_readl((queue)->bp, (queue)->reg) -pub(crate) const queue_writel(queue, reg, value) (queue)->bp->macb_reg_writel((queue)->bp, (queue)->reg, (value)) -pub(crate) const gem_readl_n(port, reg, idx) (port)->macb_reg_readl((port), GEM_##reg + idx * 4) -pub(crate) const gem_writel_n(port, reg, idx, value) (port)->macb_reg_writel((port), GEM_##reg + idx * 4, (value)) - -pub(crate) const PTP_TS_BUFFER_SIZE 128 /* must be power of 2 */ - -/* struct macb_dma_desc - Hardware DMA descriptor - * @addr: DMA address of data buffer - * @ctrl: Control and status bits - */ -struct macb_dma_desc { - u32 addr; - u32 ctrl; -}; - -#ifdef MACB_EXT_DESC -pub(crate) const HW_DMA_CAP_32B 0 -pub(crate) const HW_DMA_CAP_64B (1 << 0) -pub(crate) const HW_DMA_CAP_PTP (1 << 1) -pub(crate) const HW_DMA_CAP_64B_PTP (HW_DMA_CAP_64B | HW_DMA_CAP_PTP) - -struct macb_dma_desc_64 { - u32 addrh; - u32 resvd; -}; - -struct macb_dma_desc_ptp { - u32 ts_1; - u32 ts_2; -}; - -struct gem_tx_ts { - struct sk_buff *skb; - struct macb_dma_desc_ptp desc_ptp; -}; -#endif - -*/ -/* DMA descriptor bitfields */ -pub(crate) const MACB_RX_USED_OFFSET: u32 = 0; -pub(crate) const MACB_RX_USED_SIZE: u32 = 1; -pub(crate) const MACB_RX_WRAP_OFFSET: u32 = 1; -pub(crate) const MACB_RX_WRAP_SIZE: u32 = 1; -pub(crate) const MACB_RX_WADDR_OFFSET: u32 = 2; -pub(crate) const MACB_RX_WADDR_SIZE: u32 = 30; - -pub(crate) const MACB_RX_FRMLEN_OFFSET: u32 = 0; -pub(crate) const MACB_RX_FRMLEN_SIZE: u32 = 12; -pub(crate) const MACB_RX_OFFSET_OFFSET: u32 = 12; -pub(crate) const MACB_RX_OFFSET_SIZE: u32 = 2; -pub(crate) const MACB_RX_SOF_OFFSET: u32 = 14; -pub(crate) const MACB_RX_SOF_SIZE: u32 = 1; -pub(crate) const MACB_RX_EOF_OFFSET: u32 = 15; -pub(crate) const MACB_RX_EOF_SIZE: u32 = 1; -pub(crate) const MACB_RX_CFI_OFFSET: u32 = 16; -pub(crate) const MACB_RX_CFI_SIZE: u32 = 1; -pub(crate) const MACB_RX_VLAN_PRI_OFFSET: u32 = 17; -pub(crate) const MACB_RX_VLAN_PRI_SIZE: u32 = 3; -pub(crate) const MACB_RX_PRI_TAG_OFFSET: u32 = 20; -pub(crate) const MACB_RX_PRI_TAG_SIZE: u32 = 1; -pub(crate) const MACB_RX_VLAN_TAG_OFFSET: u32 = 21; -pub(crate) const MACB_RX_VLAN_TAG_SIZE: u32 = 1; -pub(crate) const MACB_RX_TYPEID_MATCH_OFFSET: u32 = 22; -pub(crate) const MACB_RX_TYPEID_MATCH_SIZE: u32 = 1; -pub(crate) const MACB_RX_SA4_MATCH_OFFSET: u32 = 23; -pub(crate) const MACB_RX_SA4_MATCH_SIZE: u32 = 1; -pub(crate) const MACB_RX_SA3_MATCH_OFFSET: u32 = 24; -pub(crate) const MACB_RX_SA3_MATCH_SIZE: u32 = 1; -pub(crate) const MACB_RX_SA2_MATCH_OFFSET: u32 = 25; -pub(crate) const MACB_RX_SA2_MATCH_SIZE: u32 = 1; -pub(crate) const MACB_RX_SA1_MATCH_OFFSET: u32 = 26; -pub(crate) const MACB_RX_SA1_MATCH_SIZE: u32 = 1; -pub(crate) const MACB_RX_EXT_MATCH_OFFSET: u32 = 28; -pub(crate) const MACB_RX_EXT_MATCH_SIZE: u32 = 1; -pub(crate) const MACB_RX_UHASH_MATCH_OFFSET: u32 = 29; -pub(crate) const MACB_RX_UHASH_MATCH_SIZE: u32 = 1; -pub(crate) const MACB_RX_MHASH_MATCH_OFFSET: u32 = 30; -pub(crate) const MACB_RX_MHASH_MATCH_SIZE: u32 = 1; -pub(crate) const MACB_RX_BROADCAST_OFFSET: u32 = 31; -pub(crate) const MACB_RX_BROADCAST_SIZE: u32 = 1; - -pub(crate) const MACB_RX_FRMLEN_MASK: u32 = 0xFFF; -pub(crate) const MACB_RX_JFRMLEN_MASK: u32 = 0x3FFF; - -/* RX checksum offload disabled: bit 24 clear in NCFGR */ -pub(crate) const GEM_RX_TYPEID_MATCH_OFFSET: u32 = 22; -pub(crate) const GEM_RX_TYPEID_MATCH_SIZE: u32 = 2; - -/* RX checksum offload enabled: bit 24 set in NCFGR */ -pub(crate) const GEM_RX_CSUM_OFFSET: u32 = 22; -pub(crate) const GEM_RX_CSUM_SIZE: u32 = 2; - -pub(crate) const MACB_TX_FRMLEN_OFFSET: u32 = 0; -pub(crate) const MACB_TX_FRMLEN_SIZE: u32 = 11; -pub(crate) const MACB_TX_LAST_OFFSET: u32 = 15; -pub(crate) const MACB_TX_LAST_SIZE: u32 = 1; -pub(crate) const MACB_TX_NOCRC_OFFSET: u32 = 16; -pub(crate) const MACB_TX_NOCRC_SIZE: u32 = 1; -pub(crate) const MACB_MSS_MFS_OFFSET: u32 = 16; -pub(crate) const MACB_MSS_MFS_SIZE: u32 = 14; -pub(crate) const MACB_TX_LSO_OFFSET: u32 = 17; -pub(crate) const MACB_TX_LSO_SIZE: u32 = 2; -pub(crate) const MACB_TX_TCP_SEQ_SRC_OFFSET: u32 = 19; -pub(crate) const MACB_TX_TCP_SEQ_SRC_SIZE: u32 = 1; -pub(crate) const MACB_TX_BUF_EXHAUSTED_OFFSET: u32 = 27; -pub(crate) const MACB_TX_BUF_EXHAUSTED_SIZE: u32 = 1; -pub(crate) const MACB_TX_UNDERRUN_OFFSET: u32 = 28; -pub(crate) const MACB_TX_UNDERRUN_SIZE: u32 = 1; -pub(crate) const MACB_TX_ERROR_OFFSET: u32 = 29; -pub(crate) const MACB_TX_ERROR_SIZE: u32 = 1; -pub(crate) const MACB_TX_WRAP_OFFSET: u32 = 30; -pub(crate) const MACB_TX_WRAP_SIZE: u32 = 1; -pub(crate) const MACB_TX_USED_OFFSET: u32 = 31; -pub(crate) const MACB_TX_USED_SIZE: u32 = 1; - -pub(crate) const GEM_TX_FRMLEN_OFFSET: u32 = 0; -pub(crate) const GEM_TX_FRMLEN_SIZE: u32 = 14; - -/* Buffer descriptor constants */ -pub(crate) const GEM_RX_CSUM_NONE: u32 = 0; -pub(crate) const GEM_RX_CSUM_IP_ONLY: u32 = 1; -pub(crate) const GEM_RX_CSUM_IP_TCP: u32 = 2; -pub(crate) const GEM_RX_CSUM_IP_UDP: u32 = 3; - -/* limit RX checksum offload to TCP and UDP packets */ -pub(crate) const GEM_RX_CSUM_CHECKED_MASK: u32 = 2; - -/* Scaled PPM fraction */ -pub(crate) const PPM_FRACTION: u32 = 16; - -/* -static inline bool macb_is_gem(struct macb *bp) -{ - return !!(bp->caps & MACB_CAPS_MACB_IS_GEM); -} - -static inline bool gem_has_ptp(struct macb *bp) -{ - return !!(bp->caps & MACB_CAPS_GEM_HAS_PTP); -} -*/ \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 000000000..df30a42e4 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,191 @@ +use core::arch::asm; + +// PhytiumPi +pub const CORE0_AFF: u64 = 0x000; +pub const CORE1_AFF: u64 = 0x100; +pub const CORE2_AFF: u64 = 0x200; +pub const CORE3_AFF: u64 = 0x201; +pub const FCORE_NUM: u64 = 4; + +/// Read reg: MPIDR_EL1 +pub(crate) fn read_mpidr() -> u64 { + let mut reg_r = 0; + unsafe { + core::arch::asm!("mrs {}, MPIDR_EL1", out(reg) reg_r); + } + reg_r +} + +/// Converts MPIDR to CPU ID +pub(crate) fn mpidr2cpuid(mpidr: u64) -> usize { + // RK3588 + //((mpidr >> 8) & 0xff) as usize + + // Qemu + //(mpidr & 0xffffff & 0xff) as usize + + // PhytiumPi + match (mpidr & 0xfff) { + CORE0_AFF => 0, + CORE1_AFF => 1, + CORE2_AFF => 2, + CORE3_AFF => 3, + _ => { + error!("Failed to get PhytiumPi CPU Id from mpidr={:#x}", mpidr); + 0 + } + } +} + +pub(crate) fn get_cpu_id() -> usize { + let mpidr = read_mpidr(); + mpidr2cpuid(mpidr) +} + +/// Data Synchronization Barrier +pub fn DSB() { + unsafe { + core::arch::asm!("dsb sy"); + } +} + +// pseudo assembler instructions +pub fn MFCPSR() -> u32{ + let mut rval: u32 = 0; +unsafe { + asm!("mrs {0:x}, DAIF", out(reg) rval); +} + rval +} + + +pub fn MTCPSR(val: u32) { +unsafe { + asm!("msr DAIF, {0:x}", in(reg) val); +} +} + +pub fn MTCPDC_CIVAC(adr: u64) { + unsafe { + asm!("dc CIVAC, {}", in(reg) adr); + } +} + +/// CACHE of PhytiumPi +pub const CACHE_LINE_ADDR_MASK: u64 = 0x3F; +pub const CACHE_LINE: u64 = 64; + +/// Mask IRQ and FIQ interrupts in cpsr +pub const IRQ_FIQ_MASK: u32 = 0xC0; + +/// dc civac, virt_addr 通过虚拟地址清除和无效化cache +/// adr: 64bit start address of the range to be invalidated. +/// len: Length of the range to be invalidated in bytes. +pub fn FCacheDCacheInvalidateRange(mut adr: u64, len: u64) +{ + let end: u64 = adr + len; + adr = adr & (!CACHE_LINE_ADDR_MASK); + let currmask: u32 = MFCPSR(); + MTCPSR(currmask | IRQ_FIQ_MASK); + if (len != 0) + { + while adr < end { + MTCPDC_CIVAC(adr); /* Clean and Invalidate data cache by address to Point of Coherency */ + adr += CACHE_LINE; + } + } + /* Wait for invalidate to complete */ + DSB(); + MTCPSR(currmask); +} + +use aarch64_cpu::registers::{CNTVCT_EL0, CNTFRQ_EL0, Readable}; +use alloc::boxed::Box; + +#[inline] +pub fn now_tsc() -> u64 { + CNTVCT_EL0.get() +} + +#[inline] +pub fn timer_freq() -> u64 { + CNTFRQ_EL0.get() as u64 +} + +// 纳秒(ns) +#[inline] +pub fn now_ns() -> u64 { + let freq = timer_freq(); + now_tsc() * (1_000_000_000 / freq) +} + +pub fn ticks_to_nanos(ticks: u64) -> u64 { + let freq = timer_freq(); + ticks * (1_000_000_000 / freq) +} + +// 微秒(us) +pub fn usdelay(us: u64) { + let mut current_ticks: u64 = now_tsc(); + let delay2 = current_ticks + us * (timer_freq() / 1000000); + + while delay2 >= current_ticks { + core::hint::spin_loop(); + current_ticks = now_tsc(); + } + + trace!("usdelay current_ticks: {}", current_ticks); +} + +// 毫秒(ms) +#[allow(unused)] +pub fn msdelay(ms: u64) { + usdelay(ms * 1000); +} + +#[linkage = "weak"] +#[export_name = "phys_to_virt"] +pub fn phys_to_virt(addr: usize) -> usize { + addr +} + +/// 申请DMA内存页 +#[linkage = "weak"] +#[export_name = "dma_alloc_coherent"] +pub fn dma_alloc_coherent(pages: usize) -> (usize, usize) { + let paddr: Box<[u32]> = if pages == 1 { + Box::new([0; 1024]) // 4096 + } else if pages == 8 { + Box::new([0; 1024 * 8]) // 4096 + } else { + warn!("Alloc {} pages failed", pages); + Box::new([0; 1024]) + }; + + let len = paddr.len(); + + let paddr = Box::into_raw(paddr) as *const u32 as usize; + //let vaddr = phys_to_virt(paddr); + let vaddr = paddr; + trace!("dma alloc paddr: {:#x}, len={}", paddr, len); + + (vaddr, paddr) +} + +/// 释放DMA内存页 +#[linkage = "weak"] +#[export_name = "dma_free_coherent"] +pub fn dma_free_coherent(vaddr: usize, pages: usize) { + let palloc = vaddr as *mut [u32; 1024]; + unsafe{ drop(Box::from_raw(palloc)); } +} + +/// 请求分配irq +#[linkage = "weak"] +#[export_name = "dma_request_irq"] +pub fn dma_request_irq(irq: usize, handler: fn(u64)) { + unimplemented!() +} + +// 路由中断到指定的cpu,或所有的cpu +//pub(crate) fn InterruptSetTargetCpus() {} From 5d906ed7004321ec066066fba420ff2b3d75f6b1 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Sun, 19 Jan 2025 17:11:21 +0800 Subject: [PATCH 005/132] Adapted initialization fns for RustOS --- README.md | 3 +- rust-toolchain | 2 +- src/fxmac.rs | 2508 +++++++++++++++++++++++---------------------- src/fxmac_dma.rs | 16 +- src/fxmac_intr.rs | 6 +- src/lib.rs | 3 + src/utils.rs | 12 +- 7 files changed, 1334 insertions(+), 1216 deletions(-) diff --git a/README.md b/README.md index b1d5d92af..51b937b18 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Cadence Macb ethernet driver +# FXMAC ethernet driver fxmac ethernet Rust driver on PhytiumPi board. ### Quick Start @@ -31,4 +31,5 @@ cadence_macb::eth_macb_ops::macb_recv(&mut macb_device, &mut rx_buffer); ``` ## Reference +* phytium-standalone-sdk * Linux and U-Boot C code diff --git a/rust-toolchain b/rust-toolchain index 2b7ce9778..25382af09 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2024-08-15" +channel = "nightly-2025-01-18" components = [ "llvm-tools-preview" ] targets = [ "aarch64-unknown-none-softfloat" ] profile = "minimal" diff --git a/src/fxmac.rs b/src/fxmac.rs index e5aff0234..c58921661 100644 --- a/src/fxmac.rs +++ b/src/fxmac.rs @@ -1,19 +1,18 @@ - use core::sync::atomic::Ordering; -use alloc::boxed::Box; -use log::*; use crate::fxmac_const::*; -use crate::fxmac_phy::*; use crate::fxmac_dma::*; use crate::fxmac_intr::*; +use crate::fxmac_phy::*; use crate::utils::*; +use alloc::boxed::Box; +use log::*; -pub const FXMAC_HANDLER_DMASEND: u32 = 1; /* 发送中断 */ -pub const FXMAC_HANDLER_DMARECV: u32 = 2; /* 接收中断 */ -pub const FXMAC_HANDLER_ERROR: u32 = 3; /* 异常中断 */ -pub const FXMAC_HANDLER_LINKCHANGE: u32 = 4; /* 连接状态 */ -pub const FXMAC_HANDLER_RESTART: u32 = 5; /* 发送描述符队列发生异常 */ +pub const FXMAC_HANDLER_DMASEND: u32 = 1; /* 发送中断 */ +pub const FXMAC_HANDLER_DMARECV: u32 = 2; /* 接收中断 */ +pub const FXMAC_HANDLER_ERROR: u32 = 3; /* 异常中断 */ +pub const FXMAC_HANDLER_LINKCHANGE: u32 = 4; /* 连接状态 */ +pub const FXMAC_HANDLER_RESTART: u32 = 5; /* 发送描述符队列发生异常 */ pub const FXMAC_LINKDOWN: u32 = 0; pub const FXMAC_LINKUP: u32 = 1; @@ -37,7 +36,7 @@ pub struct FXmac { pub is_ready: u32, /* Device is ininitialized and ready*/ pub is_started: u32, pub link_status: u32, /* indicates link status ,FXMAC_LINKUP is link up ,FXMAC_LINKDOWN is link down,FXMAC_NEGOTIATING is need to negotiating*/ - pub options: u32, + pub options: u32, pub mask: u32, /* indicates intr mask */ pub caps: u32, /* Capability mask bits */ @@ -50,10 +49,14 @@ pub struct FXmac { pub max_mtu_size: u32, pub max_frame_size: u32, - pub phy_address: u32, /* phy address */ - pub rxbuf_mask: u32, /* 1000,100,10 */ + pub phy_address: u32, /* phy address */ + pub rxbuf_mask: u32, /* 1000,100,10 */ } +unsafe impl Send for FXmac {} +unsafe impl Sync for FXmac {} + + pub struct FXmacConfig { pub instance_id: u32, /* Id of device*/ pub base_address: u64, @@ -71,7 +74,7 @@ pub struct FXmacConfig { pub dma_brust_length: u32, /* burst length */ pub network_default_config: u32, pub queue_irq_num: [u32; FXMAC_QUEUE_MAX_NUM as usize], /* mac0 8个 ,其他的 4个 */ - pub caps: u32, /* used to configure tail ptr feature */ + pub caps: u32, /* used to configure tail ptr feature */ pub mac: [u8; 6], } @@ -82,8 +85,7 @@ pub struct FXmacQueue { /// Interface Mode definitions #[derive(Debug, Clone, Copy, PartialEq)] -pub enum FXmacPhyInterface -{ +pub enum FXmacPhyInterface { FXMAC_PHY_INTERFACE_MODE_SGMII = 0, FXMAC_PHY_INTERFACE_MODE_RMII = 1, FXMAC_PHY_INTERFACE_MODE_RGMII = 2, @@ -93,7 +95,6 @@ pub enum FXmacPhyInterface FXMAC_PHY_INTERFACE_MODE_2500BASEX = 6, } - pub fn read_reg(src: *const T) -> T { unsafe { core::ptr::read_volatile(phys_to_virt(src as usize) as *const T) } } @@ -104,29 +105,29 @@ pub fn write_reg(dst: *mut T, value: T) { } } -pub fn xmac_init() -> i32 { - let mut hwaddr: [u8; 6] = [0x98, 0x0e, 0x24, 0x00, 0x11, 0x0]; - -/* -FXmacConfig mac_config: -mac_config.instance_id=0, -mac_config.base_address=0x3200c000, -mac_config.extral_mode_base=0x3200dc00, -mac_config.extral_loopback_base=0x3200dc04, -mac_config.interface=0, -mac_config.speed=100, -mac_config.duplex=1, -mac_config.auto_neg=0, -mac_config.pclk_hz=50000000, -mac_config.max_queue_num=4, -mac_config.tx_queue_id=0, -mac_config.rx_queue_id=0 -mac_config.hotplug_irq_num=83, -mac_config.dma_brust_length=16, -mac_config.network_default_config=0x37f0, -mac_config.queue_irq_num[0]=87, -mac_config.caps=0 -*/ +pub fn xmac_init(hwaddr: &[u8; 6]) -> &'static mut FXmac { // i32 + //let mut hwaddr: [u8; 6] = [0x98, 0x0e, 0x24, 0x00, 0x11, 0x0]; + + /* + FXmacConfig mac_config: + mac_config.instance_id=0, + mac_config.base_address=0x3200c000, + mac_config.extral_mode_base=0x3200dc00, + mac_config.extral_loopback_base=0x3200dc04, + mac_config.interface=0, + mac_config.speed=100, + mac_config.duplex=1, + mac_config.auto_neg=0, + mac_config.pclk_hz=50000000, + mac_config.max_queue_num=4, + mac_config.tx_queue_id=0, + mac_config.rx_queue_id=0 + mac_config.hotplug_irq_num=83, + mac_config.dma_brust_length=16, + mac_config.network_default_config=0x37f0, + mac_config.queue_irq_num[0]=87, + mac_config.caps=0 + */ let mut mac_config: FXmacConfig = FXmacConfig { instance_id: FXMAC0_ID, base_address: FXMAC0_BASE_ADDR as u64, @@ -143,9 +144,14 @@ mac_config.caps=0 hotplug_irq_num: FXMAC0_HOTPLUG_IRQ_NUM, dma_brust_length: 16, network_default_config: FXMAC_DEFAULT_OPTIONS, - queue_irq_num: [FXMAC0_QUEUE0_IRQ_NUM, FXMAC0_QUEUE1_IRQ_NUM, FXMAC0_QUEUE2_IRQ_NUM, FXMAC0_QUEUE3_IRQ_NUM], + queue_irq_num: [ + FXMAC0_QUEUE0_IRQ_NUM, + FXMAC0_QUEUE1_IRQ_NUM, + FXMAC0_QUEUE2_IRQ_NUM, + FXMAC0_QUEUE3_IRQ_NUM, + ], caps: 0, - mac: hwaddr, + mac: *hwaddr, }; let mut xmac = FXmac { @@ -156,14 +162,20 @@ mac_config.caps=0 options: 0, mask: 0, caps: 0, - lwipport: FXmacLwipPort{ + lwipport: FXmacLwipPort { buffer: FXmacNetifBuffer::default(), feature: FXMAC_LWIP_PORT_CONFIG_MULTICAST_ADDRESS_FILITER, - hwaddr, + hwaddr: *hwaddr, recv_flg: 0, }, - tx_bd_queue: FXmacQueue{ queue_id: 0, bdring: FXmacBdRing::default()}, - rx_bd_queue: FXmacQueue{ queue_id: 0, bdring: FXmacBdRing::default()}, + tx_bd_queue: FXmacQueue { + queue_id: 0, + bdring: FXmacBdRing::default(), + }, + rx_bd_queue: FXmacQueue { + queue_id: 0, + bdring: FXmacBdRing::default(), + }, moudle_id: 0, max_mtu_size: 0, max_frame_size: 0, @@ -171,109 +183,109 @@ mac_config.caps=0 rxbuf_mask: 0, }; -// xmac_config: interface=FXMAC_PHY_INTERFACE_MODE_SGMII, autonegotiation=0, phy_speed=FXMAC_PHY_SPEED_100M, phy_duplex=FXMAC_PHY_FULL_DUPLEX -// FXmacDmaReset, moudle_id=12, max_frame_size=1518, max_queue_num=4 (或16), dma_brust_length=16 -// network_default_config = 0x37f0, base_address=0x3200c000,FXMAC_RXBUF_HASH_MASK: GENMASK(30, 29)= 0x60000000 (0b 0110_0000_0000_0000_0000000000000000) + // xmac_config: interface=FXMAC_PHY_INTERFACE_MODE_SGMII, autonegotiation=0, phy_speed=FXMAC_PHY_SPEED_100M, phy_duplex=FXMAC_PHY_FULL_DUPLEX + // FXmacDmaReset, moudle_id=12, max_frame_size=1518, max_queue_num=4 (或16), dma_brust_length=16 + // network_default_config = 0x37f0, base_address=0x3200c000,FXMAC_RXBUF_HASH_MASK: GENMASK(30, 29)= 0x60000000 (0b 0110_0000_0000_0000_0000000000000000) // mii_interface = 1 = FXMAC_LWIP_PORT_INTERFACE_SGMII; + // FXmacLwipPortInit(): + /* step 1: initialize instance */ + /* step 2: depend on config set some options : JUMBO / IGMP */ + /* step 3: FXmacSelectClk */ + /* step 4: FXmacInitInterface */ + /* step 5: initialize phy */ + /* step 6: initialize dma */ + /* step 7: initialize interrupt */ + /* step 8: start mac */ -// FXmacLwipPortInit(): -/* step 1: initialize instance */ -/* step 2: depend on config set some options : JUMBO / IGMP */ -/* step 3: FXmacSelectClk */ -/* step 4: FXmacInitInterface */ -/* step 5: initialize phy */ -/* step 6: initialize dma */ -/* step 7: initialize interrupt */ -/* step 8: start mac */ - - - -let mut status: u32 = 0; - -// Reset the hardware and set default options -//xmac.link_status = FXMAC_LINKDOWN; -//xmac.is_ready = FT_COMPONENT_IS_READY; - -FXmacReset(&mut xmac); + let mut status: u32 = 0; -// irq_handler = (FXmacIrqHandler)FXmacIrqStubHandler; -// interrupts bit mask -xmac.mask = FXMAC_IXR_LINKCHANGE_MASK | FXMAC_IXR_TX_ERR_MASK | FXMAC_IXR_RX_ERR_MASK | FXMAC_IXR_RXCOMPL_MASK; // FXMAC_INTR_MASK -if (xmac.config.caps & FXMAC_CAPS_TAILPTR) != 0 -{ - FXmacSetOptions(&mut xmac, FXMAC_TAIL_PTR_OPTION, 0); - xmac.mask &= !FXMAC_IXR_TXUSED_MASK; -} + // Reset the hardware and set default options + //xmac.link_status = FXMAC_LINKDOWN; + //xmac.is_ready = FT_COMPONENT_IS_READY; + + FXmacReset(&mut xmac); + + // irq_handler = (FXmacIrqHandler)FXmacIrqStubHandler; + // interrupts bit mask + xmac.mask = FXMAC_IXR_LINKCHANGE_MASK + | FXMAC_IXR_TX_ERR_MASK + | FXMAC_IXR_RX_ERR_MASK + | FXMAC_IXR_RXCOMPL_MASK; // FXMAC_INTR_MASK + if (xmac.config.caps & FXMAC_CAPS_TAILPTR) != 0 { + FXmacSetOptions(&mut xmac, FXMAC_TAIL_PTR_OPTION, 0); + xmac.mask &= !FXMAC_IXR_TXUSED_MASK; + } -// xmac.lwipport.feature = LWIP_PORT_MODE_MULTICAST_ADDRESS_FILITER; + // xmac.lwipport.feature = LWIP_PORT_MODE_MULTICAST_ADDRESS_FILITER; -FxmacFeatureSetOptions(xmac.lwipport.feature, &mut xmac); + FxmacFeatureSetOptions(xmac.lwipport.feature, &mut xmac); -status = FXmacSetMacAddress( &xmac.lwipport.hwaddr, 0); + status = FXmacSetMacAddress(&xmac.lwipport.hwaddr, 0); -//mac_config.interface = FXMAC_PHY_INTERFACE_MODE_SGMII; + //mac_config.interface = FXMAC_PHY_INTERFACE_MODE_SGMII; -if xmac.config.interface != FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_USXGMII -{ - /* initialize phy */ - status = FXmacPhyInit(&mut xmac, XMAC_PHY_RESET_ENABLE); - if status != 0 - { - warn!("FXmacPhyInit is error"); + if xmac.config.interface != FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_USXGMII { + /* initialize phy */ + status = FXmacPhyInit(&mut xmac, XMAC_PHY_RESET_ENABLE); + if status != 0 { + warn!("FXmacPhyInit is error"); + } + } else { + info!("interface == FXMAC_PHY_INTERFACE_MODE_USXGMII"); } -} else { - info!("interface == FXMAC_PHY_INTERFACE_MODE_USXGMII"); -} - -FXmacSelectClk(&mut xmac); -FXmacInitInterface(&mut xmac); - -// initialize dma -let mut dmacrreg: u32 = read_reg((xmac.config.base_address + FXMAC_DMACR_OFFSET) as *const u32); -dmacrreg &= !(FXMAC_DMACR_BLENGTH_MASK); -dmacrreg = dmacrreg | FXMAC_DMACR_INCR16_AHB_AXI_BURST; /* Attempt to use bursts of up to 16. */ -write_reg((xmac.config.base_address + FXMAC_DMACR_OFFSET) as *mut u32, dmacrreg); -FXmacInitDma(&mut xmac); + FXmacSelectClk(&mut xmac); + FXmacInitInterface(&mut xmac); -// initialize interrupt -// 网卡中断初始化设置 -FXmacSetupIsr(&mut xmac); + // initialize dma + let mut dmacrreg: u32 = read_reg((xmac.config.base_address + FXMAC_DMACR_OFFSET) as *const u32); + dmacrreg &= !(FXMAC_DMACR_BLENGTH_MASK); + dmacrreg = dmacrreg | FXMAC_DMACR_INCR16_AHB_AXI_BURST; /* Attempt to use bursts of up to 16. */ + write_reg( + (xmac.config.base_address + FXMAC_DMACR_OFFSET) as *mut u32, + dmacrreg, + ); -// end of FXmacLwipPortInit() - -if (xmac.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_UNICAST_ADDRESS_FILITER) != 0 { -debug!("Set unicast hash table"); -FXmac_SetHash(&mut xmac, &hwaddr); -} + FXmacInitDma(&mut xmac); -/* 注册了 lwip_port->ops: -ethernetif_link_detect() -ethernetif_input() -ethernetif_deinit() -ethernetif_start() -> FXmacLwipPortStart() -> FXmacStart() -ethernetif_debug() -*/ + // initialize interrupt + // 网卡中断初始化设置 + FXmacSetupIsr(&mut xmac); + // end of FXmacLwipPortInit() + if (xmac.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_UNICAST_ADDRESS_FILITER) != 0 { + debug!("Set unicast hash table"); + FXmac_SetHash(&mut xmac, &hwaddr); + } -///////// + /* 注册了 lwip_port->ops: + ethernetif_link_detect() + ethernetif_input() + ethernetif_deinit() + ethernetif_start() -> FXmacLwipPortStart() -> FXmacStart() + ethernetif_debug() + */ -// ethernetif_start() -// start mac -FXmacStart(&mut xmac); + ///////// + // ethernetif_start() + // start mac + FXmacStart(&mut xmac); -// 开始发包的函数:FXmacLwipPortTx()->FXmacSgsend() -> FXmacSendHandler() -> FXmacProcessSentBds() -// 触发中断函数:FXmacIntrHandler() -// 收包handle: FXmacRecvIsrHandler()->FXmacRecvHandler + // 开始发包的函数:FXmacLwipPortTx()->FXmacSgsend() -> FXmacSendHandler() -> FXmacProcessSentBds() + // 触发中断函数:FXmacIntrHandler() + // 收包handle: FXmacRecvIsrHandler()->FXmacRecvHandler + //XMAC.store(Box::into_raw(Box::new(xmac)), Ordering::Relaxed); -XMAC.store(Box::into_raw(Box::new(xmac)), Ordering::Relaxed); + // Box::leak方法,它可以将一个变量从内存中泄漏, 将其变为'static生命周期,因此可以赋值给全局静态变量 + let xmac_ref = Box::leak(Box::new(xmac)); + XMAC.store(xmac_ref as *mut FXmac, Ordering::Relaxed); -0 + xmac_ref } /// FXmacStart(): Start mac @@ -282,68 +294,82 @@ XMAC.store(Box::into_raw(Box::new(xmac)), Ordering::Relaxed); /// - Enable receiver if FXMAC_RECEIVER_ENABLE_OPTION is set /// - Start the SG DMA send and receive channels and enable the device /// interrupt -pub fn FXmacStart(instance_p: &mut FXmac) -{ - +pub fn FXmacStart(instance_p: &mut FXmac) { assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); - /* clear any existed int status */ - write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_ALL_MASK); - - /* Enable transmitter if not already enabled */ - if (instance_p.config.network_default_config & FXMAC_TRANSMITTER_ENABLE_OPTION as u32) != 0 - { - let reg_val = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); - if (reg_val & FXMAC_NWCTRL_TXEN_MASK) == 0 - { - write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_val | FXMAC_NWCTRL_TXEN_MASK as u32); - } - } - - /* Enable receiver if not already enabled */ - if (instance_p.config.network_default_config & FXMAC_RECEIVER_ENABLE_OPTION) != 0 - { - - let reg_val = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); - info!("Enable receiver, FXMAC_NWCTRL_OFFSET = {:#x}", reg_val); - if (reg_val & FXMAC_NWCTRL_RXEN_MASK) == 0 - { - write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_val | FXMAC_NWCTRL_RXEN_MASK as u32); - } - } - info!("FXMAC_NWCTRL_OFFSET = {:#x}", read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32)); - - info!("Enable TX and RX by Mask={:#x}", instance_p.mask); - - // Enable TX and RX interrupt - //FXMAC_INT_ENABLE(instance_p, instance_p->mask); - // Enable interrupts specified in 'Mask'. The corresponding interrupt for each bit set to 1 in 'Mask', will be enabled. - write_reg((instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, - instance_p.mask & FXMAC_IXR_ALL_MASK); - - // Mark as started - instance_p.is_started = FT_COMPONENT_IS_STARTED; + /* clear any existed int status */ + write_reg( + (instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, + FXMAC_IXR_ALL_MASK, + ); + + /* Enable transmitter if not already enabled */ + if (instance_p.config.network_default_config & FXMAC_TRANSMITTER_ENABLE_OPTION as u32) != 0 { + let reg_val = + read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + if (reg_val & FXMAC_NWCTRL_TXEN_MASK) == 0 { + write_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, + reg_val | FXMAC_NWCTRL_TXEN_MASK as u32, + ); + } + } + /* Enable receiver if not already enabled */ + if (instance_p.config.network_default_config & FXMAC_RECEIVER_ENABLE_OPTION) != 0 { + let reg_val = + read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + info!("Enable receiver, FXMAC_NWCTRL_OFFSET = {:#x}", reg_val); + if (reg_val & FXMAC_NWCTRL_RXEN_MASK) == 0 { + write_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, + reg_val | FXMAC_NWCTRL_RXEN_MASK as u32, + ); + } + } + info!( + "FXMAC_NWCTRL_OFFSET = {:#x}", + read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32) + ); + + info!("Enable TX and RX by Mask={:#x}", instance_p.mask); + + // Enable TX and RX interrupt + //FXMAC_INT_ENABLE(instance_p, instance_p->mask); + // Enable interrupts specified in 'Mask'. The corresponding interrupt for each bit set to 1 in 'Mask', will be enabled. + write_reg( + (instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, + instance_p.mask & FXMAC_IXR_ALL_MASK, + ); + + // Mark as started + instance_p.is_started = FT_COMPONENT_IS_STARTED; } /// Gracefully stop the Ethernet MAC as follows: /// - Disable all interrupts from this device /// - Stop DMA channels /// - Disable the tansmitter and receiver -pub fn FXmacStop(instance_p: &mut FXmac) -{ +pub fn FXmacStop(instance_p: &mut FXmac) { assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); - // Disable all interrupts - write_reg((instance_p.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_ALL_MASK); - - /* Disable the receiver & transmitter */ - let mut reg_val: u32 = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); - reg_val &= !(FXMAC_NWCTRL_RXEN_MASK as u32); - reg_val &= !(FXMAC_NWCTRL_TXEN_MASK as u32); - write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_val); - - // Mark as stopped - instance_p.is_started = 0; + // Disable all interrupts + write_reg( + (instance_p.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, + FXMAC_IXR_ALL_MASK, + ); + + /* Disable the receiver & transmitter */ + let mut reg_val: u32 = + read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + reg_val &= !(FXMAC_NWCTRL_RXEN_MASK as u32); + reg_val &= !(FXMAC_NWCTRL_TXEN_MASK as u32); + write_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, + reg_val, + ); + + // Mark as stopped + instance_p.is_started = 0; } /* @@ -363,15 +389,19 @@ pub fn FXmacStop(instance_p: &mut FXmac) fn FXmacReset(instance_p: &mut FXmac) { let mut mac_addr: [u8; 6] = [0; 6]; - + /* Stop the device and reset hardware */ FXmacStop(instance_p); // Module identification number // instance_p->moudle_id = 12 - instance_p.moudle_id = ( read_reg((FXMAC_IOBASE + FXMAC_REVISION_REG_OFFSET) as *const u32) & - FXMAC_IDENTIFICATION_MASK ) >> 16; - info!("FXmacReset, Got Moudle IDENTIFICATION: {}", instance_p.moudle_id); + instance_p.moudle_id = (read_reg((FXMAC_IOBASE + FXMAC_REVISION_REG_OFFSET) as *const u32) + & FXMAC_IDENTIFICATION_MASK) + >> 16; + info!( + "FXmacReset, Got Moudle IDENTIFICATION: {}", + instance_p.moudle_id + ); instance_p.max_mtu_size = FXMAC_MTU; instance_p.max_frame_size = FXMAC_MAX_FRAME_SIZE; @@ -381,8 +411,8 @@ fn FXmacReset(instance_p: &mut FXmac) { instance_p.config.pclk_hz = FXMAC0_PCLK; // 50000000 - - let netctrl = (FXMAC_NWCTRL_STATCLR_MASK & !(FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK as u32)) | FXMAC_NWCTRL_MDEN_MASK; + let netctrl = (FXMAC_NWCTRL_STATCLR_MASK & !(FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK as u32)) + | FXMAC_NWCTRL_MDEN_MASK; write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, netctrl); FXmacConfigureCaps(instance_p); @@ -396,16 +426,25 @@ fn FXmacReset(instance_p: &mut FXmac) { FXmacDmaReset(instance_p); // This register, when read provides details of the status of the receive path. - write_reg((FXMAC_IOBASE + FXMAC_RXSR_OFFSET) as *mut u32, FXMAC_SR_ALL_MASK); + write_reg( + (FXMAC_IOBASE + FXMAC_RXSR_OFFSET) as *mut u32, + FXMAC_SR_ALL_MASK, + ); // write 1 ro the relavant bit location disable that particular interrupt - write_reg((FXMAC_IOBASE + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_ALL_MASK); + write_reg( + (FXMAC_IOBASE + FXMAC_IDR_OFFSET) as *mut u32, + FXMAC_IXR_ALL_MASK, + ); let reg_val: u32 = read_reg((FXMAC_IOBASE + FXMAC_ISR_OFFSET) as *const u32); write_reg((FXMAC_IOBASE + FXMAC_ISR_OFFSET) as *mut u32, reg_val); - write_reg((FXMAC_IOBASE + FXMAC_TXSR_OFFSET) as *mut u32, FXMAC_SR_ALL_MASK); - + write_reg( + (FXMAC_IOBASE + FXMAC_TXSR_OFFSET) as *mut u32, + FXMAC_SR_ALL_MASK, + ); + FXmacClearHash(); // set default mac address @@ -416,92 +455,95 @@ fn FXmacReset(instance_p: &mut FXmac) { } /* clear all counters */ - for i in 0..((FXMAC_LAST_OFFSET - FXMAC_OCTTXL_OFFSET) / 4) { + for i in 0..((FXMAC_LAST_OFFSET - FXMAC_OCTTXL_OFFSET) / 4) { read_reg((FXMAC_IOBASE + FXMAC_OCTTXL_OFFSET + (i * 4)) as *mut u32); - } + } /* Sync default options with hardware but leave receiver and * transmitter disabled. They get enabled with FXmacStart() if * FXMAC_TRANSMITTER_ENABLE_OPTION and FXMAC_RECEIVER_ENABLE_OPTION are set. */ - let options = instance_p.config.network_default_config & !((FXMAC_TRANSMITTER_ENABLE_OPTION | FXMAC_RECEIVER_ENABLE_OPTION) as u32); - FXmacSetOptions(instance_p, options, 0); - let options = !instance_p.config.network_default_config; - FXmacClearOptions(instance_p, options, 0); + let options = instance_p.config.network_default_config + & !((FXMAC_TRANSMITTER_ENABLE_OPTION | FXMAC_RECEIVER_ENABLE_OPTION) as u32); + FXmacSetOptions(instance_p, options, 0); + let options = !instance_p.config.network_default_config; + FXmacClearOptions(instance_p, options, 0); } - fn FXmacDmaReset(instance_p: &mut FXmac) - { +fn FXmacDmaReset(instance_p: &mut FXmac) { let max_frame_size: u32 = instance_p.max_frame_size; - let mut dmacfg: u32 = 0; - //let max_queue_num = 16; - //let dma_brust_length = 16; - - let mut rx_buf_size: u32 = max_frame_size / FXMAC_RX_BUF_UNIT; - rx_buf_size += if (max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { 1 } else { 0 }; /* roundup */ - - // moudle_id=12 - if (instance_p.moudle_id >= 2) - { - for queue in 0..instance_p.config.max_queue_num { - dmacfg = 0; - - // 设置发包/收包 buffer队列的基地址 - FXmacSetQueuePtr(0, queue as u8, FXMAC_SEND); - FXmacSetQueuePtr(0, queue as u8, FXMAC_RECV); - - if queue != 0 - { - write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue as u64)) as *mut u32, rx_buf_size); - } else /* queue is 0 */ - { - dmacfg |= (FXMAC_DMACR_RXBUF_MASK & (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT)); - } - } - - dmacfg |= (instance_p.config.dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); - - dmacfg &= !FXMAC_DMACR_ENDIAN_MASK; - dmacfg &= !FXMAC_DMACR_SWAP_MANAGEMENT_MASK; /* 选择小端 */ - - dmacfg &= !FXMAC_DMACR_TCPCKSUM_MASK; /* close transmitter checksum generation engine */ - - dmacfg &= !FXMAC_DMACR_ADDR_WIDTH_64; - dmacfg |= FXMAC_DMACR_RXSIZE_MASK | FXMAC_DMACR_TXSIZE_MASK; - /* - set this bit can enable auto discard rx frame when lack of receive source, - which avoid endless rx buffer not available error intrrupts. - */ - dmacfg |= FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK; /* force_discard_on_rx_err */ - - dmacfg |= FXMAC_DMACR_ADDR_WIDTH_64; // Just for aarch64 - } - else - { - FXmacSetQueuePtr(0, 0, FXMAC_SEND); - FXmacSetQueuePtr(0, 0, FXMAC_RECV); - dmacfg |= (FXMAC_DMACR_RXBUF_MASK & (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT)); - dmacfg |= (instance_p.config.dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); - - dmacfg &= !FXMAC_DMACR_ENDIAN_MASK; - dmacfg &= !FXMAC_DMACR_SWAP_MANAGEMENT_MASK; /* 选择小端 */ - - dmacfg &= !FXMAC_DMACR_TCPCKSUM_MASK; /* close transmitter checksum generation engine */ - - dmacfg &= !FXMAC_DMACR_ADDR_WIDTH_64; - dmacfg |= FXMAC_DMACR_RXSIZE_MASK | FXMAC_DMACR_TXSIZE_MASK; - /* - set this bit can enable auto discard rx frame when lack of receive source, - which avoid endless rx buffer not available error intrrupts. - */ - dmacfg |= FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK; /* force_discard_on_rx_err */ - dmacfg |= FXMAC_DMACR_ADDR_WIDTH_64; // Just for aarch64 - } - - write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, dmacfg); - } + let mut dmacfg: u32 = 0; + //let max_queue_num = 16; + //let dma_brust_length = 16; + + let mut rx_buf_size: u32 = max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if (max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { + 1 + } else { + 0 + }; /* roundup */ + // moudle_id=12 + if (instance_p.moudle_id >= 2) { + for queue in 0..instance_p.config.max_queue_num { + dmacfg = 0; + + // 设置发包/收包 buffer队列的基地址 + FXmacSetQueuePtr(0, queue as u8, FXMAC_SEND); + FXmacSetQueuePtr(0, queue as u8, FXMAC_RECV); + + if queue != 0 { + write_reg( + (FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue as u64)) as *mut u32, + rx_buf_size, + ); + } else + /* queue is 0 */ + { + dmacfg |= (FXMAC_DMACR_RXBUF_MASK & (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT)); + } + } + + dmacfg |= (instance_p.config.dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); + + dmacfg &= !FXMAC_DMACR_ENDIAN_MASK; + dmacfg &= !FXMAC_DMACR_SWAP_MANAGEMENT_MASK; /* 选择小端 */ + + dmacfg &= !FXMAC_DMACR_TCPCKSUM_MASK; /* close transmitter checksum generation engine */ + + dmacfg &= !FXMAC_DMACR_ADDR_WIDTH_64; + dmacfg |= FXMAC_DMACR_RXSIZE_MASK | FXMAC_DMACR_TXSIZE_MASK; + /* + set this bit can enable auto discard rx frame when lack of receive source, + which avoid endless rx buffer not available error intrrupts. + */ + dmacfg |= FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK; /* force_discard_on_rx_err */ + + dmacfg |= FXMAC_DMACR_ADDR_WIDTH_64; // Just for aarch64 + } else { + FXmacSetQueuePtr(0, 0, FXMAC_SEND); + FXmacSetQueuePtr(0, 0, FXMAC_RECV); + dmacfg |= (FXMAC_DMACR_RXBUF_MASK & (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT)); + dmacfg |= (instance_p.config.dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); + + dmacfg &= !FXMAC_DMACR_ENDIAN_MASK; + dmacfg &= !FXMAC_DMACR_SWAP_MANAGEMENT_MASK; /* 选择小端 */ + + dmacfg &= !FXMAC_DMACR_TCPCKSUM_MASK; /* close transmitter checksum generation engine */ + + dmacfg &= !FXMAC_DMACR_ADDR_WIDTH_64; + dmacfg |= FXMAC_DMACR_RXSIZE_MASK | FXMAC_DMACR_TXSIZE_MASK; + /* + set this bit can enable auto discard rx frame when lack of receive source, + which avoid endless rx buffer not available error intrrupts. + */ + dmacfg |= FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK; /* force_discard_on_rx_err */ + dmacfg |= FXMAC_DMACR_ADDR_WIDTH_64; // Just for aarch64 + } + + write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, dmacfg); +} fn FXmacDmaWidth(moudle_id: u32) -> u32 { if moudle_id < 2 { @@ -525,38 +567,30 @@ fn FXmacDmaWidth(moudle_id: u32) -> u32 { } } - - -fn FxmacFeatureSetOptions(feature: u32, xmac_p: &mut FXmac) -{ +fn FxmacFeatureSetOptions(feature: u32, xmac_p: &mut FXmac) { let mut options: u32 = 0; - if (feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 - { + if (feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 { info!("FXMAC_JUMBO_ENABLE_OPTION is ok"); options |= FXMAC_JUMBO_ENABLE_OPTION; } - if (feature & FXMAC_LWIP_PORT_CONFIG_UNICAST_ADDRESS_FILITER) !=0 - { + if (feature & FXMAC_LWIP_PORT_CONFIG_UNICAST_ADDRESS_FILITER) != 0 { info!("FXMAC_UNICAST_OPTION is ok"); options |= FXMAC_UNICAST_OPTION; } - if (feature & FXMAC_LWIP_PORT_CONFIG_MULTICAST_ADDRESS_FILITER) !=0 - { + if (feature & FXMAC_LWIP_PORT_CONFIG_MULTICAST_ADDRESS_FILITER) != 0 { info!("FXMAC_MULTICAST_OPTION is ok"); options |= FXMAC_MULTICAST_OPTION; } /* enable copy all frames */ - if (feature & FXMAC_LWIP_PORT_CONFIG_COPY_ALL_FRAMES) != 0 - { + if (feature & FXMAC_LWIP_PORT_CONFIG_COPY_ALL_FRAMES) != 0 { info!("FXMAC_PROMISC_OPTION is ok"); options |= FXMAC_PROMISC_OPTION; } - /* close fcs check */ - if (feature & FXMAC_LWIP_PORT_CONFIG_CLOSE_FCS_CHECK) != 0 - { + /* close fcs check */ + if (feature & FXMAC_LWIP_PORT_CONFIG_CLOSE_FCS_CHECK) != 0 { info!("FXMAC_FCS_STRIP_OPTION is ok"); options |= FXMAC_FCS_STRIP_OPTION; } @@ -576,52 +610,61 @@ fn FxmacFeatureSetOptions(feature: u32, xmac_p: &mut FXmac) * The buffer queue addresses has to be set before starting the transfer, so * this function has to be called in prior to FXmacStart() */ - pub fn FXmacSetQueuePtr(queue_p: u64, queue_num: u8, direction: u32) { - +pub fn FXmacSetQueuePtr(queue_p: u64, queue_num: u8, direction: u32) { //assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); // If already started, then just return - let flag_queue_p = if queue_p == 0 { 1 }else{ 0 }; - let FXMAC_QUEUE_REGISTER_OFFSET = |base_addr: u64, queue_id: u64| (base_addr + (queue_id - 1) * 4); + let flag_queue_p = if queue_p == 0 { 1 } else { 0 }; + let FXMAC_QUEUE_REGISTER_OFFSET = + |base_addr: u64, queue_id: u64| (base_addr + (queue_id - 1) * 4); if queue_num == 0 { - if direction == FXMAC_SEND - { + if direction == FXMAC_SEND { /* set base start address of TX buffer queue (tx buffer descriptor list) */ - write_reg((FXMAC_IOBASE + FXMAC_TXQBASE_OFFSET) as *mut u32, - ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32); - } - else - { + write_reg( + (FXMAC_IOBASE + FXMAC_TXQBASE_OFFSET) as *mut u32, + ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32, + ); + } else { /* set base start address of RX buffer queue (rx buffer descriptor list) */ - write_reg((FXMAC_IOBASE + FXMAC_RXQBASE_OFFSET) as *mut u32, - ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32); + write_reg( + (FXMAC_IOBASE + FXMAC_RXQBASE_OFFSET) as *mut u32, + ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32, + ); } - } - else - { - if direction == FXMAC_SEND - { - write_reg((FXMAC_IOBASE + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, queue_num as u64)) as *mut u32, - ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32); - } - else - { - write_reg((FXMAC_IOBASE + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_RXQ1BASE_OFFSET, queue_num as u64)) as *mut u32, - ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32); + } else { + if direction == FXMAC_SEND { + write_reg( + (FXMAC_IOBASE + + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, queue_num as u64)) + as *mut u32, + ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32, + ); + } else { + write_reg( + (FXMAC_IOBASE + + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_RXQ1BASE_OFFSET, queue_num as u64)) + as *mut u32, + ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32, + ); } - } + } - if direction == FXMAC_SEND // Only for aarch64 - { + if direction == FXMAC_SEND + // Only for aarch64 + { /* Set the MSB of TX Queue start address */ - write_reg((FXMAC_IOBASE + FXMAC_MSBBUF_TXQBASE_OFFSET) as *mut u32, - ((queue_p & ULONG64_HI_MASK) >> 32) as u32); - } else { + write_reg( + (FXMAC_IOBASE + FXMAC_MSBBUF_TXQBASE_OFFSET) as *mut u32, + ((queue_p & ULONG64_HI_MASK) >> 32) as u32, + ); + } else { /* Set the MSB of RX Queue start address */ - write_reg((FXMAC_IOBASE + FXMAC_MSBBUF_RXQBASE_OFFSET) as *mut u32, - ((queue_p & ULONG64_HI_MASK) >> 32) as u32); - } + write_reg( + (FXMAC_IOBASE + FXMAC_MSBBUF_RXQBASE_OFFSET) as *mut u32, + ((queue_p & ULONG64_HI_MASK) >> 32) as u32, + ); + } } fn FXmacConfigureCaps(instance_p: &mut FXmac) { @@ -629,445 +672,418 @@ fn FXmacConfigureCaps(instance_p: &mut FXmac) { let read_regs = read_reg((FXMAC_IOBASE + FXMAC_DESIGNCFG_DEBUG1_OFFSET) as *const u32); if (read_regs & FXMAC_DESIGNCFG_DEBUG1_BUS_IRQCOR_MASK) == 0 { instance_p.caps |= FXMAC_CAPS_ISR_CLEAR_ON_WRITE; - info!("Design ConfigReg1: {:#x} Has FXMAC_CAPS_ISR_CLEAR_ON_WRITE feature", read_regs); + info!( + "Design ConfigReg1: {:#x} Has FXMAC_CAPS_ISR_CLEAR_ON_WRITE feature", + read_regs + ); } } - fn FXmacClkDivGet(instance_p: &mut FXmac) -> u32 { // moudle_id=12 // let pclk_hz = 50000000; let pclk_hz = instance_p.config.pclk_hz; // FXMAC0_PCLK; - if (pclk_hz <= 20000000) - { + if (pclk_hz <= 20000000) { return FXMAC_NWCFG_CLOCK_DIV8_MASK; - } - else if (pclk_hz <= 40000000) - { + } else if (pclk_hz <= 40000000) { return FXMAC_NWCFG_CLOCK_DIV16_MASK; - } - else if (pclk_hz <= 80000000) - { + } else if (pclk_hz <= 80000000) { return FXMAC_NWCFG_CLOCK_DIV32_MASK; - } - else if (instance_p.moudle_id >= 2) - { - if (pclk_hz <= 120000000) - { + } else if (instance_p.moudle_id >= 2) { + if (pclk_hz <= 120000000) { return FXMAC_NWCFG_CLOCK_DIV48_MASK; - } - else if (pclk_hz <= 160000000) - { + } else if (pclk_hz <= 160000000) { return FXMAC_NWCFG_CLOCK_DIV64_MASK; - } - else if (pclk_hz <= 240000000) - { + } else if (pclk_hz <= 240000000) { return FXMAC_NWCFG_CLOCK_DIV96_MASK; - } - else if (pclk_hz <= 320000000) - { + } else if (pclk_hz <= 320000000) { return FXMAC_NWCFG_CLOCK_DIV128_MASK; - } - else - { + } else { return FXMAC_NWCFG_CLOCK_DIV224_MASK; } - } - else - { + } else { return FXMAC_NWCFG_CLOCK_DIV64_MASK; } } - /** * Set options for the driver/device. The driver should be stopped with * FXmacStop() before changing options. */ - fn FXmacSetOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u32 { - let mut reg: u32 = 0; /* Generic register contents */ - let mut reg_netcfg: u32 = 0; /* Reflects original contents of NET_CONFIG */ - let mut reg_new_netcfg: u32 = 0; /* Reflects new contents of NET_CONFIG */ - let mut status: u32 = 0; - - //let is_started = 0; - - info!("FXmacSetOptions, is_started={}, options={}, queue_num={}, max_queue_num={}", instance_p.is_started, options, queue_num, instance_p.config.max_queue_num); - - /* Be sure device has been stopped */ - if instance_p.is_started == FT_COMPONENT_IS_STARTED - { - status = 9; //FXMAC_ERR_MAC_IS_PROCESSING; +fn FXmacSetOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u32 { + let mut reg: u32 = 0; /* Generic register contents */ + let mut reg_netcfg: u32 = 0; /* Reflects original contents of NET_CONFIG */ + let mut reg_new_netcfg: u32 = 0; /* Reflects new contents of NET_CONFIG */ + let mut status: u32 = 0; + + //let is_started = 0; + + info!( + "FXmacSetOptions, is_started={}, options={}, queue_num={}, max_queue_num={}", + instance_p.is_started, options, queue_num, instance_p.config.max_queue_num + ); + + /* Be sure device has been stopped */ + if instance_p.is_started == FT_COMPONENT_IS_STARTED { + status = 9; //FXMAC_ERR_MAC_IS_PROCESSING; error!("FXMAC is processing when calling FXmacSetOptions function"); - } else { - - /* Many of these options will change the NET_CONFIG registers. - * To reduce the amount of IO to the device, group these options here - * and change them all at once. - */ - - /* Grab current register contents */ - reg_netcfg = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); - - reg_new_netcfg = reg_netcfg; - - /* - * It is configured to max 1536. - */ - if (options & FXMAC_FRAME1536_OPTION) != 0 - { - reg_new_netcfg |= FXMAC_NWCFG_1536RXEN_MASK; - } - - /* Turn on VLAN packet only, only VLAN tagged will be accepted */ - if (options & FXMAC_VLAN_OPTION) != 0 - { - reg_new_netcfg |= FXMAC_NWCFG_NVLANDISC_MASK; - } - - /* Turn on FCS stripping on receive packets */ - if (options & FXMAC_FCS_STRIP_OPTION) != 0 - { - reg_new_netcfg |= FXMAC_NWCFG_FCS_REMOVE_MASK; - } - - /* Turn on length/type field checking on receive packets */ - if (options & FXMAC_LENTYPE_ERR_OPTION) != 0 - { - reg_new_netcfg |= FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK; - } - - /* Turn on flow control */ - if (options & FXMAC_FLOW_CONTROL_OPTION) != 0 - { - reg_new_netcfg |= FXMAC_NWCFG_PAUSE_ENABLE_MASK; - } - - /* Turn on promiscuous frame filtering (all frames are received) */ - if (options & FXMAC_PROMISC_OPTION) != 0 - { - reg_new_netcfg |= FXMAC_NWCFG_COPYALLEN_MASK; - } - - /* Allow broadcast address reception */ - if (options & FXMAC_BROADCAST_OPTION) != 0 - { - reg_new_netcfg &= !(FXMAC_NWCFG_BCASTDI_MASK as u32); - } - - /* Allow multicast address filtering */ - if (options & FXMAC_MULTICAST_OPTION) != 0 - { - reg_new_netcfg |= FXMAC_NWCFG_MCASTHASHEN_MASK; - } - - if (options & FXMAC_UNICAST_OPTION) != 0 - { - reg_new_netcfg |= FXMAC_NWCFG_UCASTHASHEN_MASK; - } - - if (options & FXMAC_TAIL_PTR_OPTION) != 0 - { - write_reg((FXMAC_IOBASE + FXMAC_TAIL_ENABLE) as *mut u32, 0x80000001); - } - - - /* enable RX checksum offload */ - if (options & FXMAC_RX_CHKSUM_ENABLE_OPTION) != 0 - { - reg_new_netcfg |= FXMAC_NWCFG_RXCHKSUMEN_MASK; - } - - /* Enable jumbo frames */ - if (options & FXMAC_JUMBO_ENABLE_OPTION) != 0 - { + } else { + /* Many of these options will change the NET_CONFIG registers. + * To reduce the amount of IO to the device, group these options here + * and change them all at once. + */ + + /* Grab current register contents */ + reg_netcfg = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + + reg_new_netcfg = reg_netcfg; + + /* + * It is configured to max 1536. + */ + if (options & FXMAC_FRAME1536_OPTION) != 0 { + reg_new_netcfg |= FXMAC_NWCFG_1536RXEN_MASK; + } + + /* Turn on VLAN packet only, only VLAN tagged will be accepted */ + if (options & FXMAC_VLAN_OPTION) != 0 { + reg_new_netcfg |= FXMAC_NWCFG_NVLANDISC_MASK; + } + + /* Turn on FCS stripping on receive packets */ + if (options & FXMAC_FCS_STRIP_OPTION) != 0 { + reg_new_netcfg |= FXMAC_NWCFG_FCS_REMOVE_MASK; + } + + /* Turn on length/type field checking on receive packets */ + if (options & FXMAC_LENTYPE_ERR_OPTION) != 0 { + reg_new_netcfg |= FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK; + } + + /* Turn on flow control */ + if (options & FXMAC_FLOW_CONTROL_OPTION) != 0 { + reg_new_netcfg |= FXMAC_NWCFG_PAUSE_ENABLE_MASK; + } + + /* Turn on promiscuous frame filtering (all frames are received) */ + if (options & FXMAC_PROMISC_OPTION) != 0 { + reg_new_netcfg |= FXMAC_NWCFG_COPYALLEN_MASK; + } + + /* Allow broadcast address reception */ + if (options & FXMAC_BROADCAST_OPTION) != 0 { + reg_new_netcfg &= !(FXMAC_NWCFG_BCASTDI_MASK as u32); + } + + /* Allow multicast address filtering */ + if (options & FXMAC_MULTICAST_OPTION) != 0 { + reg_new_netcfg |= FXMAC_NWCFG_MCASTHASHEN_MASK; + } + + if (options & FXMAC_UNICAST_OPTION) != 0 { + reg_new_netcfg |= FXMAC_NWCFG_UCASTHASHEN_MASK; + } + + if (options & FXMAC_TAIL_PTR_OPTION) != 0 { + write_reg((FXMAC_IOBASE + FXMAC_TAIL_ENABLE) as *mut u32, 0x80000001); + } + + /* enable RX checksum offload */ + if (options & FXMAC_RX_CHKSUM_ENABLE_OPTION) != 0 { + reg_new_netcfg |= FXMAC_NWCFG_RXCHKSUMEN_MASK; + } + + /* Enable jumbo frames */ + if (options & FXMAC_JUMBO_ENABLE_OPTION) != 0 { instance_p.max_mtu_size = FXMAC_MTU_JUMBO; instance_p.max_frame_size = FXMAC_MAX_FRAME_SIZE_JUMBO; - reg_new_netcfg |= FXMAC_NWCFG_JUMBO_MASK; + reg_new_netcfg |= FXMAC_NWCFG_JUMBO_MASK; - write_reg((FXMAC_IOBASE + FXMAC_JUMBOMAXLEN_OFFSET) as *mut u32, FXMAC_MAX_FRAME_SIZE_JUMBO); + write_reg( + (FXMAC_IOBASE + FXMAC_JUMBOMAXLEN_OFFSET) as *mut u32, + FXMAC_MAX_FRAME_SIZE_JUMBO, + ); - write_reg((FXMAC_IOBASE + FXMAC_TXQSEGALLOC_QLOWER_OFFSET) as *mut u32, FXMAC_TXQSEGALLOC_QLOWER_JUMBO_MASK); + write_reg( + (FXMAC_IOBASE + FXMAC_TXQSEGALLOC_QLOWER_OFFSET) as *mut u32, + FXMAC_TXQSEGALLOC_QLOWER_JUMBO_MASK, + ); if queue_num == 0 { - let mut rx_buf_size: u32 = 0; - reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); - - reg &= !FXMAC_DMACR_RXBUF_MASK; - - rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; - rx_buf_size += if (instance_p.max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { 1 } else { 0 }; - - reg |= (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT) & FXMAC_DMACR_RXBUF_MASK; - write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); - - } else if queue_num < instance_p.config.max_queue_num { - let mut rx_buf_size: u32 = 0; - rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; - rx_buf_size += if (instance_p.max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { 1 } else { 0 }; - - write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue_num as u64)) as *mut u32, rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK); - } - } - - if (options & FXMAC_SGMII_ENABLE_OPTION) != 0 - { - reg_new_netcfg |= (FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK | - FXMAC_NWCFG_PCSSEL_MASK); - } - - if (options & FXMAC_LOOPBACK_NO_MII_OPTION) != 0 - { - reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - reg |= FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK; - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); - } - - if (options & FXMAC_LOOPBACK_USXGMII_OPTION) != 0 - { - write_reg((FXMAC_IOBASE + FXMAC_TEST_CONTROL_OFFSET) as *mut u32, 2); - } - - /* Officially change the NET_CONFIG registers if it needs to be - * modified. - */ - if (reg_netcfg != reg_new_netcfg) - { - write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, reg_new_netcfg); - } - - /* Enable TX checksum offload */ - if (options & FXMAC_TX_CHKSUM_ENABLE_OPTION) != 0 - { - reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); - reg |= FXMAC_DMACR_TCPCKSUM_MASK; - write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); - } - - /* Enable transmitter */ - if (options & FXMAC_TRANSMITTER_ENABLE_OPTION) != 0 - { - reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - reg |= FXMAC_NWCTRL_TXEN_MASK; - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); - - } - - /* Enable receiver */ - if (options & FXMAC_RECEIVER_ENABLE_OPTION) != 0 - { - reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - reg |= FXMAC_NWCTRL_RXEN_MASK; - - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); - - } - - /* The remaining options not handled here are managed elsewhere in the - * driver. No register modifications are needed at this time. Reflecting - * the option in instance_p->options is good enough for now. - */ - - /* Set options word to its new value */ - instance_p.options |= options; - - status = 0; // FT_SUCCESS; - } - - status - } - + let mut rx_buf_size: u32 = 0; + reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); + + reg &= !FXMAC_DMACR_RXBUF_MASK; + + rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if (instance_p.max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { + 1 + } else { + 0 + }; + + reg |= (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT) & FXMAC_DMACR_RXBUF_MASK; + write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); + } else if queue_num < instance_p.config.max_queue_num { + let mut rx_buf_size: u32 = 0; + rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if (instance_p.max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { + 1 + } else { + 0 + }; + + write_reg( + (FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue_num as u64)) as *mut u32, + rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK, + ); + } + } + + if (options & FXMAC_SGMII_ENABLE_OPTION) != 0 { + reg_new_netcfg |= (FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK | FXMAC_NWCFG_PCSSEL_MASK); + } + + if (options & FXMAC_LOOPBACK_NO_MII_OPTION) != 0 { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg |= FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + } + + if (options & FXMAC_LOOPBACK_USXGMII_OPTION) != 0 { + write_reg((FXMAC_IOBASE + FXMAC_TEST_CONTROL_OFFSET) as *mut u32, 2); + } + + /* Officially change the NET_CONFIG registers if it needs to be + * modified. + */ + if (reg_netcfg != reg_new_netcfg) { + write_reg( + (FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, + reg_new_netcfg, + ); + } + + /* Enable TX checksum offload */ + if (options & FXMAC_TX_CHKSUM_ENABLE_OPTION) != 0 { + reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); + reg |= FXMAC_DMACR_TCPCKSUM_MASK; + write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); + } + + /* Enable transmitter */ + if (options & FXMAC_TRANSMITTER_ENABLE_OPTION) != 0 { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg |= FXMAC_NWCTRL_TXEN_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + } + + /* Enable receiver */ + if (options & FXMAC_RECEIVER_ENABLE_OPTION) != 0 { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg |= FXMAC_NWCTRL_RXEN_MASK; + + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + } + + /* The remaining options not handled here are managed elsewhere in the + * driver. No register modifications are needed at this time. Reflecting + * the option in instance_p->options is good enough for now. + */ + + /* Set options word to its new value */ + instance_p.options |= options; + + status = 0; // FT_SUCCESS; + } + + status +} + /// Clear options for the driver/device -fn FXmacClearOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u32 - { - let mut reg: u32 = 0; /* Generic */ - let mut reg_net_cfg: u32 = 0; /* Reflects original contents of NET_CONFIG */ - let mut reg_new_net_cfg: u32 = 0; /* Reflects new contents of NET_CONFIG */ - let mut status: u32 = 0; - - //let is_started = 0; - /* Be sure device has been stopped */ - if (instance_p.is_started == FT_COMPONENT_IS_STARTED) - { - status = 9; //FXMAC_ERR_MAC_IS_PROCESSING - error!("FXMAC is processing when calling FXmacClearOptions function"); - - } else { - /* Many of these options will change the NET_CONFIG registers. - * To reduce the amount of IO to the device, group these options here - * and change them all at once. - */ - /* Grab current register contents */ - reg_net_cfg = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); - reg_new_net_cfg = reg_net_cfg; - /* There is only RX configuration!? - * It is configured in two different length, up to 1536 and 10240 bytes - */ - if (options & FXMAC_FRAME1536_OPTION) != 0 - { - reg_new_net_cfg &= !(FXMAC_NWCFG_1536RXEN_MASK as u32); - } - - /* Turn off VLAN packet only */ - if (options & FXMAC_VLAN_OPTION) != 0 - { - reg_new_net_cfg &= !(FXMAC_NWCFG_NVLANDISC_MASK as u32); - } - - /* Turn off FCS stripping on receive packets */ - if (options & FXMAC_FCS_STRIP_OPTION) != 0 - { - reg_new_net_cfg &= !(FXMAC_NWCFG_FCS_REMOVE_MASK as u32); - } - - /* Turn off length/type field checking on receive packets */ - if (options & FXMAC_LENTYPE_ERR_OPTION) != 0 - { - reg_new_net_cfg &= !(FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK as u32); - } - - /* Turn off flow control */ - if (options & FXMAC_FLOW_CONTROL_OPTION) != 0 - { - reg_new_net_cfg &= !(FXMAC_NWCFG_PAUSE_ENABLE_MASK as u32); - } - - /* Turn off promiscuous frame filtering (all frames are received) */ - if (options & FXMAC_PROMISC_OPTION) != 0 - { - reg_new_net_cfg &= !(FXMAC_NWCFG_COPYALLEN_MASK as u32); - } - - /* Disallow broadcast address filtering => broadcast reception */ - if (options & FXMAC_BROADCAST_OPTION) != 0 - { - reg_new_net_cfg |= FXMAC_NWCFG_BCASTDI_MASK; - } - - /* Disallow unicast address filtering */ - if (options & FXMAC_UNICAST_OPTION) != 0 - { - reg_new_net_cfg &= !(FXMAC_NWCFG_UCASTHASHEN_MASK as u32); - } - - /* Disallow multicast address filtering */ - if (options & FXMAC_MULTICAST_OPTION) != 0 - { - reg_new_net_cfg &= !(FXMAC_NWCFG_MCASTHASHEN_MASK as u32); - } - - if (options & FXMAC_TAIL_PTR_OPTION) != 0 - { - write_reg((FXMAC_IOBASE + FXMAC_TAIL_ENABLE) as *mut u32, 0); - } - - /* Disable RX checksum offload */ - if (options & FXMAC_RX_CHKSUM_ENABLE_OPTION) != 0 - { - reg_new_net_cfg &= !(FXMAC_NWCFG_RXCHKSUMEN_MASK as u32); - } - - /* Disable jumbo frames */ - if (options & FXMAC_JUMBO_ENABLE_OPTION) != 0 /* 恢复之前buffer 容量 */ - { +fn FXmacClearOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u32 { + let mut reg: u32 = 0; /* Generic */ + let mut reg_net_cfg: u32 = 0; /* Reflects original contents of NET_CONFIG */ + let mut reg_new_net_cfg: u32 = 0; /* Reflects new contents of NET_CONFIG */ + let mut status: u32 = 0; + + //let is_started = 0; + /* Be sure device has been stopped */ + if (instance_p.is_started == FT_COMPONENT_IS_STARTED) { + status = 9; //FXMAC_ERR_MAC_IS_PROCESSING + error!("FXMAC is processing when calling FXmacClearOptions function"); + } else { + /* Many of these options will change the NET_CONFIG registers. + * To reduce the amount of IO to the device, group these options here + * and change them all at once. + */ + /* Grab current register contents */ + reg_net_cfg = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + reg_new_net_cfg = reg_net_cfg; + /* There is only RX configuration!? + * It is configured in two different length, up to 1536 and 10240 bytes + */ + if (options & FXMAC_FRAME1536_OPTION) != 0 { + reg_new_net_cfg &= !(FXMAC_NWCFG_1536RXEN_MASK as u32); + } + + /* Turn off VLAN packet only */ + if (options & FXMAC_VLAN_OPTION) != 0 { + reg_new_net_cfg &= !(FXMAC_NWCFG_NVLANDISC_MASK as u32); + } + + /* Turn off FCS stripping on receive packets */ + if (options & FXMAC_FCS_STRIP_OPTION) != 0 { + reg_new_net_cfg &= !(FXMAC_NWCFG_FCS_REMOVE_MASK as u32); + } + + /* Turn off length/type field checking on receive packets */ + if (options & FXMAC_LENTYPE_ERR_OPTION) != 0 { + reg_new_net_cfg &= !(FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK as u32); + } + + /* Turn off flow control */ + if (options & FXMAC_FLOW_CONTROL_OPTION) != 0 { + reg_new_net_cfg &= !(FXMAC_NWCFG_PAUSE_ENABLE_MASK as u32); + } + + /* Turn off promiscuous frame filtering (all frames are received) */ + if (options & FXMAC_PROMISC_OPTION) != 0 { + reg_new_net_cfg &= !(FXMAC_NWCFG_COPYALLEN_MASK as u32); + } + + /* Disallow broadcast address filtering => broadcast reception */ + if (options & FXMAC_BROADCAST_OPTION) != 0 { + reg_new_net_cfg |= FXMAC_NWCFG_BCASTDI_MASK; + } + + /* Disallow unicast address filtering */ + if (options & FXMAC_UNICAST_OPTION) != 0 { + reg_new_net_cfg &= !(FXMAC_NWCFG_UCASTHASHEN_MASK as u32); + } + + /* Disallow multicast address filtering */ + if (options & FXMAC_MULTICAST_OPTION) != 0 { + reg_new_net_cfg &= !(FXMAC_NWCFG_MCASTHASHEN_MASK as u32); + } + + if (options & FXMAC_TAIL_PTR_OPTION) != 0 { + write_reg((FXMAC_IOBASE + FXMAC_TAIL_ENABLE) as *mut u32, 0); + } + + /* Disable RX checksum offload */ + if (options & FXMAC_RX_CHKSUM_ENABLE_OPTION) != 0 { + reg_new_net_cfg &= !(FXMAC_NWCFG_RXCHKSUMEN_MASK as u32); + } + + /* Disable jumbo frames */ + if (options & FXMAC_JUMBO_ENABLE_OPTION) != 0 + /* 恢复之前buffer 容量 */ + { instance_p.max_mtu_size = FXMAC_MTU; instance_p.max_frame_size = FXMAC_MAX_FRAME_SIZE; - - reg_new_net_cfg &= !(FXMAC_NWCFG_JUMBO_MASK as u32); - - reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); - - reg &= !FXMAC_DMACR_RXBUF_MASK; - - if queue_num == 0 - { - let mut rx_buf_size: u32 = 0; - - reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); - reg &= !FXMAC_DMACR_RXBUF_MASK; - - rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; - rx_buf_size += if instance_p.max_frame_size % FXMAC_RX_BUF_UNIT != 0 {1} else {0}; - - reg |= (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT) & FXMAC_DMACR_RXBUF_MASK; - - write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); - } - else if (queue_num < instance_p.config.max_queue_num) - { - let mut rx_buf_size: u32 = 0; - rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; - rx_buf_size += if (instance_p.max_frame_size % FXMAC_RX_BUF_UNIT) != 0 {1} else {0}; - - write_reg((FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue_num as u64)) as *mut u32, rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK); - } - } - - if (options & FXMAC_SGMII_ENABLE_OPTION) != 0 - { - reg_new_net_cfg &= !((FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK | - FXMAC_NWCFG_PCSSEL_MASK) as u32); - } - - if (options & FXMAC_LOOPBACK_NO_MII_OPTION) != 0 - { - reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - reg &= !FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK; - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); - } - - if (options & FXMAC_LOOPBACK_USXGMII_OPTION) != 0 - { - write_reg((FXMAC_IOBASE + FXMAC_TEST_CONTROL_OFFSET) as *mut u32, - read_reg((FXMAC_IOBASE + FXMAC_TEST_CONTROL_OFFSET) as *const u32) & !2 ); - } - - /* Officially change the NET_CONFIG registers if it needs to be - * modified. - */ - if reg_net_cfg != reg_new_net_cfg - { - write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, reg_new_net_cfg); - } - - /* Disable TX checksum offload */ - if (options & FXMAC_TX_CHKSUM_ENABLE_OPTION) != 0 - { - reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); - reg &= !FXMAC_DMACR_TCPCKSUM_MASK; - write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); - } - - /* Disable transmitter */ - if (options & FXMAC_TRANSMITTER_ENABLE_OPTION) != 0 - { - reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - reg &= !FXMAC_NWCTRL_TXEN_MASK; - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); - } - - /* Disable receiver */ - if (options & FXMAC_RECEIVER_ENABLE_OPTION) != 0 - { - reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - reg &= !FXMAC_NWCTRL_RXEN_MASK; - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); - } - - /* The remaining options not handled here are managed elsewhere in the - * driver. No register modifications are needed at this time. Reflecting - * option in instance_p->options is good enough for now. - */ - - /* Set options word to its new value */ - instance_p.options &= !options; - - status = 0; // FT_SUCCESS - } - status - } + + reg_new_net_cfg &= !(FXMAC_NWCFG_JUMBO_MASK as u32); + + reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); + + reg &= !FXMAC_DMACR_RXBUF_MASK; + + if queue_num == 0 { + let mut rx_buf_size: u32 = 0; + + reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); + reg &= !FXMAC_DMACR_RXBUF_MASK; + + rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if instance_p.max_frame_size % FXMAC_RX_BUF_UNIT != 0 { + 1 + } else { + 0 + }; + + reg |= (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT) & FXMAC_DMACR_RXBUF_MASK; + + write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); + } else if (queue_num < instance_p.config.max_queue_num) { + let mut rx_buf_size: u32 = 0; + rx_buf_size = instance_p.max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += if (instance_p.max_frame_size % FXMAC_RX_BUF_UNIT) != 0 { + 1 + } else { + 0 + }; + + write_reg( + (FXMAC_IOBASE + FXMAC_RXBUFQX_SIZE_OFFSET(queue_num as u64)) as *mut u32, + rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK, + ); + } + } + + if (options & FXMAC_SGMII_ENABLE_OPTION) != 0 { + reg_new_net_cfg &= + !((FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK | FXMAC_NWCFG_PCSSEL_MASK) as u32); + } + + if (options & FXMAC_LOOPBACK_NO_MII_OPTION) != 0 { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg &= !FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + } + + if (options & FXMAC_LOOPBACK_USXGMII_OPTION) != 0 { + write_reg( + (FXMAC_IOBASE + FXMAC_TEST_CONTROL_OFFSET) as *mut u32, + read_reg((FXMAC_IOBASE + FXMAC_TEST_CONTROL_OFFSET) as *const u32) & !2, + ); + } + + /* Officially change the NET_CONFIG registers if it needs to be + * modified. + */ + if reg_net_cfg != reg_new_net_cfg { + write_reg( + (FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, + reg_new_net_cfg, + ); + } + + /* Disable TX checksum offload */ + if (options & FXMAC_TX_CHKSUM_ENABLE_OPTION) != 0 { + reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); + reg &= !FXMAC_DMACR_TCPCKSUM_MASK; + write_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *mut u32, reg); + } + + /* Disable transmitter */ + if (options & FXMAC_TRANSMITTER_ENABLE_OPTION) != 0 { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg &= !FXMAC_NWCTRL_TXEN_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + } + + /* Disable receiver */ + if (options & FXMAC_RECEIVER_ENABLE_OPTION) != 0 { + reg = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + reg &= !FXMAC_NWCTRL_RXEN_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, reg); + } + + /* The remaining options not handled here are managed elsewhere in the + * driver. No register modifications are needed at this time. Reflecting + * option in instance_p->options is good enough for now. + */ + + /* Set options word to its new value */ + instance_p.options &= !options; + + status = 0; // FT_SUCCESS + } + status +} /// Clear the Hash registers for the mac address pointed by address_ptr. fn FXmacClearHash() { @@ -1080,33 +1096,46 @@ fn FXmacClearHash() { /// Set the MAC address for this driver/device. The address is a 48-bit value. /// The device must be stopped before calling this function. pub fn FXmacSetMacAddress(address_ptr: &[u8; 6], index: u8) -> u32 { - let mut mac_addr: u32 = 0; + let mut mac_addr: u32 = 0; let aptr = address_ptr; let index_loc: u8 = index; let mut status: u32 = 0; - assert!((index_loc < FXMAC_MAX_MAC_ADDR as u8), "index of Mac Address exceed {}", FXMAC_MAX_MAC_ADDR); + assert!( + (index_loc < FXMAC_MAX_MAC_ADDR as u8), + "index of Mac Address exceed {}", + FXMAC_MAX_MAC_ADDR + ); let is_started = 0; /* Be sure device has been stopped */ - if is_started == FT_COMPONENT_IS_STARTED as u32 - { + if is_started == FT_COMPONENT_IS_STARTED as u32 { //status = FXMAC_ERR_MAC_IS_PROCESSING; status = 9; error!("FXMAC is processing when calling FXmacSetMacAddress function"); } else { /* Set the MAC bits [31:0] in BOT */ - mac_addr = aptr[0] as u32 | ((aptr[1] as u32) << 8) | ((aptr[2] as u32) << 16) | ((aptr[3] as u32) << 24); - write_reg((FXMAC_IOBASE + FXMAC_GEM_SA1B as u64 + (index_loc * 8) as u64) as *mut u32, mac_addr); + mac_addr = aptr[0] as u32 + | ((aptr[1] as u32) << 8) + | ((aptr[2] as u32) << 16) + | ((aptr[3] as u32) << 24); + write_reg( + (FXMAC_IOBASE + FXMAC_GEM_SA1B as u64 + (index_loc * 8) as u64) as *mut u32, + mac_addr, + ); /* There are reserved bits in TOP so don't affect them */ - mac_addr = read_reg((FXMAC_IOBASE + FXMAC_GEM_SA1T as u64 + (index_loc * 8) as u64) as *const u32); + mac_addr = + read_reg((FXMAC_IOBASE + FXMAC_GEM_SA1T as u64 + (index_loc * 8) as u64) as *const u32); mac_addr &= !FXMAC_GEM_SAB_MASK; /* Set MAC bits [47:32] in TOP */ mac_addr |= aptr[4] as u32; mac_addr |= (aptr[5] as u32) << 8; - write_reg((FXMAC_IOBASE + FXMAC_GEM_SA1T as u64 + (index_loc * 8) as u64) as *mut u32, mac_addr); + write_reg( + (FXMAC_IOBASE + FXMAC_GEM_SA1T as u64 + (index_loc * 8) as u64) as *mut u32, + mac_addr, + ); status = 0; // FT_SUCCESS } @@ -1121,11 +1150,11 @@ pub fn FXmacSetMacAddress(address_ptr: &[u8; 6], index: u8) -> u32 { * which the current MAC address will be copied. * @param {u8} index is a index to which MAC (0-3) address. */ -pub fn FXmacGetMacAddress(address_ptr: &mut [u8; 6], index: u8) -{ - assert!((index as u32)< FXMAC_MAX_MAC_ADDR); +pub fn FXmacGetMacAddress(address_ptr: &mut [u8; 6], index: u8) { + assert!((index as u32) < FXMAC_MAX_MAC_ADDR); - let mut reg_value: u32 = read_reg((FXMAC_IOBASE + FXMAC_GEM_SA1B as u64 + (index as u64 * 8)) as *const u32); + let mut reg_value: u32 = + read_reg((FXMAC_IOBASE + FXMAC_GEM_SA1B as u64 + (index as u64 * 8)) as *const u32); address_ptr[0] = reg_value as u8; address_ptr[1] = (reg_value >> 8) as u8; address_ptr[2] = (reg_value >> 16) as u8; @@ -1148,114 +1177,142 @@ pub fn FXmacGetMacAddress(address_ptr: &mut [u8; 6], index: u8) /// destination address is reduced to a 6 bit index into the 64 bit hash /// register using the following hash function. The hash function is an XOR /// of every sixth bit of the destination address. -pub fn FXmac_SetHash(intance_p: &mut FXmac, mac_address: &[u8; 6]) -> u32 -{ +pub fn FXmac_SetHash(intance_p: &mut FXmac, mac_address: &[u8; 6]) -> u32 { let mut HashAddr: u32 = 0; - let mut Status: u32 = 0; - debug!("Set MAC: {:x?} in hash table", mac_address); + let mut Status: u32 = 0; + debug!("Set MAC: {:x?} in hash table", mac_address); - // Check that the Ethernet address (MAC) is not 00:00:00:00:00:00 - assert!(!((mac_address[0] == 0) && (mac_address[5] == 0))); - assert!(intance_p.is_ready == FT_COMPONENT_IS_READY); + // Check that the Ethernet address (MAC) is not 00:00:00:00:00:00 + assert!(!((mac_address[0] == 0) && (mac_address[5] == 0))); + assert!(intance_p.is_ready == FT_COMPONENT_IS_READY); - /* Be sure device has been stopped */ - if (intance_p.is_started == FT_COMPONENT_IS_STARTED) { - error!("FXmac_SetHash failed: FXMAC_ERR_MAC_IS_PROCESSING"); - Status = 9;// FXMAC_ERR_MAC_IS_PROCESSING + /* Be sure device has been stopped */ + if (intance_p.is_started == FT_COMPONENT_IS_STARTED) { + error!("FXmac_SetHash failed: FXMAC_ERR_MAC_IS_PROCESSING"); + Status = 9; // FXMAC_ERR_MAC_IS_PROCESSING + } else { + let Temp1: u8 = (mac_address[0]) & 0x3F; + let Temp2: u8 = ((mac_address[0] >> 6) & 0x03) | ((mac_address[1] & 0x0F) << 2); + let Temp3: u8 = ((mac_address[1] >> 4) & 0x0F) | ((mac_address[2] & 0x3) << 4); + let Temp4: u8 = ((mac_address[2] >> 2) & 0x3F); + let Temp5: u8 = mac_address[3] & 0x3F; + let Temp6: u8 = ((mac_address[3] >> 6) & 0x03) | ((mac_address[4] & 0x0F) << 2); + let Temp7: u8 = ((mac_address[4] >> 4) & 0x0F) | ((mac_address[5] & 0x03) << 4); + let Temp8: u8 = ((mac_address[5] >> 2) & 0x3F); + + let Result: u32 = (Temp1 as u32) + ^ (Temp2 as u32) + ^ (Temp3 as u32) + ^ (Temp4 as u32) + ^ (Temp5 as u32) + ^ (Temp6 as u32) + ^ (Temp7 as u32) + ^ (Temp8 as u32); + + if (Result >= FXMAC_MAX_HASH_BITS) { + Status = 1; // FXMAC_ERR_INVALID_PARAM } else { - let Temp1: u8 = ( mac_address[0]) & 0x3F; - let Temp2: u8 = ((mac_address[0] >> 6) & 0x03) | ((mac_address[1] & 0x0F) << 2); - let Temp3: u8 = ((mac_address[1] >> 4) & 0x0F) | ((mac_address[2] & 0x3) << 4); - let Temp4: u8 = ((mac_address[2] >> 2) & 0x3F); - let Temp5: u8 = mac_address[3] & 0x3F; - let Temp6: u8 = ((mac_address[3] >> 6) & 0x03) | ((mac_address[4] & 0x0F) << 2); - let Temp7: u8 = ((mac_address[4] >> 4) & 0x0F) | ((mac_address[5] & 0x03) << 4); - let Temp8: u8 = ((mac_address[5] >> 2) & 0x3F); - - let Result: u32 = (Temp1 as u32) ^ (Temp2 as u32) ^ (Temp3 as u32) ^ (Temp4 as u32) ^ - (Temp5 as u32) ^ (Temp6 as u32) ^ (Temp7 as u32) ^ (Temp8 as u32); - - if (Result >= FXMAC_MAX_HASH_BITS) { - Status = 1;// FXMAC_ERR_INVALID_PARAM - } else { - - if (Result < 32) { - HashAddr = read_reg((intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *const u32); + if (Result < 32) { + HashAddr = + read_reg((intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *const u32); HashAddr |= 1 << Result; - write_reg((intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *mut u32, HashAddr); - - } else { - HashAddr = read_reg((intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *const u32); + write_reg( + (intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *mut u32, + HashAddr, + ); + } else { + HashAddr = + read_reg((intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *const u32); HashAddr |= 1 << (Result - 32); - write_reg((intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *mut u32, HashAddr); - } - Status = 0; - } + write_reg( + (intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *mut u32, + HashAddr, + ); + } + Status = 0; } + } - Status + Status } /// Delete 48-bit MAC addresses in hash table. /// The device must be stopped before calling this function. -pub fn FXmac_DeleteHash(intance_p: &mut FXmac, mac_address: &[u8; 6]) -> u32 -{ - let mut HashAddr: u32 = 0; - let mut Status: u32 = 0; +pub fn FXmac_DeleteHash(intance_p: &mut FXmac, mac_address: &[u8; 6]) -> u32 { + let mut HashAddr: u32 = 0; + let mut Status: u32 = 0; - assert!(intance_p.is_ready == FT_COMPONENT_IS_READY); + assert!(intance_p.is_ready == FT_COMPONENT_IS_READY); - /* Be sure device has been stopped */ - if (intance_p.is_started == FT_COMPONENT_IS_STARTED) { - Status = 9; // (FXMAC_ERR_MAC_IS_PROCESSING); + /* Be sure device has been stopped */ + if (intance_p.is_started == FT_COMPONENT_IS_STARTED) { + Status = 9; // (FXMAC_ERR_MAC_IS_PROCESSING); + } else { + let mut Temp1: u8 = (mac_address[0]) & 0x3F; + let mut Temp2: u8 = ((mac_address[0] >> 6) & 0x03) | ((mac_address[1] & 0x0F) << 2); + let mut Temp3: u8 = ((mac_address[1] >> 4) & 0x0F) | ((mac_address[2] & 0x03) << 4); + let mut Temp4: u8 = ((mac_address[2] >> 2) & 0x3F); + let mut Temp5: u8 = (mac_address[3]) & 0x3F; + let mut Temp6: u8 = ((mac_address[3] >> 6) & 0x03) | ((mac_address[4] & 0x0F) << 2); + let mut Temp7: u8 = ((mac_address[4] >> 4) & 0x0F) | ((mac_address[5] & 0x03) << 4); + let mut Temp8: u8 = ((mac_address[5] >> 2) & 0x3F); + + let Result: u32 = (Temp1 as u32) + ^ (Temp2 as u32) + ^ (Temp3 as u32) + ^ (Temp4 as u32) + ^ (Temp5 as u32) + ^ (Temp6 as u32) + ^ (Temp7 as u32) + ^ (Temp8 as u32); + + if Result >= FXMAC_MAX_HASH_BITS { + Status = 1; //(FXMAC_ERR_INVALID_PARAM); } else { - let mut Temp1: u8 = ( mac_address[0]) & 0x3F; - let mut Temp2: u8 = ((mac_address[0] >> 6) & 0x03) | ((mac_address[1] & 0x0F) << 2); - let mut Temp3: u8 = ((mac_address[1] >> 4) & 0x0F) | ((mac_address[2] & 0x03) << 4); - let mut Temp4: u8 = ((mac_address[2] >> 2) & 0x3F); - let mut Temp5: u8 = (mac_address[3]) & 0x3F; - let mut Temp6: u8 = ((mac_address[3] >> 6) & 0x03) | ((mac_address[4] & 0x0F) << 2); - let mut Temp7: u8 = ((mac_address[4] >> 4) & 0x0F) | ((mac_address[5] & 0x03) << 4); - let mut Temp8: u8 = ((mac_address[5] >> 2) & 0x3F); - - let Result: u32 = (Temp1 as u32) ^ (Temp2 as u32) ^ (Temp3 as u32) ^ (Temp4 as u32) ^ - (Temp5 as u32) ^ (Temp6 as u32) ^ (Temp7 as u32) ^ (Temp8 as u32); - - if Result >= FXMAC_MAX_HASH_BITS { - Status = 1;//(FXMAC_ERR_INVALID_PARAM); - } else { - if Result < 32 { - HashAddr = read_reg((intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *const u32); + if Result < 32 { + HashAddr = + read_reg((intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *const u32); HashAddr &= !((1 << Result) as u32); - write_reg((intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *mut u32, HashAddr); - - } else { - HashAddr = read_reg((intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *const u32); + write_reg( + (intance_p.config.base_address + FXMAC_HASHL_OFFSET) as *mut u32, + HashAddr, + ); + } else { + HashAddr = + read_reg((intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *const u32); HashAddr &= !((1 << (Result - 32)) as u32); - write_reg((intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *mut u32, HashAddr); - } - Status = 0; - } + write_reg( + (intance_p.config.base_address + FXMAC_HASHH_OFFSET) as *mut u32, + HashAddr, + ); + } + Status = 0; } - Status + } + Status } -/// Set the Type ID match for this driver/device. The register is a 32-bit value. +/// Set the Type ID match for this driver/device. The register is a 32-bit value. /// The device must be stopped before calling this function. fn FXmacSetTypeIdCheck(id_check: u32, index: u8) -> u32 { let mut status: u32 = 0; - assert!((index < FXMAC_MAX_TYPE_ID as u8), "index of Type ID exceed {}", FXMAC_MAX_TYPE_ID); + assert!( + (index < FXMAC_MAX_TYPE_ID as u8), + "index of Type ID exceed {}", + FXMAC_MAX_TYPE_ID + ); let is_started = 0; /* Be sure device has been stopped */ - if is_started == FT_COMPONENT_IS_STARTED - { + if is_started == FT_COMPONENT_IS_STARTED { status = 9; //FXMAC_ERR_MAC_IS_PROCESSING error!("FXMAC is processing when calling FXmacSetTypeIdCheck function"); - } else { - + } else { /* Set the ID bits in MATCHx register */ - write_reg((FXMAC_IOBASE + FXMAC_MATCH1_OFFSET + (index * 4) as u64) as *mut u32, id_check); + write_reg( + (FXMAC_IOBASE + FXMAC_MATCH1_OFFSET + (index * 4) as u64) as *mut u32, + id_check, + ); status = FT_SUCCESS; } @@ -1266,201 +1323,260 @@ fn FXmacSetTypeIdCheck(id_check: u32, index: u8) -> u32 { /// FXmacSelectClk /// Determine the driver clock configuration based on the media independent interface /// FXMAC_CLK_TYPE_0 - fn FXmacSelectClk(instance_p: &mut FXmac) - { - let speed: u32 = instance_p.config.speed; - let FXMAC_WRITEREG32 = |base_address: u64, offset: u32, reg_value: u32| write_reg((base_address + offset as u64) as *mut u32, reg_value); - - assert!((speed == FXMAC_SPEED_10) || (speed == FXMAC_SPEED_100) || (speed == FXMAC_SPEED_1000) || (speed == FXMAC_SPEED_2500) || (speed == FXMAC_SPEED_10000)); - - if (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_USXGMII) || (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_XGMII) - { - if speed == FXMAC_SPEED_10000 - { - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); /*0x1c08*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x1); /*0x1c0c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ - } - else if speed == FXMAC_SPEED_5000 - { - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x8); /*0x1c08*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); /*0x1c0c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0); /*0x1c10*/ - } - } - else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_5GBASER - { - if speed == FXMAC_SPEED_5000 - { - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x8); /*0x1c08*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); /*0x1c0c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x0); /*0x1c10*/ - } - } - else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_2500BASEX - { - if speed == FXMAC_SPEED_25000 - { - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x1); /*0x1c08*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); /*0x1c0c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0); /*0x1c20*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); /*0x1c28*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); /*0x1c2c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); /*0x1c30*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); /*0x1c34*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ - } - } - else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII - { - info!("FXMAC_PHY_INTERFACE_MODE_SGMII init"); - if speed == FXMAC_SPEED_2500 - { - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x1); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ - } - else if speed == FXMAC_SPEED_1000 - { - info!("sgmii FXMAC_SPEED_1000"); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); /*0x1c08*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x8); /*0x1c0c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x0); /*0x1c24*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); /*0x1c2c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); /*0x1c30*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); /*0x1c34*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ - } - else if (speed == FXMAC_SPEED_100) || (speed == FXMAC_SPEED_10) - { +fn FXmacSelectClk(instance_p: &mut FXmac) { + let speed: u32 = instance_p.config.speed; + let FXMAC_WRITEREG32 = |base_address: u64, offset: u32, reg_value: u32| { + write_reg((base_address + offset as u64) as *mut u32, reg_value) + }; + + assert!( + (speed == FXMAC_SPEED_10) + || (speed == FXMAC_SPEED_100) + || (speed == FXMAC_SPEED_1000) + || (speed == FXMAC_SPEED_2500) + || (speed == FXMAC_SPEED_10000) + ); + + if (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_USXGMII) + || (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_XGMII) + { + if speed == FXMAC_SPEED_10000 { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x1); /*0x1c0c*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_PMA_XCVR_POWER_STATE, + 0x1, + ); /*0x1c10*/ + } else if speed == FXMAC_SPEED_5000 { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x8); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); /*0x1c0c*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_PMA_XCVR_POWER_STATE, + 0, + ); /*0x1c10*/ + } + } else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_5GBASER { + if speed == FXMAC_SPEED_5000 { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x8); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); /*0x1c0c*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_PMA_XCVR_POWER_STATE, + 0x0, + ); /*0x1c10*/ + } + } else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_2500BASEX { + if speed == FXMAC_SPEED_25000 { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x1); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); /*0x1c0c*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_PMA_XCVR_POWER_STATE, + 0x1, + ); /*0x1c10*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); + /*0x1c7c*/ + } + } else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII { + info!("FXMAC_PHY_INTERFACE_MODE_SGMII init"); + if speed == FXMAC_SPEED_2500 { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_PMA_XCVR_POWER_STATE, + 0x1, + ); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); + /*0x1c7c*/ + } else if speed == FXMAC_SPEED_1000 { + info!("sgmii FXMAC_SPEED_1000"); + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x8); /*0x1c0c*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_PMA_XCVR_POWER_STATE, + 0x1, + ); /*0x1c10*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x0); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); + /*0x1c7c*/ + } else if (speed == FXMAC_SPEED_100) || (speed == FXMAC_SPEED_10) { info!("sgmii FXMAC_SPEED_{}", speed); - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); /*0x1c08*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x8); /*0x1c0c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x0); /*0x1c24*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); /*0x1c28*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); /*0x1c2c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); /*0x1c30*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); /*0x1c34*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x1); /*0x1c70*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x1); /*0x1c7c*/ - } - } - else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_RGMII - { - info!("FXMAC_PHY_INTERFACE_MODE_RGMII init"); - if speed == FXMAC_SPEED_1000 - { - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_MII_SELECT, 0x1); /*0x1c18*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SEL_MII_ON_RGMII, 0x0); /*0x1c1c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, - 0x0); /*0x1c38*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL0, 0x1); /*0x1c80*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL1, 0x0); /*0x1c84*/ - } - else if speed == FXMAC_SPEED_100 - { - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_MII_SELECT, 0x1); /*0x1c18*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SEL_MII_ON_RGMII, 0x0); /*0x1c1c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, - 0x0); /*0x1c38*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL0, 0x0); /*0x1c80*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL1, 0x0); /*0x1c84*/ - } - else - { - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_MII_SELECT, 0x1); /*0x1c18*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SEL_MII_ON_RGMII, 0x0); /*0x1c1c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, - 0x1); /*0x1c38*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL0, 0x0); /*0x1c80*/ - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL1, 0x0); /*0x1c84*/ - } - } - else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_RMII - { - FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ - } - - FXmacHighSpeedConfiguration(instance_p, speed); - } - - fn FXmacHighSpeedConfiguration(instance_p: &mut FXmac, speed: u32) -{ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); /*0x1c04*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); /*0x1c08*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x8); /*0x1c0c*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_PMA_XCVR_POWER_STATE, + 0x1, + ); /*0x1c10*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x0); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x1); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x1); + /*0x1c7c*/ + } + } else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_RGMII { + info!("FXMAC_PHY_INTERFACE_MODE_RGMII init"); + if speed == FXMAC_SPEED_1000 { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_MII_SELECT, 0x1); /*0x1c18*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_SEL_MII_ON_RGMII, + 0x0, + ); /*0x1c1c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, + 0x0, + ); /*0x1c38*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_RGMII_TX_CLK_SEL0, + 0x1, + ); /*0x1c80*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_RGMII_TX_CLK_SEL1, + 0x0, + ); /*0x1c84*/ + } else if speed == FXMAC_SPEED_100 { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_MII_SELECT, 0x1); /*0x1c18*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_SEL_MII_ON_RGMII, + 0x0, + ); /*0x1c1c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, + 0x0, + ); /*0x1c38*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_RGMII_TX_CLK_SEL0, + 0x0, + ); /*0x1c80*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_RGMII_TX_CLK_SEL1, + 0x0, + ); /*0x1c84*/ + } else { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_MII_SELECT, 0x1); /*0x1c18*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_SEL_MII_ON_RGMII, + 0x0, + ); /*0x1c1c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, + 0x1, + ); /*0x1c38*/ + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_RGMII_TX_CLK_SEL0, + 0x0, + ); /*0x1c80*/ + FXMAC_WRITEREG32( + instance_p.config.base_address, + FXMAC_GEM_RGMII_TX_CLK_SEL1, + 0x0, + ); /*0x1c84*/ + } + } else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_RMII { + FXMAC_WRITEREG32(instance_p.config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); + /*0x1c48*/ + } + + FXmacHighSpeedConfiguration(instance_p, speed); +} + +fn FXmacHighSpeedConfiguration(instance_p: &mut FXmac, speed: u32) { let mut reg_value: u32 = 0; - let mut set_speed: i32 = 0; - match speed - { - FXMAC_SPEED_25000 => { - set_speed = 2; - } - FXMAC_SPEED_10000 => { - set_speed = 4; - } - FXMAC_SPEED_5000 => { - set_speed = 3; - } - FXMAC_SPEED_2500 => { - set_speed = 2; - } - FXMAC_SPEED_1000 => { - set_speed = 1; - } - _ => { - set_speed = 0; + let mut set_speed: i32 = 0; + match speed { + FXMAC_SPEED_25000 => { + set_speed = 2; + } + FXMAC_SPEED_10000 => { + set_speed = 4; + } + FXMAC_SPEED_5000 => { + set_speed = 3; + } + FXMAC_SPEED_2500 => { + set_speed = 2; + } + FXMAC_SPEED_1000 => { + set_speed = 1; + } + _ => { + set_speed = 0; } } @@ -1468,218 +1584,204 @@ fn FXmacSetTypeIdCheck(id_check: u32, index: u8) -> u32 { reg_value = read_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC as u64) as *const u32); reg_value &= !FXMAC_GEM_HSMACSPEED_MASK; reg_value |= (set_speed as u32) & FXMAC_GEM_HSMACSPEED_MASK; - write_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC as u64) as *mut u32, reg_value); + write_reg( + (FXMAC_IOBASE + FXMAC_GEM_HSMAC as u64) as *mut u32, + reg_value, + ); reg_value = read_reg((FXMAC_IOBASE + FXMAC_GEM_HSMAC as u64) as *const u32); info!("FXMAC_GEM_HSMAC is {:#x}", reg_value); } - /// FXmacInitInterface /// Initialize the MAC controller configuration based on the PHY interface type - fn FXmacInitInterface(instance_p: &mut FXmac) - { - let mut config: u32 = 0; - let mut control: u32 = 0; - - info!("FXmacInitInterface, PHY MODE:{:?}", instance_p.config.interface); - - if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_XGMII - { - config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); - config &= !FXMAC_NWCFG_PCSSEL_MASK; - write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); - - control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - control |= FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); - - instance_p.config.duplex = 1; - } - else if (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_USXGMII) || (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_5GBASER) - { - info!("usx interface is {:?}",instance_p.config.interface); - /* network_config */ - instance_p.config.duplex = 1; - config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); - config |= FXMAC_NWCFG_PCSSEL_MASK; - config &= !FXMAC_NWCFG_100_MASK; - config &= !FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; - if (instance_p.config.duplex == 1) - { - info!("is duplex"); - config |= FXMAC_NWCFG_FDEN_MASK; - } - - write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); - - /* network_control */ - control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - control |= FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); - - - /* High speed PCS control register */ - control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); - - if (instance_p.config.speed == FXMAC_SPEED_10000) - { - info!("is 10G"); - control |= FXMAC_GEM_USX_HS_MAC_SPEED_10G; - control |= FXMAC_GEM_USX_SERDES_RATE_10G; - } - else if (instance_p.config.speed == FXMAC_SPEED_25000) - { - control |= FXMAC_GEM_USX_HS_MAC_SPEED_2_5G; - } - else if (instance_p.config.speed == FXMAC_SPEED_1000) - { - control |= FXMAC_GEM_USX_HS_MAC_SPEED_1G; - } - else if (instance_p.config.speed == FXMAC_SPEED_100) - { - control |= FXMAC_GEM_USX_HS_MAC_SPEED_100M; - } - else if(instance_p.config.speed == FXMAC_SPEED_5000) - { - control |= FXMAC_GEM_USX_HS_MAC_SPEED_5G; - control |= FXMAC_GEM_USX_SERDES_RATE_5G; - } - - control &= !(FXMAC_GEM_USX_TX_SCR_BYPASS | FXMAC_GEM_USX_RX_SCR_BYPASS); - control |= FXMAC_GEM_USX_RX_SYNC_RESET; - write_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, control); - - control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); - control &= !FXMAC_GEM_USX_RX_SYNC_RESET; - control |= FXMAC_GEM_USX_TX_DATAPATH_EN; - control |= FXMAC_GEM_USX_SIGNAL_OK; - - write_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, control); - - } - else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_2500BASEX - { - /* network_config */ - instance_p.config.duplex = 1; - config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); - config |= FXMAC_NWCFG_PCSSEL_MASK | FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; - config &= !FXMAC_NWCFG_100_MASK; - - if (instance_p.config.duplex == 1) - { - config |= FXMAC_NWCFG_FDEN_MASK; - } - write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); - - /* network_control */ - control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - control &= !FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; - control |= FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; /* Use high speed MAC */ - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); - - /* High speed PCS control register */ - control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); - - if (instance_p.config.speed == FXMAC_SPEED_25000) - { - control |= FXMAC_GEM_USX_HS_MAC_SPEED_2_5G; - } - - control &= !(FXMAC_GEM_USX_TX_SCR_BYPASS | FXMAC_GEM_USX_RX_SCR_BYPASS); - control |= FXMAC_GEM_USX_RX_SYNC_RESET; - write_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, control); - - control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); - control &= !FXMAC_GEM_USX_RX_SYNC_RESET; - control |= FXMAC_GEM_USX_TX_DATAPATH_EN; - control |= FXMAC_GEM_USX_SIGNAL_OK; - - write_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, control); - - } - else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII - { - config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); - config |= FXMAC_NWCFG_PCSSEL_MASK | FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; - - config &= !(FXMAC_NWCFG_100_MASK | FXMAC_NWCFG_FDEN_MASK|FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK); - - if instance_p.moudle_id >= 2 - { - config &= !FXMAC_NWCFG_1000_MASK; - } - - if instance_p.config.duplex != 0 - { - config |= FXMAC_NWCFG_FDEN_MASK; - } - - if instance_p.config.speed == FXMAC_SPEED_100 - { - config |= FXMAC_NWCFG_100_MASK; - } - else if instance_p.config.speed == FXMAC_SPEED_1000 - { - config |= FXMAC_NWCFG_1000_MASK; - } - - write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); - - if instance_p.config.speed == FXMAC_SPEED_2500 - { - control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - control |= FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); - } - else - { - control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - control &= !FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); - } - - control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - control &= !FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); - - control = read_reg((FXMAC_IOBASE + FXMAC_PCS_CONTROL_OFFSET) as *const u32); - control |= FXMAC_PCS_CONTROL_ENABLE_AUTO_NEG; - write_reg((FXMAC_IOBASE + FXMAC_PCS_CONTROL_OFFSET) as *mut u32, control); - } - else - { - config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); - - info!("select rgmii"); - - config &= !FXMAC_NWCFG_PCSSEL_MASK; - config &= !(FXMAC_NWCFG_100_MASK | FXMAC_NWCFG_FDEN_MASK); - - if instance_p.moudle_id >= 2 - { - config &= !FXMAC_NWCFG_1000_MASK; - } - - if instance_p.config.duplex != 0 - { - config |= FXMAC_NWCFG_FDEN_MASK; - } - - if instance_p.config.speed == FXMAC_SPEED_100 - { - config |= FXMAC_NWCFG_100_MASK; - } - else if instance_p.config.speed == FXMAC_SPEED_1000 - { - config |= FXMAC_NWCFG_1000_MASK; - } - - write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); - - control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); - control &= !FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ - write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); - } - } +fn FXmacInitInterface(instance_p: &mut FXmac) { + let mut config: u32 = 0; + let mut control: u32 = 0; + + info!( + "FXmacInitInterface, PHY MODE:{:?}", + instance_p.config.interface + ); + + if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_XGMII { + config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + config &= !FXMAC_NWCFG_PCSSEL_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); + + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control |= FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + + instance_p.config.duplex = 1; + } else if (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_USXGMII) + || (instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_5GBASER) + { + info!("usx interface is {:?}", instance_p.config.interface); + /* network_config */ + instance_p.config.duplex = 1; + config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + config |= FXMAC_NWCFG_PCSSEL_MASK; + config &= !FXMAC_NWCFG_100_MASK; + config &= !FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; + if (instance_p.config.duplex == 1) { + info!("is duplex"); + config |= FXMAC_NWCFG_FDEN_MASK; + } + + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); + + /* network_control */ + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control |= FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + + /* High speed PCS control register */ + control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); + + if (instance_p.config.speed == FXMAC_SPEED_10000) { + info!("is 10G"); + control |= FXMAC_GEM_USX_HS_MAC_SPEED_10G; + control |= FXMAC_GEM_USX_SERDES_RATE_10G; + } else if (instance_p.config.speed == FXMAC_SPEED_25000) { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_2_5G; + } else if (instance_p.config.speed == FXMAC_SPEED_1000) { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_1G; + } else if (instance_p.config.speed == FXMAC_SPEED_100) { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_100M; + } else if (instance_p.config.speed == FXMAC_SPEED_5000) { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_5G; + control |= FXMAC_GEM_USX_SERDES_RATE_5G; + } + + control &= !(FXMAC_GEM_USX_TX_SCR_BYPASS | FXMAC_GEM_USX_RX_SCR_BYPASS); + control |= FXMAC_GEM_USX_RX_SYNC_RESET; + write_reg( + (FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, + control, + ); + + control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); + control &= !FXMAC_GEM_USX_RX_SYNC_RESET; + control |= FXMAC_GEM_USX_TX_DATAPATH_EN; + control |= FXMAC_GEM_USX_SIGNAL_OK; + + write_reg( + (FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, + control, + ); + } else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_2500BASEX { + /* network_config */ + instance_p.config.duplex = 1; + config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + config |= FXMAC_NWCFG_PCSSEL_MASK | FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; + config &= !FXMAC_NWCFG_100_MASK; + + if (instance_p.config.duplex == 1) { + config |= FXMAC_NWCFG_FDEN_MASK; + } + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); + + /* network_control */ + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control &= !FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; + control |= FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; /* Use high speed MAC */ + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + + /* High speed PCS control register */ + control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); + + if (instance_p.config.speed == FXMAC_SPEED_25000) { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_2_5G; + } + + control &= !(FXMAC_GEM_USX_TX_SCR_BYPASS | FXMAC_GEM_USX_RX_SCR_BYPASS); + control |= FXMAC_GEM_USX_RX_SYNC_RESET; + write_reg( + (FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, + control, + ); + + control = read_reg((FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *const u32); + control &= !FXMAC_GEM_USX_RX_SYNC_RESET; + control |= FXMAC_GEM_USX_TX_DATAPATH_EN; + control |= FXMAC_GEM_USX_SIGNAL_OK; + + write_reg( + (FXMAC_IOBASE + FXMAC_GEM_USX_CONTROL_OFFSET) as *mut u32, + control, + ); + } else if instance_p.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII { + config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + config |= FXMAC_NWCFG_PCSSEL_MASK | FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; + + config &= !(FXMAC_NWCFG_100_MASK + | FXMAC_NWCFG_FDEN_MASK + | FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK); + + if instance_p.moudle_id >= 2 { + config &= !FXMAC_NWCFG_1000_MASK; + } + + if instance_p.config.duplex != 0 { + config |= FXMAC_NWCFG_FDEN_MASK; + } + + if instance_p.config.speed == FXMAC_SPEED_100 { + config |= FXMAC_NWCFG_100_MASK; + } else if instance_p.config.speed == FXMAC_SPEED_1000 { + config |= FXMAC_NWCFG_1000_MASK; + } + + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); + + if instance_p.config.speed == FXMAC_SPEED_2500 { + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control |= FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + } else { + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control &= !FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + } + + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control &= !FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + + control = read_reg((FXMAC_IOBASE + FXMAC_PCS_CONTROL_OFFSET) as *const u32); + control |= FXMAC_PCS_CONTROL_ENABLE_AUTO_NEG; + write_reg( + (FXMAC_IOBASE + FXMAC_PCS_CONTROL_OFFSET) as *mut u32, + control, + ); + } else { + config = read_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *const u32); + + info!("select rgmii"); + + config &= !FXMAC_NWCFG_PCSSEL_MASK; + config &= !(FXMAC_NWCFG_100_MASK | FXMAC_NWCFG_FDEN_MASK); + + if instance_p.moudle_id >= 2 { + config &= !FXMAC_NWCFG_1000_MASK; + } + + if instance_p.config.duplex != 0 { + config |= FXMAC_NWCFG_FDEN_MASK; + } + + if instance_p.config.speed == FXMAC_SPEED_100 { + config |= FXMAC_NWCFG_100_MASK; + } else if instance_p.config.speed == FXMAC_SPEED_1000 { + config |= FXMAC_NWCFG_1000_MASK; + } + + write_reg((FXMAC_IOBASE + FXMAC_NWCFG_OFFSET) as *mut u32, config); + + control = read_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *const u32); + control &= !FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ + write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, control); + } +} diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index d490b26ba..07de822b7 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -746,22 +746,24 @@ pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_s status } -pub fn FXmacLwipPortTx(instance: &mut FXmac, pbuf: Vec>) -> u32 +pub fn FXmacLwipPortTx(instance: &mut FXmac, pbuf: Vec>) -> i32 { + info!("TX transmit packets"); // 发送网络包时注意屏蔽下中断 // check if space is available to send let freecnt = (instance.tx_bd_queue.bdring).free_cnt; if freecnt <= 5 { + info!("TX freecnt={}, let's process sent BDs", freecnt); //let txring = &mut (instance.tx_bd_queue.bdring); FXmacProcessSentBds(instance); } if (instance.tx_bd_queue.bdring).free_cnt != 0 { - FXmacSgsend(instance, pbuf) + FXmacSgsend(instance, pbuf) as i32 }else{ error!(" TX packets dropped, no space"); - 3 // FREERTOS_XMAC_NO_VALID_SPACE + -3 // FREERTOS_XMAC_NO_VALID_SPACE } } @@ -875,7 +877,8 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { } /// 收包函数 -pub fn FXmacRecvHandler(instance_p: &mut FXmac) { +pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { + info!("RX receive packets"); let mut recv_packets = Vec::new(); let mut rxbdset: *mut FXmacBd = null_mut(); @@ -954,6 +957,11 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) { SetupRxBds(instance_p); } + if recv_packets.len() > 0 { + Some(recv_packets) + } else { + None + } } pub fn SetupRxBds(instance_p: &mut FXmac) { diff --git a/src/fxmac_intr.rs b/src/fxmac_intr.rs index e184c8029..c78318042 100644 --- a/src/fxmac_intr.rs +++ b/src/fxmac_intr.rs @@ -116,7 +116,7 @@ pub fn FXmacErrorHandler(instance_p: &mut FXmac, direction: u8, error_word: u32) pub fn FXmacRecvIsrHandler(instance: &mut FXmac) { info!("Recv Isr"); write_reg((instance.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_RXCOMPL_MASK); - // instance_p->recv_flg ++; + instance.lwipport.recv_flg += 1; ethernetif_input_to_recv_packets(instance); } @@ -126,9 +126,11 @@ pub static XMAC: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); pub fn xmac_intr_handler(intr: u64) { info!("Handling xmac intr ..."); + let xmac = XMAC.load(Ordering::Relaxed); -if !xmac.is_null() { + if !xmac.is_null() { let xmac_ptr = unsafe{ &mut (*xmac)}; + // maybe irq num let vector = xmac_ptr.config.queue_irq_num[0] ; FXmacIntrHandler(vector as i32, xmac_ptr); diff --git a/src/lib.rs b/src/lib.rs index c32f3fa67..5d2fc373c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,9 @@ mod fxmac_dma; mod fxmac_intr; pub mod fxmac; +pub use fxmac::{FXmac, xmac_init}; +pub use fxmac_dma::{FXmacLwipPortTx, FXmacRecvHandler}; + #[cfg(test)] mod tests { #[test] diff --git a/src/utils.rs b/src/utils.rs index df30a42e4..9353d2c93 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -144,14 +144,15 @@ pub fn msdelay(ms: u64) { } #[linkage = "weak"] -#[export_name = "phys_to_virt"] +#[unsafe(export_name = "phys_to_virt")] pub fn phys_to_virt(addr: usize) -> usize { + debug!("fxmac: phys_to_virt {:#x}", addr); addr } /// 申请DMA内存页 #[linkage = "weak"] -#[export_name = "dma_alloc_coherent"] +#[unsafe(export_name = "dma_alloc_coherent")] pub fn dma_alloc_coherent(pages: usize) -> (usize, usize) { let paddr: Box<[u32]> = if pages == 1 { Box::new([0; 1024]) // 4096 @@ -167,22 +168,23 @@ pub fn dma_alloc_coherent(pages: usize) -> (usize, usize) { let paddr = Box::into_raw(paddr) as *const u32 as usize; //let vaddr = phys_to_virt(paddr); let vaddr = paddr; - trace!("dma alloc paddr: {:#x}, len={}", paddr, len); + debug!("fxmac: dma alloc paddr: {:#x}, len={}", paddr, len); (vaddr, paddr) } /// 释放DMA内存页 #[linkage = "weak"] -#[export_name = "dma_free_coherent"] +#[unsafe(export_name = "dma_free_coherent")] pub fn dma_free_coherent(vaddr: usize, pages: usize) { + debug!("fxmac: dma free vaddr: {:#x}, pages={}", vaddr, pages); let palloc = vaddr as *mut [u32; 1024]; unsafe{ drop(Box::from_raw(palloc)); } } /// 请求分配irq #[linkage = "weak"] -#[export_name = "dma_request_irq"] +#[unsafe(export_name = "dma_request_irq")] pub fn dma_request_irq(irq: usize, handler: fn(u64)) { unimplemented!() } From 873b7e6db6e42ff9c2e8cf6d05ef85069c63db81 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Mon, 20 Jan 2025 12:23:15 +0800 Subject: [PATCH 006/132] modified CPU CORE ID, Phy read/write fn, and dma pbufs allocation --- src/fxmac_dma.rs | 22 ++++++++++++---------- src/fxmac_phy.rs | 4 ++-- src/utils.rs | 18 +++++++++--------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index 07de822b7..4bcdae7bf 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -340,10 +340,10 @@ pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; - info!("rxringptr: {:p}", rxringptr); - info!("txringptr: {:p}", txringptr); - info!("rx_bdspace: {:p}", &instance_p.lwipport.buffer.rx_bdspace); - info!("tx_bdspace: {:p}", &instance_p.lwipport.buffer.tx_bdspace); + info!("FXmacInitDma, rxringptr: {:p}", rxringptr); + info!("FXmacInitDma, txringptr: {:p}", txringptr); + info!("FXmacInitDma, rx_bdspace: {:p}", &instance_p.lwipport.buffer.rx_bdspace); + info!("FXmacInitDma, tx_bdspace: {:p}", &instance_p.lwipport.buffer.tx_bdspace); // Setup RxBD space. // 对BD域清零 @@ -783,6 +783,7 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { /* obtain as many BD's */ status = FXmacBdRingAlloc(txring, n_pbufs, txbdset); + debug!("FXmacSgsend, txbdset is null ? {}", txbdset.is_null()); let mut txbd: *mut FXmacBd = txbdset; for q in &p { bdindex = FXMAC_BD_TO_INDEX(txring, txbd as u64); @@ -985,14 +986,14 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { }; let alloc_rx_buffer_pages: usize = (max_frame_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; - let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = crate::utils::dma_alloc_coherent(alloc_rx_buffer_pages); - status = FXmacBdRingAlloc(rxring, 1, rxbd); - + assert!(!rxbd.is_null()); status = FXmacBdRingToHw(rxring, 1, rxbd); + // 继续使用前面申请过的dma pbufs, 不再清理并重新申请 + //let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = crate::utils::dma_alloc_coherent(alloc_rx_buffer_pages); - crate::utils::FCacheDCacheInvalidateRange(rx_mbufs_vaddr as u64, max_frame_size as u64); + //crate::utils::FCacheDCacheInvalidateRange(rx_mbufs_vaddr as u64, max_frame_size as u64); let bdindex: u32 = FXMAC_BD_TO_INDEX(rxring, rxbd as u64); let mut temp = rxbd as *mut u32; @@ -1009,9 +1010,10 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { crate::utils::DSB(); // 设置BD的地址字段(word 0) - fxmac_bd_set_address_rx(rxbd as u64, rx_mbufs_dma as u64); + //fxmac_bd_set_address_rx(rxbd as u64, rx_mbufs_dma as u64); - instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize] = rx_mbufs_vaddr as u64; + assert!(instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize] != 0); + //instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize] = rx_mbufs_vaddr as u64; } } diff --git a/src/fxmac_phy.rs b/src/fxmac_phy.rs index be374ef60..809ca4401 100644 --- a/src/fxmac_phy.rs +++ b/src/fxmac_phy.rs @@ -94,7 +94,7 @@ pub fn FXmacPhyWrite(instance_p: &mut FXmac, phy_address: u32, register_num: u32 ipisr = read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32); ip_write_temp = ipisr; - if !(ip_write_temp & FXMAC_NWSR_MDIOIDLE_MASK) == 0 { + if (ip_write_temp & FXMAC_NWSR_MDIOIDLE_MASK) != 0 { break; } } @@ -130,7 +130,7 @@ pub fn FXmacPhyRead(instance_p: &mut FXmac, phy_address: u32, register_num: u32, ipisr = read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32); IpReadTemp = ipisr; - if !(IpReadTemp & FXMAC_NWSR_MDIOIDLE_MASK) == 0 { + if (IpReadTemp & FXMAC_NWSR_MDIOIDLE_MASK) != 0 { break; } } diff --git a/src/utils.rs b/src/utils.rs index 9353d2c93..d9fb74462 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,10 +1,10 @@ use core::arch::asm; // PhytiumPi -pub const CORE0_AFF: u64 = 0x000; -pub const CORE1_AFF: u64 = 0x100; -pub const CORE2_AFF: u64 = 0x200; -pub const CORE3_AFF: u64 = 0x201; +pub const CORE0_AFF: u64 = 0x200; +pub const CORE1_AFF: u64 = 0x201; +pub const CORE2_AFF: u64 = 0x00; +pub const CORE3_AFF: u64 = 0x100; pub const FCORE_NUM: u64 = 4; /// Read reg: MPIDR_EL1 @@ -144,15 +144,15 @@ pub fn msdelay(ms: u64) { } #[linkage = "weak"] -#[unsafe(export_name = "phys_to_virt")] +#[unsafe(export_name = "phys_to_virt_fxmac")] pub fn phys_to_virt(addr: usize) -> usize { debug!("fxmac: phys_to_virt {:#x}", addr); addr } -/// 申请DMA内存页 +/// 申请DMA连续内存页 #[linkage = "weak"] -#[unsafe(export_name = "dma_alloc_coherent")] +#[unsafe(export_name = "dma_alloc_coherent_fxmac")] pub fn dma_alloc_coherent(pages: usize) -> (usize, usize) { let paddr: Box<[u32]> = if pages == 1 { Box::new([0; 1024]) // 4096 @@ -175,7 +175,7 @@ pub fn dma_alloc_coherent(pages: usize) -> (usize, usize) { /// 释放DMA内存页 #[linkage = "weak"] -#[unsafe(export_name = "dma_free_coherent")] +#[unsafe(export_name = "dma_free_coherent_fxmac")] pub fn dma_free_coherent(vaddr: usize, pages: usize) { debug!("fxmac: dma free vaddr: {:#x}, pages={}", vaddr, pages); let palloc = vaddr as *mut [u32; 1024]; @@ -184,7 +184,7 @@ pub fn dma_free_coherent(vaddr: usize, pages: usize) { /// 请求分配irq #[linkage = "weak"] -#[unsafe(export_name = "dma_request_irq")] +#[unsafe(export_name = "dma_request_irq_fxmac")] pub fn dma_request_irq(irq: usize, handler: fn(u64)) { unimplemented!() } From 164ae3e77a349251096dec1c24877488dbb4e567 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Mon, 20 Jan 2025 14:45:55 +0800 Subject: [PATCH 007/132] Alloc DMA pbufs for TX and RX add Flush Data cache function --- src/fxmac_dma.rs | 253 +++++++++++++++++++---------------------------- src/utils.rs | 28 ++++++ 2 files changed, 131 insertions(+), 150 deletions(-) diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index 4bcdae7bf..4b4634af8 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -242,149 +242,25 @@ fn FXMAC_RING_SEEKAHEAD(ring_ptr: &mut FXmacBdRing, mut bdptr: *mut FXmacBd, num bdptr = addr as *mut FXmacBd; } -/* -pub fn FXmacBdRingCreate() -> u32 { - - // 为DMA构建环形缓冲区内存 - - let alloc_tx_ring_pages = - ((MACB_TX_RING_SIZE * DMA_DESC_SIZE) + (M::PAGE_SIZE - 1)) / M::PAGE_SIZE; - let alloc_rx_ring_pages = - ((MACB_RX_RING_SIZE * DMA_DESC_SIZE) + (M::PAGE_SIZE - 1)) / M::PAGE_SIZE; - let tx_ring_dma = M::dma_alloc_coherent(alloc_tx_ring_pages); - let rx_ring_dma = M::dma_alloc_coherent(alloc_rx_ring_pages); - - let tx_ring = unsafe { - slice::from_raw_parts_mut( - phys_to_virt(tx_ring_dma) as *mut DmaDesc, - MACB_TX_RING_SIZE * DMA_DESC_SIZE / size_of::(), // 4096/16 = 256 个 dma_desc ? - ) - }; - - let rx_ring = unsafe { - slice::from_raw_parts_mut( - phys_to_virt(rx_ring_dma) as *mut DmaDesc, - MACB_RX_RING_SIZE * DMA_DESC_SIZE / size_of::(), - ) - }; - - let mut send_buffers = Vec::with_capacity(tx_ring.len()); - let mut recv_buffers = Vec::with_capacity(rx_ring.len()); - - // 一起申请所有RX内存 - let alloc_rx_buffer_pages = - ((MACB_RX_RING_SIZE * buffer_size) + (M::PAGE_SIZE - 1)) / M::PAGE_SIZE; - let rx_buffer_dma: usize = M::dma_alloc_coherent(alloc_rx_buffer_pages); - - info!("Set ring desc buffer for RX"); - let mut count = 0; - let mut paddr: u64 = rx_buffer_dma as u64; - for i in 0..MACB_RX_RING_SIZE { - if i == MACB_RX_RING_SIZE - 1 { - paddr |= 1 << MACB_RX_WRAP_OFFSET; - } - - if (config.hw_dma_cap & HW_DMA_CAP_64B) != 0 { - count = i * 2; - rx_ring[count + 1].addr = upper_32_bits(paddr); // Fill DmaDesc64.addrh - } else { - count = i; - } - rx_ring[count].ctrl = 0; - rx_ring[count].addr = lower_32_bits(paddr); - - recv_buffers.push(phys_to_virt(paddr as usize)); - paddr += buffer_size as u64; - - // sync memery, fence指令? - } - flush_dcache_range(); // RX dma ring and buffer - - // 一起申请所有TX内存 - let alloc_tx_buffer_pages = - ((MACB_TX_RING_SIZE * buffer_size) + (M::PAGE_SIZE - 1)) / M::PAGE_SIZE; - let tx_buffer_dma: usize = M::dma_alloc_coherent(alloc_tx_buffer_pages); - info!("Set ring desc buffer for TX"); - count = 0; - paddr = tx_buffer_dma as u64; - for i in 0..MACB_TX_RING_SIZE { - if (config.hw_dma_cap & HW_DMA_CAP_64B) != 0 { - count = i * 2; - tx_ring[count + 1].addr = upper_32_bits(paddr); // Fill DmaDesc64.addrh - } else { - count = i; - } - tx_ring[count].addr = lower_32_bits(paddr); - - if i == MACB_TX_RING_SIZE - 1 { - tx_ring[count].ctrl = (1 << MACB_TX_USED_OFFSET) | (1 << MACB_TX_WRAP_OFFSET); - } else { - tx_ring[count].ctrl = (1 << MACB_TX_USED_OFFSET); - } - // Used – must be zero for the controller to read data to the transmit buffer. - // The controller sets this to one for the first buffer of a frame once it has been successfully transmitted. - // Software must clear this bit before the buffer can be used again. - send_buffers.push(phys_to_virt(paddr as usize)); - paddr += buffer_size as u64; - } - flush_dcache_range(); // TX dma ring - -} -*/ - - -pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 -{ - //let mut rxbd: FXmacBd = [0; FXMAC_BD_NUM_WORDS]; +pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { + // 为DMA构建环形缓冲区内存 + let mut status: u32 = 0; let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; - info!("FXmacInitDma, rxringptr: {:p}", rxringptr); - info!("FXmacInitDma, txringptr: {:p}", txringptr); - info!("FXmacInitDma, rx_bdspace: {:p}", &instance_p.lwipport.buffer.rx_bdspace); - info!("FXmacInitDma, tx_bdspace: {:p}", &instance_p.lwipport.buffer.tx_bdspace); - // Setup RxBD space. - // 对BD域清零 - //let mut bdtemplate: FXmacBd = unsafe { core::mem::zeroed() }; - - // FXmacBd: The FXMAC_Bd is the type for buffer descriptors (BDs). - let mut bdtemplate: FXmacBd = [0; FXMAC_BD_NUM_WORDS]; - - // Create the RxBD ring, bdspace地址必须对齐128 - // 创建收包的环形缓冲区 - let mut status: u32 = FXmacBdRingCreate(rxringptr, &instance_p.lwipport.buffer.rx_bdspace as *const _ as u64, &instance_p.lwipport.buffer.rx_bdspace as *const _ as u64, BD_ALIGNMENT, FXMAX_RX_PBUFS_LENGTH as u32); - - // 将给定的BD, 克隆到list中的每个BD上 - status = FXmacBdRingClone(rxringptr, &mut bdtemplate, FXMAC_RECV); - - - let mut bdtemplate: [u32; FXMAC_BD_NUM_WORDS] = [0; FXMAC_BD_NUM_WORDS]; - - // FXMAC_BD_SET_STATUS() - // Set the BD's Status field (word 1). - fxmac_bd_write((&bdtemplate as *const _ as u64), FXMAC_BD_STAT_OFFSET, - fxmac_bd_read((&bdtemplate as *const _ as u64), FXMAC_BD_STAT_OFFSET) | (FXMAC_TXBUF_USED_MASK)); - - /* Create the TxBD ring */ - status = FXmacBdRingCreate(txringptr, &instance_p.lwipport.buffer.tx_bdspace as *const _ as u64, &instance_p.lwipport.buffer.tx_bdspace as *const _ as u64, BD_ALIGNMENT, FXMAX_TX_PBUFS_LENGTH as u32); - - /* We reuse the bd template, as the same one will work for both rx and tx. */ - status = FXmacBdRingClone(txringptr, &mut bdtemplate, FXMAC_SEND); - - /* - * Allocate RX descriptors, 1 RxBD at a time. - */ + // Allocate RX descriptors, 1 RxBD at a time. info!("Allocate RX descriptors, 1 RxBD at a time."); for i in 0..FXMAX_RX_PBUFS_LENGTH { let max_frame_size = if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 { info!("FXMAC_LWIP_PORT_CONFIG_JUMBO"); FXMAC_MAX_FRAME_SIZE_JUMBO } else { info!("NO CONFIG_JUMBO"); FXMAC_MAX_FRAME_SIZE }; - let alloc_rx_buffer_pages = (max_frame_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; + let alloc_rx_buffer_pages = (max_frame_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = crate::utils::dma_alloc_coherent(alloc_rx_buffer_pages); + let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; let rxbd: *mut FXmacBd = null_mut(); //let my_speed: Box = Box::new(88); //rxbd = Box::into_raw(my_speed); @@ -394,6 +270,7 @@ pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 // 在BD list中预留待设置的BD status = FXmacBdRingAlloc(rxringptr, 1, rxbd); + assert!(!rxbd.is_null()); if (status != 0) { error!("FXmacInitDma: Error allocating RxBD"); @@ -429,8 +306,79 @@ pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize] = rx_mbufs_vaddr as u64; } - FXmacSetQueuePtr(instance_p.rx_bd_queue.bdring.phys_base_addr, 0, FXMAC_RECV); + for index in 0..FXMAX_TX_PBUFS_LENGTH { + let max_fr_size = if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 + { + FXMAC_MAX_FRAME_SIZE_JUMBO + } else { + FXMAC_MAX_FRAME_SIZE + }; + let alloc_pages = (max_fr_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; + let (mut tx_mbufs_vaddr, mut tx_mbufs_dma) = crate::utils::dma_alloc_coherent(alloc_pages); + instance_p.lwipport.buffer.tx_pbufs_storage[index as usize] = tx_mbufs_vaddr as u64; + + /* + let txbd: *mut FXmacBd = null_mut(); + FXmacBdRingAlloc(txringptr, 1, txbd); + FXmacBdRingToHw(txringptr, 1, txbd); + + let bdindex = FXMAC_BD_TO_INDEX(txringptr, txbd as u64); + assert!(index == bdindex as usize); + */ + // From index to BD + let txbd = (txringptr.base_bd_addr + (index as u64 * txringptr.separation as u64)) as *mut FXmacBd; + + fxmac_bd_set_address_tx(txbd as u64, tx_mbufs_dma as u64); + //curbdpntr = FXMAC_BD_RING_NEXT(txring, curbdpntr); + crate::utils::DSB(); + } + 0 +} + +pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 +{ + //let mut rxbd: FXmacBd = [0; FXMAC_BD_NUM_WORDS]; + + let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; + let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; + info!("FXmacInitDma, rxringptr: {:p}", rxringptr); + info!("FXmacInitDma, txringptr: {:p}", txringptr); + info!("FXmacInitDma, rx_bdspace: {:p}", &instance_p.lwipport.buffer.rx_bdspace); + info!("FXmacInitDma, tx_bdspace: {:p}", &instance_p.lwipport.buffer.tx_bdspace); + + // Setup RxBD space. + // 对BD域清零 + //let mut bdtemplate: FXmacBd = unsafe { core::mem::zeroed() }; + + // FXmacBd: The FXMAC_Bd is the type for buffer descriptors (BDs). + let mut bdtemplate: FXmacBd = [0; FXMAC_BD_NUM_WORDS]; + + // Create the RxBD ring, bdspace地址必须对齐128 + // 创建收包的环形缓冲区 + let mut status: u32 = FXmacBdRingCreate(rxringptr, &instance_p.lwipport.buffer.rx_bdspace as *const _ as u64, &instance_p.lwipport.buffer.rx_bdspace as *const _ as u64, BD_ALIGNMENT, FXMAX_RX_PBUFS_LENGTH as u32); + + // 将给定的BD, 克隆到list中的每个BD上 + status = FXmacBdRingClone(rxringptr, &mut bdtemplate, FXMAC_RECV); + + + let mut bdtemplate: [u32; FXMAC_BD_NUM_WORDS] = [0; FXMAC_BD_NUM_WORDS]; + + // FXMAC_BD_SET_STATUS() + // Set the BD's Status field (word 1). + fxmac_bd_write((&bdtemplate as *const _ as u64), FXMAC_BD_STAT_OFFSET, + fxmac_bd_read((&bdtemplate as *const _ as u64), FXMAC_BD_STAT_OFFSET) | (FXMAC_TXBUF_USED_MASK)); + + /* Create the TxBD ring */ + status = FXmacBdRingCreate(txringptr, &instance_p.lwipport.buffer.tx_bdspace as *const _ as u64, &instance_p.lwipport.buffer.tx_bdspace as *const _ as u64, BD_ALIGNMENT, FXMAX_TX_PBUFS_LENGTH as u32); + + /* We reuse the bd template, as the same one will work for both rx and tx. */ + status = FXmacBdRingClone(txringptr, &mut bdtemplate, FXMAC_SEND); + + // 创建收发网络包的DMA内存 + FXmacAllocDmaPbufs(instance_p); + + FXmacSetQueuePtr(instance_p.rx_bd_queue.bdring.phys_base_addr, 0, FXMAC_RECV); FXmacSetQueuePtr(instance_p.tx_bd_queue.bdring.phys_base_addr, 0, FXMAC_SEND); let FXMAC_TAIL_QUEUE = |queue: u64| 0x0e80 + (queue << 2); @@ -772,6 +720,7 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { let mut status: u32 = 0; let mut bdindex: u32 = 0; let mut max_fr_size: u32 = 0; + let mut send_len: u32 = 0; let mut last_txbd: *mut FXmacBd = null_mut(); let txbdset: *mut FXmacBd = null_mut(); @@ -788,18 +737,12 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { for q in &p { bdindex = FXMAC_BD_TO_INDEX(txring, txbd as u64); + /* if (instance_p.lwipport.buffer.tx_pbufs_storage[bdindex as usize] != 0) { panic!("PBUFS not available"); - } - - /* Send the data from the pbuf to the interface, one pbuf at a - time. The size of the data in each pbuf is kept in the ->len - variable. */ - //FCacheDCacheFlushRange((uintptr)q->payload, (uintptr)q->len); - - fxmac_bd_set_address_tx(txbd as u64, q.as_ptr() as u64); - + }*/ + if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 { max_fr_size = FXMAC_MAX_FRAME_SIZE_JUMBO; @@ -808,10 +751,21 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { { max_fr_size = FXMAC_MAX_FRAME_SIZE; } + let pbufs_len = min(q.len(), max_fr_size as usize); + + let pbufs_virt = instance_p.lwipport.buffer.tx_pbufs_storage[bdindex as usize]; + let pbuf = unsafe { from_raw_parts_mut(pbufs_virt as *mut u8, pbufs_len) }; + pbuf.copy_from_slice(q); + crate::utils::FCacheDCacheFlushRange(pbufs_virt, pbufs_len as u64); + info!(">>>>>>>>> TX PKT {}@{:#x}", pbufs_len, pbufs_virt); + + //fxmac_bd_set_address_tx(txbd as u64, q.as_ptr() as u64); + send_len += pbufs_len as u32; let t_txbd = txbd as u64; if q.len() > max_fr_size as usize { + warn!("The packet: {} to be send is TOO LARGE", q.len()); // FXMAC_BD_SET_LENGTH: 设置BD的发送长度(以bytes为单位)。每次BD交给硬件时,都必须设置该长度 // FXMAC_BD_SET_LENGTH(txbd, max_fr_size & 0x3FFF); fxmac_bd_write(t_txbd, FXMAC_BD_STAT_OFFSET, @@ -822,7 +776,7 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { ((fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) | (q.len() as u32 & 0x3FFF))); } - instance_p.lwipport.buffer.tx_pbufs_storage[bdindex as usize] = q.as_ptr() as u64; + //instance_p.lwipport.buffer.tx_pbufs_storage[bdindex as usize] = q.as_ptr() as u64; // 增加该pbuf的引用计数。 //pbuf_ref(q); @@ -874,7 +828,7 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { let value = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32) | FXMAC_NWCTRL_STARTTX_MASK; write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, value); - 0 + send_len } /// 收包函数 @@ -918,8 +872,9 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { }; let bdindex: u32 = FXMAC_BD_TO_INDEX(rxring, curbdptr as u64); - let mbuf = unsafe { from_raw_parts_mut(instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize] as *mut u8, rx_bytes as usize) }; - info!("RX PKT {} <<<<<<<<<", rx_bytes); + let pbufs_virt = instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize]; + info!("RX PKT {}@{:#x} <<<<<<<<<", rx_bytes, pbufs_virt); + let mbuf = unsafe { from_raw_parts_mut(pbufs_virt as *mut u8, rx_bytes as usize) }; // Copy mbuf into a new Vec recv_packets.push(mbuf.to_vec()); @@ -1167,20 +1122,18 @@ pub fn FXmacProcessSentBds(instance_p: &mut FXmac) } crate::utils::DSB(); - let pbuf = instance_p.lwipport.buffer.tx_pbufs_storage[bdindex]; - if pbuf != 0 { + //let pbuf = instance_p.lwipport.buffer.tx_pbufs_storage[bdindex]; + /* if pbuf != 0 { // pbuf_free(p); - // ?todo xiaoluoyuan@163.com let pages = (FXMAC_MAX_FRAME_SIZE as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; // Deallocate DMA memory by virtual address crate::utils::dma_free_coherent(pbuf as usize, pages); - } + } */ - instance_p.lwipport.buffer.tx_pbufs_storage[bdindex] = 0; + //instance_p.lwipport.buffer.tx_pbufs_storage[bdindex] = 0; let b = curbdpntr; curbdpntr = FXMAC_BD_RING_NEXT(txring, curbdpntr); - assert!(curbdpntr as usize != b as usize); n_pbufs_freed -= 1; diff --git a/src/utils.rs b/src/utils.rs index d9fb74462..de8c7b5fc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -71,6 +71,12 @@ pub fn MTCPDC_CIVAC(adr: u64) { } } +pub fn MTCPDC_CVAC(adr: u64) { + unsafe { + asm!("dc CVAC, {}", in(reg) adr); + } +} + /// CACHE of PhytiumPi pub const CACHE_LINE_ADDR_MASK: u64 = 0x3F; pub const CACHE_LINE: u64 = 64; @@ -99,6 +105,28 @@ pub fn FCacheDCacheInvalidateRange(mut adr: u64, len: u64) MTCPSR(currmask); } +/// Flush Data cache +/// DC CVAC, Virtual address to use. No alignment restrictions apply to vaddr +/// adr: 64bit start address of the range to be flush. +pub fn FCacheDCacheFlushRange(mut adr: u64, len: u64) +{ + let end: u64 = adr + len; + adr = adr & (!CACHE_LINE_ADDR_MASK); + let currmask: u32 = MFCPSR(); + MTCPSR(currmask | IRQ_FIQ_MASK); + if len != 0 + { + while (adr < end) + { + MTCPDC_CVAC(adr); /* Clean data cache by address to Point of Coherency */ + adr += CACHE_LINE; + } + } + /* Wait for Clean to complete */ + DSB(); + MTCPSR(currmask); +} + use aarch64_cpu::registers::{CNTVCT_EL0, CNTFRQ_EL0, Readable}; use alloc::boxed::Box; From a8299687092ee6834d76b9a999e33cf32d742c7d Mon Sep 17 00:00:00 2001 From: elliott10 Date: Tue, 21 Jan 2025 21:08:58 +0800 Subject: [PATCH 008/132] supports the function of receiving and sending network packets --- src/fxmac.rs | 1 + src/fxmac_dma.rs | 232 ++++++++++++++++++++++++++++++----------------- src/utils.rs | 11 ++- 3 files changed, 161 insertions(+), 83 deletions(-) diff --git a/src/fxmac.rs b/src/fxmac.rs index c58921661..5274450c0 100644 --- a/src/fxmac.rs +++ b/src/fxmac.rs @@ -270,6 +270,7 @@ pub fn xmac_init(hwaddr: &[u8; 6]) -> &'static mut FXmac { // i32 */ ///////// + msdelay(2000); //开始前等2s // ethernetif_start() // start mac diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index 4b4634af8..0cdef2945 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -8,6 +8,7 @@ use log::*; use crate::fxmac_const::*; use crate::fxmac_phy::*; use crate::fxmac::*; +use crate::utils::*; // fxmac_lwip_port.h pub const FXMAX_RX_BDSPACE_LENGTH: usize = 0x20000; /* default set 128KB*/ @@ -69,7 +70,23 @@ pub const ULONG64_LO_MASK: u64 = !ULONG64_HI_MASK; pub const BD_ALIGNMENT: u64 = FXMAC_DMABD_MINIMUM_ALIGNMENT*2; // 128 pub const FXMAC_DMABD_MINIMUM_ALIGNMENT: u64 = 64; pub const FXMAC_BD_NUM_WORDS: usize = 4; + +/// DMA address width 64 bits: +/// word 0: 32 bit address of Data Buffer +/// word 1: control / status, 32-bit +/// word 2: upper 32 bit address of Data Buffer +/// word 3: unused +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct macb_dma_desc { + pub addr: u32, + pub ctrl: u32, + pub addrh: u32, + pub resvd: u32, +} + pub type FXmacBd = [u32; FXMAC_BD_NUM_WORDS]; + // sizeof(uintptr)=8 pub type uintptr = u64; @@ -130,8 +147,8 @@ impl Default for FXmacBdRing { pub struct FXmacNetifBuffer { // 作为FXmacBdRing的基地址,并设置成一串多个BD - pub rx_bdspace: Box<[u8; FXMAX_RX_BDSPACE_LENGTH]>, // aligned(256); 接收bd 缓冲区 - pub tx_bdspace: Box<[u8; FXMAX_RX_BDSPACE_LENGTH]>, // aligned(256); 发送bd 缓冲区 + pub rx_bdspace: usize, // [u8; FXMAX_RX_BDSPACE_LENGTH], aligned(256); 接收bd 缓冲区 + pub tx_bdspace: usize, // FXMAX_TX_BDSPACE_LENGTH, aligned(256); 发送bd 缓冲区 // 保存收发数据包的内存基地址,收发数据包内存需要申请alloc pub rx_pbufs_storage: [uintptr; FXMAX_RX_PBUFS_LENGTH], @@ -140,9 +157,18 @@ pub struct FXmacNetifBuffer impl Default for FXmacNetifBuffer { fn default() -> Self { + + let alloc_pages = (FXMAX_RX_BDSPACE_LENGTH + (PAGE_SIZE - 1)) / PAGE_SIZE; + let (mut rx_vaddr, mut rx_dma) = crate::utils::dma_alloc_coherent(alloc_pages); + + let alloc_pages = (FXMAX_TX_BDSPACE_LENGTH + (PAGE_SIZE - 1)) / PAGE_SIZE; + let (mut tx_vaddr, mut tx_dma) = crate::utils::dma_alloc_coherent(alloc_pages); + + //let rx_buf = unsafe { from_raw_parts_mut(vaddr as *mut u8, FXMAX_RX_BDSPACE_LENGTH) }; + Self { - rx_bdspace: Box::new([0; FXMAX_RX_BDSPACE_LENGTH]), - tx_bdspace: Box::new([0; FXMAX_RX_BDSPACE_LENGTH]), + rx_bdspace: rx_vaddr, + tx_bdspace: tx_vaddr, rx_pbufs_storage: [0; FXMAX_RX_PBUFS_LENGTH], tx_pbufs_storage: [0; FXMAX_TX_PBUFS_LENGTH], } @@ -160,12 +186,14 @@ pub struct FXmacLwipPort } pub fn fxmac_bd_read(bd_ptr: u64, offset: u32) -> u32 { - read_reg((bd_ptr + offset as u64) as *const u32) + trace!("fxmac_bd_read at {:#x}", bd_ptr + offset as u64); + read_reg((virt_to_phys(bd_ptr as usize) + offset as usize) as *const u32) } pub fn fxmac_bd_write(bd_ptr: u64, offset: u32, data: u32) { + debug!("fxmac_bd_write {:#x} to {:#x}", data, bd_ptr + offset as u64); // uintptr: u64 - write_reg((bd_ptr + offset as u64) as *mut u32, data); + write_reg((virt_to_phys(bd_ptr as usize) + offset as usize) as *mut u32, data); } /// FXmacBdSetRxWrap @@ -175,6 +203,7 @@ fn FXmacBdSetRxWrap(mut bdptr: u64) { let temp_ptr = bdptr as *mut u32; if !temp_ptr.is_null() { let mut data_value_rx: u32 = unsafe{*temp_ptr}; + info!("RX WRAP of BD @ {:#x} set {:#x} | FXMAC_RXBUF_WRAP_MASK", bdptr, data_value_rx); data_value_rx |= FXMAC_RXBUF_WRAP_MASK; unsafe { temp_ptr.write_volatile(data_value_rx); @@ -189,6 +218,7 @@ fn FXmacBdSetTxWrap(mut bdptr: u64) { let temp_ptr = bdptr as *mut u32; if !temp_ptr.is_null() { let mut data_value_tx: u32 = unsafe{*temp_ptr}; + info!("TX WRAP of BD @ {:#x} set {:#x} | TXBUF_WRAP", bdptr, data_value_tx); data_value_tx |= FXMAC_TXBUF_WRAP_MASK; unsafe { temp_ptr.write_volatile(data_value_tx); @@ -219,7 +249,7 @@ fn fxmac_bd_set_address_rx(bd_ptr: u64, addr: u64) { /// Set the BD's address field (word 0). fn fxmac_bd_set_address_tx(bd_ptr: u64, addr: u64) { - fxmac_bd_write((bd_ptr), FXMAC_BD_ADDR_OFFSET, (addr & ULONG64_LO_MASK) as u32); + fxmac_bd_write(bd_ptr, FXMAC_BD_ADDR_OFFSET, (addr & ULONG64_LO_MASK) as u32); fxmac_bd_write(bd_ptr, FXMAC_BD_ADDR_HI_OFFSET, ((addr & ULONG64_HI_MASK) >> 32) as u32); @@ -227,19 +257,22 @@ fn fxmac_bd_set_address_tx(bd_ptr: u64, addr: u64) { } /// 将bdptr参数向前移动任意数量的bd,绕到环的开头。 -fn FXMAC_RING_SEEKAHEAD(ring_ptr: &mut FXmacBdRing, mut bdptr: *mut FXmacBd, num_bd: u32) +fn FXMAC_RING_SEEKAHEAD(ring_ptr: &mut FXmacBdRing, bdptr: &mut (*mut FXmacBd), num_bd: u32) { + trace!("FXMAC_RING_SEEKAHEAD, bdptr={:#x}", *bdptr as u64); + // 第一个free BD // bdptr = free_head - - let mut addr: u64 = bdptr as u64; + let mut addr: u64 = *bdptr as u64; addr += (ring_ptr.separation * num_bd) as u64; - if (addr > ring_ptr.high_bd_addr) || (bdptr as u64 > addr) + if (addr > ring_ptr.high_bd_addr) || (*bdptr as u64 > addr) { addr -= ring_ptr.length as u64; } - bdptr = addr as *mut FXmacBd; + *bdptr = addr as *mut FXmacBd; + + trace!("FXMAC_RING_SEEKAHEAD, bdptr: {:#x}, addr: {:#x}", *bdptr as u64, addr); } @@ -261,7 +294,7 @@ pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = crate::utils::dma_alloc_coherent(alloc_rx_buffer_pages); let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; - let rxbd: *mut FXmacBd = null_mut(); + let mut rxbd: *mut FXmacBd = null_mut(); //let my_speed: Box = Box::new(88); //rxbd = Box::into_raw(my_speed); // OR @@ -269,7 +302,7 @@ pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { //rxbd = &mut my_speed; // 在BD list中预留待设置的BD - status = FXmacBdRingAlloc(rxringptr, 1, rxbd); + status = FXmacBdRingAlloc(rxringptr, 1, &mut rxbd); assert!(!rxbd.is_null()); if (status != 0) { @@ -326,10 +359,13 @@ pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { let bdindex = FXMAC_BD_TO_INDEX(txringptr, txbd as u64); assert!(index == bdindex as usize); */ + // From index to BD let txbd = (txringptr.base_bd_addr + (index as u64 * txringptr.separation as u64)) as *mut FXmacBd; fxmac_bd_set_address_tx(txbd as u64, tx_mbufs_dma as u64); + //debug!("TX DMA DESC {}: {:#010x?}", index, unsafe{*(txbd as *const macb_dma_desc)}); + //curbdpntr = FXMAC_BD_RING_NEXT(txring, curbdpntr); crate::utils::DSB(); } @@ -344,8 +380,8 @@ pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; info!("FXmacInitDma, rxringptr: {:p}", rxringptr); info!("FXmacInitDma, txringptr: {:p}", txringptr); - info!("FXmacInitDma, rx_bdspace: {:p}", &instance_p.lwipport.buffer.rx_bdspace); - info!("FXmacInitDma, tx_bdspace: {:p}", &instance_p.lwipport.buffer.tx_bdspace); + info!("FXmacInitDma, rx_bdspace: {:#x}", &instance_p.lwipport.buffer.rx_bdspace); + info!("FXmacInitDma, tx_bdspace: {:#x}", &instance_p.lwipport.buffer.tx_bdspace); // Setup RxBD space. // 对BD域清零 @@ -356,24 +392,28 @@ pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 // Create the RxBD ring, bdspace地址必须对齐128 // 创建收包的环形缓冲区 - let mut status: u32 = FXmacBdRingCreate(rxringptr, &instance_p.lwipport.buffer.rx_bdspace as *const _ as u64, &instance_p.lwipport.buffer.rx_bdspace as *const _ as u64, BD_ALIGNMENT, FXMAX_RX_PBUFS_LENGTH as u32); + let mut status: u32 = FXmacBdRingCreate(rxringptr, instance_p.lwipport.buffer.rx_bdspace as u64, instance_p.lwipport.buffer.rx_bdspace as u64, BD_ALIGNMENT, FXMAX_RX_PBUFS_LENGTH as u32); // 将给定的BD, 克隆到list中的每个BD上 - status = FXmacBdRingClone(rxringptr, &mut bdtemplate, FXMAC_RECV); - - - let mut bdtemplate: [u32; FXMAC_BD_NUM_WORDS] = [0; FXMAC_BD_NUM_WORDS]; - - // FXMAC_BD_SET_STATUS() + status = FXmacBdRingClone(rxringptr, &bdtemplate, FXMAC_RECV); + + /*let bdtem = macb_dma_desc { + addr: 0, + ctrl: FXMAC_TXBUF_USED_MASK, + addrh: 0, + resvd: 0, + };*/ + bdtemplate.fill(0); + // FXMAC_BD_SET_STATUS(), 注:这里先设置TXBUF_USED位,对于发包很重要! // Set the BD's Status field (word 1). fxmac_bd_write((&bdtemplate as *const _ as u64), FXMAC_BD_STAT_OFFSET, - fxmac_bd_read((&bdtemplate as *const _ as u64), FXMAC_BD_STAT_OFFSET) | (FXMAC_TXBUF_USED_MASK)); + fxmac_bd_read((&mut bdtemplate as *mut _ as u64), FXMAC_BD_STAT_OFFSET) | (FXMAC_TXBUF_USED_MASK)); /* Create the TxBD ring */ - status = FXmacBdRingCreate(txringptr, &instance_p.lwipport.buffer.tx_bdspace as *const _ as u64, &instance_p.lwipport.buffer.tx_bdspace as *const _ as u64, BD_ALIGNMENT, FXMAX_TX_PBUFS_LENGTH as u32); + status = FXmacBdRingCreate(txringptr, instance_p.lwipport.buffer.tx_bdspace as u64, instance_p.lwipport.buffer.tx_bdspace as u64, BD_ALIGNMENT, FXMAX_TX_PBUFS_LENGTH as u32); /* We reuse the bd template, as the same one will work for both rx and tx. */ - status = FXmacBdRingClone(txringptr, &mut bdtemplate, FXMAC_SEND); + status = FXmacBdRingClone(txringptr, &bdtemplate, FXMAC_SEND); // 创建收发网络包的DMA内存 FXmacAllocDmaPbufs(instance_p); @@ -440,6 +480,7 @@ pub fn FXmacBdRingCreate(ring_ptr: &mut FXmacBdRing, phys_addr: u64, virt_addr: bd_virt_addr += ring_ptr.separation as u64; } + info!("FXmacBdRingCreate BDs count={}, separation={}, {:#x}~{:#x}", bd_count, ring_ptr.separation, virt_addr, bd_virt_addr); // Setup and initialize pointers and counters // BD list中第一个的虚拟地址 @@ -469,18 +510,19 @@ pub fn FXmacBdRingCreate(ring_ptr: &mut FXmacBdRing, phys_addr: u64, virt_addr: } /// 将给定的BD, 克隆到list中的每个BD上 -pub fn FXmacBdRingClone(ring_ptr: &mut FXmacBdRing, src_bd_ptr: &mut FXmacBd, direction: u32) -> u32 +pub fn FXmacBdRingClone(ring_ptr: &mut FXmacBdRing, src_bd_ptr: & FXmacBd, direction: u32) -> u32 { // Can't do this function with some of the BDs in use assert!(ring_ptr.free_cnt == ring_ptr.all_cnt); let mut cur_bd = ring_ptr.base_bd_addr; for i in 0..ring_ptr.all_cnt { + trace!("FXmacBdRingClone, copy current bd @ {:#x}", cur_bd); // 将所有BD逐个复制成bdtemplate - //let cur_bd_slice = unsafe { from_raw_parts_mut(cur_bd as *mut FXmacBd, 1) }; - //cur_bd_slice.copy_from_slice(src_bd_ptr); - unsafe{ *(cur_bd as *mut FXmacBd) }.copy_from_slice(src_bd_ptr); - + let cur_bd_slice = unsafe { from_raw_parts_mut(cur_bd as *mut FXmacBd, 1) }; + cur_bd_slice[0].copy_from_slice(src_bd_ptr); + crate::utils::DSB(); + cur_bd += ring_ptr.separation as u64; } cur_bd -= ring_ptr.separation as u64; @@ -497,7 +539,7 @@ pub fn FXmacBdRingClone(ring_ptr: &mut FXmacBdRing, src_bd_ptr: &mut FXmacBd, di /// 在BD list中预留待设置的BD -pub fn FXmacBdRingAlloc(ring_ptr: &mut FXmacBdRing, num_bd: u32, mut bd_set_ptr: *mut FXmacBd) -> u32 { +pub fn FXmacBdRingAlloc(ring_ptr: &mut FXmacBdRing, num_bd: u32, bd_set_ptr: &mut(*mut FXmacBd)) -> u32 { /* let num_bd = 1; let bd_set_ptr = &rxbd; @@ -508,10 +550,14 @@ let bd_set_ptr = &rxbd; 4 } else { // 获取待设置的BD,并向前移动free BD - bd_set_ptr = ring_ptr.free_head as *mut FXmacBd; + *bd_set_ptr = ring_ptr.free_head as *mut FXmacBd; let b = ring_ptr.free_head; - FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr.free_head, num_bd); + let mut free_head_t = ring_ptr.free_head; + FXMAC_RING_SEEKAHEAD(ring_ptr, &mut free_head_t, num_bd); + ring_ptr.free_head = free_head_t; + + debug!("free_head {:#x} seekahead to {:#x}", b as usize, ring_ptr.free_head as usize); assert!(b as usize != ring_ptr.free_head as usize); ring_ptr.free_cnt -= num_bd; @@ -531,7 +577,9 @@ pub fn FXmacBdRingToHw(ring_ptr: &mut FXmacBdRing, num_bd: u32, bd_set_ptr: *mut cur_bd_ptr = FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr); } - FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr.pre_head, num_bd); + let mut pre_head_t = ring_ptr.pre_head; + FXMAC_RING_SEEKAHEAD(ring_ptr, &mut pre_head_t, num_bd); + ring_ptr.pre_head = pre_head_t; ring_ptr.pre_cnt -= num_bd; ring_ptr.hw_tail = cur_bd_ptr; @@ -540,7 +588,7 @@ pub fn FXmacBdRingToHw(ring_ptr: &mut FXmacBdRing, num_bd: u32, bd_set_ptr: *mut 0 } -pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_set_ptr: *mut FXmacBd) -> u32 { +pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_ptr: &mut (*mut FXmacBd)) -> u32 { let mut cur_bd_ptr: *mut FXmacBd = ring_ptr.hw_head; let mut status: u32 = 0; @@ -551,7 +599,7 @@ pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_s if ring_ptr.hw_cnt == 0 { warn!("No BDs in RX work group, there's nothing to search"); - bd_set_ptr = null_mut(); + *bd_set_ptr = null_mut(); status = 0; }else{ @@ -567,10 +615,11 @@ pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_s bd_str = fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_STAT_OFFSET); // FXMAC_BD_IS_RX_NEW, Determine the new bit of the receive BD - let bd_is_rx_new = (fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_ADDR_OFFSET) & FXMAC_RXBUF_NEW_MASK) != 0; - if !bd_is_rx_new == true { + let bd_rx_new = fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_ADDR_OFFSET) & FXMAC_RXBUF_NEW_MASK; + if bd_rx_new == 0 { break; } + debug!("FXMAC_RXBUF_NEW_MASK got {:#x}", bd_rx_new); bd_count += 1; @@ -598,16 +647,21 @@ pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_s * parameters, update pointers and counters, return success */ if bd_count > 0 { - bd_set_ptr = ring_ptr.hw_head; + *bd_set_ptr = ring_ptr.hw_head; ring_ptr.hw_cnt -= bd_count; ring_ptr.post_cnt += bd_count; - FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr.hw_head, bd_count); + + let mut hw_head_t = ring_ptr.hw_head; + FXMAC_RING_SEEKAHEAD(ring_ptr, &mut hw_head_t, bd_count); + ring_ptr.hw_head = hw_head_t; info!("FXmacBdRingFromHwRx, Found BD={}", bd_count); status = bd_count; } else { - bd_set_ptr = null_mut(); + //warn!("FXmacBdRingFromHwRx, no found BD={}", bd_count); + + *bd_set_ptr = null_mut(); status = 0; } } @@ -620,7 +674,7 @@ pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_s /// BDs may be examined to determine the outcome of the DMA transaction(s). /// Once the BDs have been examined, the user must call FXmacBdRingFree() /// in the same order which they were retrieved here. -pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_set_ptr: *mut FXmacBd) -> u32 { +pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_ptr: &mut(*mut FXmacBd)) -> u32 { let mut bd_str: u32 = 0; let mut bd_count: u32 = 0; let mut bd_partial_count: u32 = 0; @@ -633,7 +687,7 @@ pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_s if ring_ptr.hw_cnt == 0 { warn!("No BDs in TX work group, then there's nothing to search"); - bd_set_ptr = null_mut(); + *bd_set_ptr = null_mut(); status = 0; } else { @@ -651,8 +705,11 @@ pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_s // Read the status bd_str = fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_STAT_OFFSET); + // 当DMA硬件对该BD已经成功处理完成时,TXBUF_USED位则处于被硬件置位; + // 当软件去主动清除TX_USED位时,使能了为DMA硬件准备好的buffer if (bd_str & FXMAC_TXBUF_USED_MASK) != 0 { + info!("FXmacBdRingFromHwTx, found a hardware USED TXBUF"); bd_count += 1; bd_partial_count += 1; } @@ -670,23 +727,26 @@ pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, mut bd_s cur_bd_ptr = FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr); } - /* Subtract off any partial packet BDs found */ + info!("FXmacBdRingFromHwTx, Subtract off any partial packet BDs found"); bd_count -= bd_partial_count; /* If bd_count is non-zero then BDs were found to return. Set return * parameters, update pointers and counters, return success */ if bd_count > 0 { - bd_set_ptr = ring_ptr.hw_head; + *bd_set_ptr = ring_ptr.hw_head; ring_ptr.hw_cnt -= bd_count; ring_ptr.post_cnt += bd_count; - FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr.hw_head, bd_count); + + let mut hw_head_t = ring_ptr.hw_head; + FXMAC_RING_SEEKAHEAD(ring_ptr, &mut hw_head_t, bd_count); + ring_ptr.hw_head = hw_head_t; info!("FXmacBdRingFromHwTx, Found BD={}", bd_count); status = bd_count; } else { - bd_set_ptr = null_mut(); + *bd_set_ptr = null_mut(); status = 0; } } @@ -701,9 +761,8 @@ pub fn FXmacLwipPortTx(instance: &mut FXmac, pbuf: Vec>) -> i32 // check if space is available to send let freecnt = (instance.tx_bd_queue.bdring).free_cnt; - if freecnt <= 5 { + if freecnt <= 124 { //5 info!("TX freecnt={}, let's process sent BDs", freecnt); - //let txring = &mut (instance.tx_bd_queue.bdring); FXmacProcessSentBds(instance); } @@ -723,22 +782,22 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { let mut send_len: u32 = 0; let mut last_txbd: *mut FXmacBd = null_mut(); - let txbdset: *mut FXmacBd = null_mut(); + let mut txbdset: *mut FXmacBd = null_mut(); let txring: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; // Count the number of pbufs: p let n_pbufs: u32 = p.len() as u32; + debug!("Sending packets: {}", n_pbufs); /* obtain as many BD's */ - status = FXmacBdRingAlloc(txring, n_pbufs, txbdset); + status = FXmacBdRingAlloc(txring, n_pbufs, &mut txbdset); + assert!(!txbdset.is_null()); - debug!("FXmacSgsend, txbdset is null ? {}", txbdset.is_null()); let mut txbd: *mut FXmacBd = txbdset; for q in &p { bdindex = FXMAC_BD_TO_INDEX(txring, txbd as u64); - /* - if (instance_p.lwipport.buffer.tx_pbufs_storage[bdindex as usize] != 0) + /* if (instance_p.lwipport.buffer.tx_pbufs_storage[bdindex as usize] != 0) { panic!("PBUFS not available"); }*/ @@ -757,30 +816,29 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { let pbuf = unsafe { from_raw_parts_mut(pbufs_virt as *mut u8, pbufs_len) }; pbuf.copy_from_slice(q); crate::utils::FCacheDCacheFlushRange(pbufs_virt, pbufs_len as u64); - info!(">>>>>>>>> TX PKT {}@{:#x}", pbufs_len, pbufs_virt); + warn!(">>>>>>>>> TX PKT {} @{:#x} - {}", pbufs_len, pbufs_virt, bdindex); - //fxmac_bd_set_address_tx(txbd as u64, q.as_ptr() as u64); + debug!("pbuf: {:x?}", pbuf); + + //fxmac_bd_set_address_tx(txbd as u64, (pbufs_virt & 0xffff_ffff) as u64); send_len += pbufs_len as u32; - let t_txbd = txbd as u64; if q.len() > max_fr_size as usize { warn!("The packet: {} to be send is TOO LARGE", q.len()); // FXMAC_BD_SET_LENGTH: 设置BD的发送长度(以bytes为单位)。每次BD交给硬件时,都必须设置该长度 // FXMAC_BD_SET_LENGTH(txbd, max_fr_size & 0x3FFF); - fxmac_bd_write(t_txbd, FXMAC_BD_STAT_OFFSET, - ((fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) | (max_fr_size & 0x3FFF))); + fxmac_bd_write(txbd as u64, FXMAC_BD_STAT_OFFSET, + ((fxmac_bd_read(txbd as u64, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) | (max_fr_size & 0x3FFF))); } else { - // FXMAC_BD_SET_LENGTH(txbd, q->len & 0x3FFF); - fxmac_bd_write(t_txbd, FXMAC_BD_STAT_OFFSET, - ((fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) | (q.len() as u32 & 0x3FFF))); + fxmac_bd_write(txbd as u64, FXMAC_BD_STAT_OFFSET, + ((fxmac_bd_read(txbd as u64, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) | (q.len() as u32 & 0x3FFF))); } //instance_p.lwipport.buffer.tx_pbufs_storage[bdindex as usize] = q.as_ptr() as u64; // 增加该pbuf的引用计数。 //pbuf_ref(q); - last_txbd = txbd; // 告诉DMA当前包不是以该BD结束 @@ -797,9 +855,11 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { fxmac_bd_write(t_txbd, FXMAC_BD_STAT_OFFSET, fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) | FXMAC_TXBUF_LAST_MASK ); + ///////// + /* The bdindex always points to the first free_head in tx_bdrings */ if (instance_p.config.caps & FXMAC_CAPS_TAILPTR) != 0 - { + { bdindex = FXMAC_BD_TO_INDEX(txring, txbd as u64); } @@ -824,6 +884,8 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { write_reg((instance_p.config.base_address + FXMAC_TAIL_QUEUE(0)) as *mut u32, (1 << 31) | bdindex); } + debug!("TX DMA DESC: {:#010x?}", unsafe{*(txbdset as *const macb_dma_desc)}); + // Start transmit let value = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32) | FXMAC_NWCTRL_STARTTX_MASK; write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, value); @@ -833,7 +895,7 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { /// 收包函数 pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { - info!("RX receive packets"); + trace!("RX receive packets"); let mut recv_packets = Vec::new(); let mut rxbdset: *mut FXmacBd = null_mut(); @@ -846,12 +908,13 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { loop { // Returns a set of BD(s) that have been processed by hardware. - let bd_processed: u32 = FXmacBdRingFromHwRx(&mut instance_p.rx_bd_queue.bdring, FXMAX_RX_PBUFS_LENGTH, rxbdset); - assert!(!rxbdset.is_null()); + let bd_processed: u32 = FXmacBdRingFromHwRx(&mut instance_p.rx_bd_queue.bdring, FXMAX_RX_PBUFS_LENGTH, &mut rxbdset); if bd_processed <= 0 { + // 没有待收的网络包了 break; } + assert!(!rxbdset.is_null()); let mut curbdptr: *mut FXmacBd = rxbdset; for k in 0..bd_processed { @@ -866,16 +929,18 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { fxmac_bd_read(curbdptr as u64, FXMAC_BD_STAT_OFFSET) & 0x00003FFF } else { + debug!("FXMAC_RXBUF_LEN_MASK={:#x}", FXMAC_RXBUF_LEN_MASK); //FXMAC_BD_GET_LENGTH(curbdptr) fxmac_bd_read(curbdptr as u64, FXMAC_BD_STAT_OFFSET) & FXMAC_RXBUF_LEN_MASK - }; let bdindex: u32 = FXMAC_BD_TO_INDEX(rxring, curbdptr as u64); let pbufs_virt = instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize]; - info!("RX PKT {}@{:#x} <<<<<<<<<", rx_bytes, pbufs_virt); + info!("RX PKT {} @{:#x} <<<<<<<<< - {}", rx_bytes, pbufs_virt, bdindex); let mbuf = unsafe { from_raw_parts_mut(pbufs_virt as *mut u8, rx_bytes as usize) }; + debug!("pbuf: {:x?}", mbuf); + // Copy mbuf into a new Vec recv_packets.push(mbuf.to_vec()); @@ -941,7 +1006,7 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { }; let alloc_rx_buffer_pages: usize = (max_frame_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; - status = FXmacBdRingAlloc(rxring, 1, rxbd); + status = FXmacBdRingAlloc(rxring, 1, &mut rxbd); assert!(!rxbd.is_null()); status = FXmacBdRingToHw(rxring, 1, rxbd); @@ -1004,7 +1069,10 @@ pub fn FXmacBdRingFree(ring_ptr: &mut FXmacBdRing, num_bd: u32) -> u32 { /* Update pointers and counters */ ring_ptr.free_cnt += num_bd; ring_ptr.post_cnt -= num_bd; - FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr.post_head, num_bd); + + let mut post_head_t = ring_ptr.post_head; + FXMAC_RING_SEEKAHEAD(ring_ptr, &mut post_head_t, num_bd); + ring_ptr.post_head = post_head_t; 0 } @@ -1018,8 +1086,8 @@ pub fn ResetDma(instance_p: &mut FXmac) let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; - FXmacBdringPtrReset(txringptr, &instance_p.lwipport.buffer.tx_bdspace as *const _ as *mut FXmacBd); - FXmacBdringPtrReset(rxringptr, &instance_p.lwipport.buffer.rx_bdspace as *const _ as *mut FXmacBd); + FXmacBdringPtrReset(txringptr, instance_p.lwipport.buffer.tx_bdspace as *mut FXmacBd); + FXmacBdringPtrReset(rxringptr, instance_p.lwipport.buffer.rx_bdspace as *mut FXmacBd); FXmacSetQueuePtr(instance_p.tx_bd_queue.bdring.phys_base_addr, 0, FXMAC_SEND); FXmacSetQueuePtr(instance_p.rx_bd_queue.bdring.phys_base_addr, 0, FXMAC_RECV); @@ -1069,9 +1137,9 @@ fn CleanDmaTxdescs(instance_p: &mut FXmac) fxmac_bd_write((&mut bdtemplate as *mut _ as u64), FXMAC_BD_STAT_OFFSET, fxmac_bd_read((&mut bdtemplate as *mut _ as u64), FXMAC_BD_STAT_OFFSET) | (FXMAC_TXBUF_USED_MASK)); - let tx_bdspace_ptr = &mut instance_p.lwipport.buffer.tx_bdspace as *mut _ as u64; + let tx_bdspace_ptr = instance_p.lwipport.buffer.tx_bdspace as u64; FXmacBdRingCreate(txringptr, tx_bdspace_ptr, tx_bdspace_ptr, BD_ALIGNMENT, FXMAX_TX_BDSPACE_LENGTH as u32); - FXmacBdRingClone(txringptr, &mut bdtemplate, FXMAC_SEND); + FXmacBdRingClone(txringptr, &bdtemplate, FXMAC_SEND); } fn FreeOnlyTxPbufs(instance_p: &mut FXmac) @@ -1093,12 +1161,12 @@ fn FreeOnlyTxPbufs(instance_p: &mut FXmac) pub fn FXmacProcessSentBds(instance_p: &mut FXmac) { let txring: &mut FXmacBdRing = &mut (instance_p.tx_bd_queue.bdring); - let txbdset: *mut FXmacBd = null_mut(); + let mut txbdset: *mut FXmacBd = null_mut(); loop { /* obtain processed BD's */ - let n_bds: u32 = FXmacBdRingFromHwTx(txring, FXMAX_TX_PBUFS_LENGTH, txbdset); + let n_bds: u32 = FXmacBdRingFromHwTx(txring, FXMAX_TX_PBUFS_LENGTH, &mut txbdset); if n_bds == 0 { - error!("FXmacProcessSentBds have not found BD"); + warn!("FXmacProcessSentBds have not found BD"); return; } /* free the processed BD's */ @@ -1107,6 +1175,8 @@ pub fn FXmacProcessSentBds(instance_p: &mut FXmac) while n_pbufs_freed > 0 { let bdindex = FXMAC_BD_TO_INDEX(txring, curbdpntr as u64) as usize; + info!("FXmacProcessSentBds - {}: {:#010x?}", bdindex, unsafe{*(curbdpntr as *const macb_dma_desc)}); + let mut v = 0; if bdindex == (FXMAX_TX_PBUFS_LENGTH - 1) { v = 0xC0000000; /* Word 1 ,used/Wrap – marks last descriptor in transmit buffer descriptor list.*/ @@ -1117,8 +1187,8 @@ pub fn FXmacProcessSentBds(instance_p: &mut FXmac) let mut temp = curbdpntr as *mut u32; // Word 0 unsafe{ - temp.write_volatile(0); - temp.add(1).write_volatile(v); + //temp.write_volatile(0); // 这里不再对dma buffer地址清0 + temp.add(1).write_volatile(v); // 设置dma desc的ctrl } crate::utils::DSB(); diff --git a/src/utils.rs b/src/utils.rs index de8c7b5fc..37900ff0a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -170,11 +170,17 @@ pub fn usdelay(us: u64) { pub fn msdelay(ms: u64) { usdelay(ms * 1000); } +#[linkage = "weak"] +#[unsafe(export_name = "virt_to_phys_fxmac")] +pub fn virt_to_phys(addr: usize) -> usize { + debug!("fxmac: virt_to_phys_fxmac {:#x}", addr); + addr +} #[linkage = "weak"] #[unsafe(export_name = "phys_to_virt_fxmac")] pub fn phys_to_virt(addr: usize) -> usize { - debug!("fxmac: phys_to_virt {:#x}", addr); + debug!("fxmac: phys_to_virt_fxmac {:#x}", addr); addr } @@ -214,7 +220,8 @@ pub fn dma_free_coherent(vaddr: usize, pages: usize) { #[linkage = "weak"] #[unsafe(export_name = "dma_request_irq_fxmac")] pub fn dma_request_irq(irq: usize, handler: fn(u64)) { - unimplemented!() + warn!("dma_request_irq_fxmac unimplemented"); + //unimplemented!() } // 路由中断到指定的cpu,或所有的cpu From c8dcdf4086ce7592fc4dd3450ea5f86be9d8fdd8 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Wed, 22 Jan 2025 19:18:13 +0800 Subject: [PATCH 009/132] Adjust rx/tx packets ring recycling --- src/fxmac_dma.rs | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index 0cdef2945..57ffa314c 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -11,12 +11,11 @@ use crate::fxmac::*; use crate::utils::*; // fxmac_lwip_port.h -pub const FXMAX_RX_BDSPACE_LENGTH: usize = 0x20000; /* default set 128KB*/ -pub const FXMAX_TX_BDSPACE_LENGTH: usize = 0x20000; /* default set 128KB*/ +pub const FXMAX_RX_BDSPACE_LENGTH: usize = FXMAX_RX_PBUFS_LENGTH * 64; // 0x20000, default set 128KB +pub const FXMAX_TX_BDSPACE_LENGTH: usize = FXMAX_RX_PBUFS_LENGTH * 64; // 0x20000, default set 128KB - -pub const FXMAX_RX_PBUFS_LENGTH: usize = 128; -pub const FXMAX_TX_PBUFS_LENGTH: usize = 128; +pub const FXMAX_RX_PBUFS_LENGTH: usize = 8; // 128 +pub const FXMAX_TX_PBUFS_LENGTH: usize = 8; pub const FXMAX_MAX_HARDWARE_ADDRESS_LENGTH: usize =6; @@ -619,7 +618,7 @@ pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p if bd_rx_new == 0 { break; } - debug!("FXMAC_RXBUF_NEW_MASK got {:#x}", bd_rx_new); + //debug!("FXMAC_RXBUF_NEW_MASK got {:#x}", bd_rx_new); bd_count += 1; @@ -686,7 +685,7 @@ pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p /* If no BDs in work group, then there's nothing to search */ if ring_ptr.hw_cnt == 0 { - warn!("No BDs in TX work group, then there's nothing to search"); + debug!("No BDs in TX work group, then there's nothing to search"); *bd_set_ptr = null_mut(); status = 0; } else { @@ -761,7 +760,7 @@ pub fn FXmacLwipPortTx(instance: &mut FXmac, pbuf: Vec>) -> i32 // check if space is available to send let freecnt = (instance.tx_bd_queue.bdring).free_cnt; - if freecnt <= 124 { //5 + if freecnt <= 4 { //5 info!("TX freecnt={}, let's process sent BDs", freecnt); FXmacProcessSentBds(instance); } @@ -816,9 +815,9 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { let pbuf = unsafe { from_raw_parts_mut(pbufs_virt as *mut u8, pbufs_len) }; pbuf.copy_from_slice(q); crate::utils::FCacheDCacheFlushRange(pbufs_virt, pbufs_len as u64); - warn!(">>>>>>>>> TX PKT {} @{:#x} - {}", pbufs_len, pbufs_virt, bdindex); + warn!(">>>>>>>>> TX PKT {} @{:#x} - {}", pbufs_len, pbufs_virt, bdindex); - debug!("pbuf: {:x?}", pbuf); + debug!(">>>>>>>>> {:x?}", pbuf); //fxmac_bd_set_address_tx(txbd as u64, (pbufs_virt & 0xffff_ffff) as u64); send_len += pbufs_len as u32; @@ -1012,20 +1011,23 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { // 继续使用前面申请过的dma pbufs, 不再清理并重新申请 //let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = crate::utils::dma_alloc_coherent(alloc_rx_buffer_pages); - //crate::utils::FCacheDCacheInvalidateRange(rx_mbufs_vaddr as u64, max_frame_size as u64); let bdindex: u32 = FXMAC_BD_TO_INDEX(rxring, rxbd as u64); - let mut temp = rxbd as *mut u32; - let mut v = 0; + + let rx_macb_dma_desc = unsafe{(rxbd as *const macb_dma_desc).read_volatile()}; + debug!("SetupRxBds - {}: {:#010x?}", bdindex, rx_macb_dma_desc); + + let mut v = rx_macb_dma_desc.addr & (!0x7f); // 128位对齐? if bdindex == (FXMAX_RX_PBUFS_LENGTH - 1) as u32 { // Mask last descriptor in receive buffer list - v = 0x2; + v |= FXMAC_RXBUF_WRAP_MASK; } + + let mut temp = rxbd as *mut u32; unsafe { - temp.write_volatile(v); - // Clear word 1 in descriptor - temp.add(1).write_volatile(0); + temp.add(1).write_volatile(0); // clear rx ctl + temp.write_volatile(v); // set rx addr } crate::utils::DSB(); @@ -1175,7 +1177,7 @@ pub fn FXmacProcessSentBds(instance_p: &mut FXmac) while n_pbufs_freed > 0 { let bdindex = FXMAC_BD_TO_INDEX(txring, curbdpntr as u64) as usize; - info!("FXmacProcessSentBds - {}: {:#010x?}", bdindex, unsafe{*(curbdpntr as *const macb_dma_desc)}); + trace!("FXmacProcessSentBds - {}: {:#010x?}", bdindex, unsafe{*(curbdpntr as *const macb_dma_desc)}); let mut v = 0; if bdindex == (FXMAX_TX_PBUFS_LENGTH - 1) { From 28fd51f8d579ff3e562b082dd478c778209fd917 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Wed, 22 Jan 2025 20:30:39 +0800 Subject: [PATCH 010/132] Enable and test the interrupt function --- src/fxmac.rs | 9 +++------ src/fxmac_dma.rs | 30 +++++++++++++++++------------- src/fxmac_intr.rs | 24 ++++++++++++++---------- src/utils.rs | 2 +- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/fxmac.rs b/src/fxmac.rs index 5274450c0..a2118cb9f 100644 --- a/src/fxmac.rs +++ b/src/fxmac.rs @@ -212,14 +212,14 @@ pub fn xmac_init(hwaddr: &[u8; 6]) -> &'static mut FXmac { // i32 xmac.mask = FXMAC_IXR_LINKCHANGE_MASK | FXMAC_IXR_TX_ERR_MASK | FXMAC_IXR_RX_ERR_MASK - | FXMAC_IXR_RXCOMPL_MASK; // FXMAC_INTR_MASK + | FXMAC_IXR_RXCOMPL_MASK; // FXMAC_INTR_MASK // 这里打开收包中断,关闭发包中断 + if (xmac.config.caps & FXMAC_CAPS_TAILPTR) != 0 { FXmacSetOptions(&mut xmac, FXMAC_TAIL_PTR_OPTION, 0); xmac.mask &= !FXMAC_IXR_TXUSED_MASK; } // xmac.lwipport.feature = LWIP_PORT_MODE_MULTICAST_ADDRESS_FILITER; - FxmacFeatureSetOptions(xmac.lwipport.feature, &mut xmac); status = FXmacSetMacAddress(&xmac.lwipport.hwaddr, 0); @@ -269,9 +269,6 @@ pub fn xmac_init(hwaddr: &[u8; 6]) -> &'static mut FXmac { // i32 ethernetif_debug() */ - ///////// - msdelay(2000); //开始前等2s - // ethernetif_start() // start mac FXmacStart(&mut xmac); @@ -335,7 +332,7 @@ pub fn FXmacStart(instance_p: &mut FXmac) { info!("Enable TX and RX by Mask={:#x}", instance_p.mask); - // Enable TX and RX interrupt + // 使能网卡中断: Enable TX and RX interrupt //FXMAC_INT_ENABLE(instance_p, instance_p->mask); // Enable interrupts specified in 'Mask'. The corresponding interrupt for each bit set to 1 in 'Mask', will be enabled. write_reg( diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index 57ffa314c..a863762f4 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -14,8 +14,8 @@ use crate::utils::*; pub const FXMAX_RX_BDSPACE_LENGTH: usize = FXMAX_RX_PBUFS_LENGTH * 64; // 0x20000, default set 128KB pub const FXMAX_TX_BDSPACE_LENGTH: usize = FXMAX_RX_PBUFS_LENGTH * 64; // 0x20000, default set 128KB -pub const FXMAX_RX_PBUFS_LENGTH: usize = 8; // 128 -pub const FXMAX_TX_PBUFS_LENGTH: usize = 8; +pub const FXMAX_RX_PBUFS_LENGTH: usize = 128; // 128 +pub const FXMAX_TX_PBUFS_LENGTH: usize = 128; pub const FXMAX_MAX_HARDWARE_ADDRESS_LENGTH: usize =6; @@ -274,7 +274,6 @@ fn FXMAC_RING_SEEKAHEAD(ring_ptr: &mut FXmacBdRing, bdptr: &mut (*mut FXmacBd), trace!("FXMAC_RING_SEEKAHEAD, bdptr: {:#x}, addr: {:#x}", *bdptr as u64, addr); } - pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { // 为DMA构建环形缓冲区内存 @@ -935,10 +934,10 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { let bdindex: u32 = FXMAC_BD_TO_INDEX(rxring, curbdptr as u64); let pbufs_virt = instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize]; - info!("RX PKT {} @{:#x} <<<<<<<<< - {}", rx_bytes, pbufs_virt, bdindex); + debug!("RX PKT {} @{:#x} <<<<<<<<< - {}", rx_bytes, pbufs_virt, bdindex); let mbuf = unsafe { from_raw_parts_mut(pbufs_virt as *mut u8, rx_bytes as usize) }; - debug!("pbuf: {:x?}", mbuf); + debug!("pbuf: {:x?}", mbuf); // Copy mbuf into a new Vec recv_packets.push(mbuf.to_vec()); @@ -1016,7 +1015,7 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { let bdindex: u32 = FXMAC_BD_TO_INDEX(rxring, rxbd as u64); let rx_macb_dma_desc = unsafe{(rxbd as *const macb_dma_desc).read_volatile()}; - debug!("SetupRxBds - {}: {:#010x?}", bdindex, rx_macb_dma_desc); + trace!("SetupRxBds - {}: {:#010x?}", bdindex, rx_macb_dma_desc); let mut v = rx_macb_dma_desc.addr & (!0x7f); // 128位对齐? if bdindex == (FXMAX_RX_PBUFS_LENGTH - 1) as u32 { @@ -1042,16 +1041,18 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { pub fn ethernetif_input_to_recv_packets(instance_p: &mut FXmac) { if(instance_p.lwipport.recv_flg > 0) - { + { info!("ethernetif_input_to_recv_packets, fxmac_port->recv_flg={}", instance_p.lwipport.recv_flg); // 也许需要屏蔽中断的临界区来保护 instance_p.lwipport.recv_flg -= 1; + // 开中断 write_reg((instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, instance_p.mask); - FXmacRecvHandler(instance_p); - } + // 若需要中断处理函数中来接收包,可以这里解注释 + //FXmacRecvHandler(instance_p); + } { // move received packet into a new pbuf @@ -1130,7 +1131,7 @@ pub fn FXmacHandleTxErrors(instance_p: &mut FXmac) fn CleanDmaTxdescs(instance_p: &mut FXmac) { - info!("Clean DMA TX DESCs"); + warn!("Clean DMA TX DESCs"); let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; let mut bdtemplate: FXmacBd = [0; FXMAC_BD_NUM_WORDS]; @@ -1146,6 +1147,7 @@ fn CleanDmaTxdescs(instance_p: &mut FXmac) fn FreeOnlyTxPbufs(instance_p: &mut FXmac) { + warn!("Free all TX DMA pbuf"); for index in 0..FXMAX_TX_PBUFS_LENGTH { if (instance_p.lwipport.buffer.tx_pbufs_storage[index] != 0) @@ -1168,7 +1170,7 @@ pub fn FXmacProcessSentBds(instance_p: &mut FXmac) /* obtain processed BD's */ let n_bds: u32 = FXmacBdRingFromHwTx(txring, FXMAX_TX_PBUFS_LENGTH, &mut txbdset); if n_bds == 0 { - warn!("FXmacProcessSentBds have not found BD"); + info!("FXmacProcessSentBds have not found BD"); return; } /* free the processed BD's */ @@ -1177,8 +1179,7 @@ pub fn FXmacProcessSentBds(instance_p: &mut FXmac) while n_pbufs_freed > 0 { let bdindex = FXMAC_BD_TO_INDEX(txring, curbdpntr as u64) as usize; - trace!("FXmacProcessSentBds - {}: {:#010x?}", bdindex, unsafe{*(curbdpntr as *const macb_dma_desc)}); - + trace!("FXmacProcessSentBds - {}: {:#010x?}", bdindex, unsafe{*(curbdpntr as *const macb_dma_desc)}); let mut v = 0; if bdindex == (FXMAX_TX_PBUFS_LENGTH - 1) { v = 0xC0000000; /* Word 1 ,used/Wrap – marks last descriptor in transmit buffer descriptor list.*/ @@ -1218,6 +1219,7 @@ pub fn FXmacProcessSentBds(instance_p: &mut FXmac) pub fn FXmacSendHandler(instance: &mut FXmac) { + debug!("-> FXmacSendHandler"); //let txringptr: FXmacBdRing = instance.tx_bd_queue.bdring; let regval: u32 = read_reg((instance.config.base_address + FXMAC_TXSR_OFFSET) as *const u32); @@ -1231,6 +1233,8 @@ pub fn FXmacSendHandler(instance: &mut FXmac) pub fn FXmacLinkChange(instance: &mut FXmac) { + debug!("-> FXmacLinkChange"); + if instance.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII { let mut link: u32 = 0; diff --git a/src/fxmac_intr.rs b/src/fxmac_intr.rs index c78318042..002e70b70 100644 --- a/src/fxmac_intr.rs +++ b/src/fxmac_intr.rs @@ -59,7 +59,7 @@ pub const FXMAC3_QUEUE3_IRQ_NUM:u32 = (73 + 30); pub fn FXmacErrorHandler(instance_p: &mut FXmac, direction: u8, error_word: u32) { - info!("FXmacErrorHandler, direction={}, error_word={}", direction, error_word); + debug!("-> FXmacErrorHandler, direction={}, error_word={}", direction, error_word); if error_word != 0 { match direction as u32 @@ -73,12 +73,12 @@ pub fn FXmacErrorHandler(instance_p: &mut FXmac, direction: u8, error_word: u32) if (error_word & FXMAC_RXSR_RXOVR_MASK) != 0 { error!("Receive over run"); - FXmacRecvHandler(instance_p); + //FXmacRecvHandler(instance_p); } if (error_word & FXMAC_RXSR_BUFFNA_MASK) != 0 { error!("Receive buffer not available"); - FXmacRecvHandler(instance_p); + //FXmacRecvHandler(instance_p); } } FXMAC_SEND => { @@ -114,18 +114,19 @@ pub fn FXmacErrorHandler(instance_p: &mut FXmac, direction: u8, error_word: u32) } pub fn FXmacRecvIsrHandler(instance: &mut FXmac) { - info!("Recv Isr"); + debug!("-> FXmacRecvIsrHandler"); + // 关中断 write_reg((instance.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_RXCOMPL_MASK); instance.lwipport.recv_flg += 1; ethernetif_input_to_recv_packets(instance); + // 处理后会开中断 } pub static XMAC: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); -pub fn xmac_intr_handler(intr: u64) { - - info!("Handling xmac intr ..."); +pub fn xmac_intr_handler() { + debug!("Handling xmac intr ..."); let xmac = XMAC.load(Ordering::Relaxed); if !xmac.is_null() { @@ -134,7 +135,8 @@ pub fn xmac_intr_handler(intr: u64) { // maybe irq num let vector = xmac_ptr.config.queue_irq_num[0] ; FXmacIntrHandler(vector as i32, xmac_ptr); - info!("xmac intr is already handled"); + + info!("xmac intr is already handled"); } else { error!("static FXmac has not been initialized"); } @@ -164,7 +166,7 @@ pub fn FXmacIntrHandler(vector: i32, instance_p: &mut FXmac) { */ let mut reg_isr: u32 = read_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *const u32); - info!("<<<<<<<<< IRQ num vector={}, Interrupt Status ISR={:#x}, tx_queue_id={}, rx_queue_id={}", vector, reg_isr, tx_queue_id, rx_queue_id); + info!("+++++++++ IRQ num vector={}, Interrupt Status ISR={:#x}, tx_queue_id={}, rx_queue_id={}", vector, reg_isr, tx_queue_id, rx_queue_id); if vector as u32 == instance_p.config.queue_irq_num[tx_queue_id as usize] { if tx_queue_id == 0 @@ -433,7 +435,9 @@ pub fn FXmacSetupIsr(instance: &mut FXmac) // setup interrupt handler, 该函数将自定义中断回调函数注册到对应的中断ID // 使能对应中断 - let irq_num = instance.config.queue_irq_num[0] as usize; + let irq_num = instance.config.queue_irq_num[0] as usize; // 32 + 55 + + // SPI(Shared Peripheral Interrupt) rang: 32..1020 info!("register callback function for irq: {}", irq_num); crate::utils::dma_request_irq(irq_num, xmac_intr_handler); diff --git a/src/utils.rs b/src/utils.rs index 37900ff0a..25fd4e2ef 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -219,7 +219,7 @@ pub fn dma_free_coherent(vaddr: usize, pages: usize) { /// 请求分配irq #[linkage = "weak"] #[unsafe(export_name = "dma_request_irq_fxmac")] -pub fn dma_request_irq(irq: usize, handler: fn(u64)) { +pub fn dma_request_irq(irq: usize, handler: fn()) { warn!("dma_request_irq_fxmac unimplemented"); //unimplemented!() } From 2fcbe8bd64d1fee6d16fd26f9cd9cdee0a1a1078 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Thu, 23 Jan 2025 13:06:40 +0800 Subject: [PATCH 011/132] Add doc and picture of PHY --- doc/PHY-Motorcomm-YT8521SH.pdf | Bin 0 -> 2094494 bytes doc/phtpi-eth.jpg | Bin 0 -> 81115 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/PHY-Motorcomm-YT8521SH.pdf create mode 100644 doc/phtpi-eth.jpg diff --git a/doc/PHY-Motorcomm-YT8521SH.pdf b/doc/PHY-Motorcomm-YT8521SH.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3c5b0dc8e677137b42ec49e237a68c813b615bdb GIT binary patch literal 2094494 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쾺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

Vx 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 z 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

C1+$oY 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%0D6q4n*Z4cHZgMb=V@sBwOCr9uUNSaThm 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

lN~ay!WF+8V_qLY+MZ|>c&t_a@!dgM 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 z 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

JTP2Y<4|Wxrv;}mpMHh*0+v})%aP`i= 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

Q|i*2oPax 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�@=w2eFxren#n|sBjBvF%A#T(GR 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
2^#?!I|2_I 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

0rlf?6Z&Q9d*oDq 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 literal 0 HcmV?d00001 From 6dde061d765c5421ed5447956551f02287e200c5 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Thu, 23 Jan 2025 13:07:39 +0800 Subject: [PATCH 012/132] Modified interfaces and README --- Cargo.toml | 11 ++-- README.md | 60 ++++++++++++++------- src/fxmac_dma.rs | 87 +++++++++++++++--------------- src/fxmac_intr.rs | 132 +++++++++++++++++++++++----------------------- src/lib.rs | 9 ++-- src/utils.rs | 67 ++++++++++++----------- 6 files changed, 199 insertions(+), 167 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c5e7ddd3d..fdd5d76cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,13 +6,16 @@ authors = ["xiaoluoyuan@163.com"] version = "0.1.0" edition = "2024" license = "GPL-2.0" -description = "fxmac_rs ethernet rust driver on SiFive FU740 board." -homepage = "https://github.com/elliott10/cadence-macb.git" -documentation = "https://github.com/elliott10/cadence-macb/blob/main/README.md" -repository = "https://github.com/elliott10/cadence-macb.git" +description = "fxmac ethernet driver in Rust on PhytiumPi board." +homepage = "https://github.com/elliott10/fxmac_rs.git" +documentation = "https://github.com/elliott10/fxmac_rs/blob/main/README.md" +repository = "https://github.com/elliott10/fxmac_rs.git" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] log = "0.4" + + +[target.'cfg(target_arch = "aarch64")'.dependencies] aarch64-cpu = "10" \ No newline at end of file diff --git a/README.md b/README.md index 51b937b18..73d6aebca 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,57 @@ # FXMAC ethernet driver -fxmac ethernet Rust driver on PhytiumPi board. +fxmac ethernet driver in Rust on PhytiumPi board. -### Quick Start +## Quick Start -Initialize ethernet driver +* Initialize ethernet driver ``` -pub struct MEM; -impl MemMapping for MEM { - fn dma_alloc_coherent(pages: usize) -> usize { - ... - } - fn dma_free_coherent(paddr: usize, pages: usize) { - ... - } +/// 虚拟地址转换成物理地址 +#[no_mangle] +pub fn virt_to_phys_fxmac(addr: usize) -> usize { } -let mut macb_device = cadence_macb::eth_macb::open::(&mac).unwrap(); -``` +/// 物理地址转换成虚拟地址 +#[no_mangle] +pub fn phys_to_virt_fxmac(addr: usize) -> usize { +} + +/// 使能并注册网卡中断 +#[no_mangle] +pub fn dma_request_irq_fxmac(irq: usize, handler: fn()) { +} + +/// 申请页对齐的DMA连续内存页 +/// 返回((cpu virtual address, dma physical address)) +#[no_mangle] +pub fn dma_alloc_coherent_fxmac(pages: usize) -> (usize, usize) { +} + +/// 释放DMA内存页 +#[no_mangle] +fn dma_free_coherent_fxmac(vaddr: usize, pages: usize) { +} -Sending network packets +let hwaddr: [u8; 6] = [0x55, 0x44, 0x33, 0x22, 0x11, 0x00]; +let fxmac_device: &'static mut FXmac = fxmac_rs::fxmac::xmac_init(&hwaddr); ``` -cadence_macb::eth_macb_ops::macb_send(&mut macb_device, &packet); +* Sending network packets +``` +let mut tx_vec = Vec::new(); +tx_vec.push(packet.to_vec()); +FXmacLwipPortTx(fxmac_device, tx_vec); ``` -Receiving network packets +* Receiving network packets ``` -cadence_macb::eth_macb_ops::macb_recv(&mut macb_device, &mut rx_buffer); +let recv_packets = FXmacRecvHandler(fxmac_device); ``` +## About ethernet +PHY: Motorcomm YT8521 + +![yt8521](doc/phtpi-eth.jpg) ## Reference -* phytium-standalone-sdk -* Linux and U-Boot C code +* [phytium-standalone-sdk](https://gitee.com/phytium_embedded/phytium-standalone-sdk/tree/master) *sdk驱动的代码质量及逻辑写得一言难尽啊 * +* [Linux 5.10](https://gitee.com/phytium_embedded/phytium-linux-kernel/blob/linux-5.10/drivers/net/ethernet/cadence/macb_main.c) diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index a863762f4..95171d4d7 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -752,26 +752,6 @@ pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p status } -pub fn FXmacLwipPortTx(instance: &mut FXmac, pbuf: Vec>) -> i32 -{ - info!("TX transmit packets"); - // 发送网络包时注意屏蔽下中断 - - // check if space is available to send - let freecnt = (instance.tx_bd_queue.bdring).free_cnt; - if freecnt <= 4 { //5 - info!("TX freecnt={}, let's process sent BDs", freecnt); - FXmacProcessSentBds(instance); - } - - if (instance.tx_bd_queue.bdring).free_cnt != 0 { - FXmacSgsend(instance, pbuf) as i32 - }else{ - error!(" TX packets dropped, no space"); - -3 // FREERTOS_XMAC_NO_VALID_SPACE - } -} - /// 发包函数 pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { let mut status: u32 = 0; @@ -1038,30 +1018,6 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { } } -pub fn ethernetif_input_to_recv_packets(instance_p: &mut FXmac) -{ - if(instance_p.lwipport.recv_flg > 0) - { - info!("ethernetif_input_to_recv_packets, fxmac_port->recv_flg={}", instance_p.lwipport.recv_flg); - - // 也许需要屏蔽中断的临界区来保护 - instance_p.lwipport.recv_flg -= 1; - - // 开中断 - write_reg((instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, instance_p.mask); - - // 若需要中断处理函数中来接收包,可以这里解注释 - //FXmacRecvHandler(instance_p); - } - - { - // move received packet into a new pbuf - //p = low_level_input(netif); - // IP or ARP packet - // full packet send to tcpip thread to process - } -} - /// FXmacBdRingFree, Frees a set of BDs that had been previously retrieved with pub fn FXmacBdRingFree(ring_ptr: &mut FXmacBdRing, num_bd: u32) -> u32 { @@ -1310,3 +1266,46 @@ pub fn phy_autoneg_status(xmac_p: &mut FXmac, phy_addr: u32) -> u32 0 } + +pub fn FXmacLwipPortTx(instance: &mut FXmac, pbuf: Vec>) -> i32 { + info!("TX transmit packets"); + // 发送网络包时注意屏蔽下中断 + + // check if space is available to send + let freecnt = (instance.tx_bd_queue.bdring).free_cnt; + if freecnt <= 4 { //5 + info!("TX freecnt={}, let's process sent BDs", freecnt); + FXmacProcessSentBds(instance); + } + + if (instance.tx_bd_queue.bdring).free_cnt != 0 { + FXmacSgsend(instance, pbuf) as i32 + }else{ + error!(" TX packets dropped, no space"); + -3 // FREERTOS_XMAC_NO_VALID_SPACE + } +} + +pub fn ethernetif_input_to_recv_packets(instance_p: &mut FXmac) +{ + if(instance_p.lwipport.recv_flg > 0) + { + info!("ethernetif_input_to_recv_packets, fxmac_port->recv_flg={}", instance_p.lwipport.recv_flg); + + // 也许需要屏蔽中断的临界区来保护 + instance_p.lwipport.recv_flg -= 1; + + // 开中断 + write_reg((instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, instance_p.mask); + + // 若需要中断处理函数中来接收包,可以这里解注释 + //FXmacRecvHandler(instance_p); + } + + { + // move received packet into a new pbuf + //p = low_level_input(netif); + // IP or ARP packet + // full packet send to tcpip thread to process + } +} \ No newline at end of file diff --git a/src/fxmac_intr.rs b/src/fxmac_intr.rs index 002e70b70..478962a4d 100644 --- a/src/fxmac_intr.rs +++ b/src/fxmac_intr.rs @@ -57,72 +57,6 @@ pub const FXMAC3_QUEUE3_IRQ_NUM:u32 = (73 + 30); //pub const FXMAC_PHY_MAX_NUM:u32 = 32; // #define FXMAC_CLK_TYPE_0 -pub fn FXmacErrorHandler(instance_p: &mut FXmac, direction: u8, error_word: u32) -{ - debug!("-> FXmacErrorHandler, direction={}, error_word={}", direction, error_word); - if error_word != 0 - { - match direction as u32 - { - FXMAC_RECV => { - if (error_word & FXMAC_RXSR_HRESPNOK_MASK) != 0 - { - error!("Receive DMA error"); - FXmacHandleDmaTxError(instance_p); - } - if (error_word & FXMAC_RXSR_RXOVR_MASK) != 0 - { - error!("Receive over run"); - //FXmacRecvHandler(instance_p); - } - if (error_word & FXMAC_RXSR_BUFFNA_MASK) != 0 - { - error!("Receive buffer not available"); - //FXmacRecvHandler(instance_p); - } - } - FXMAC_SEND => { - if (error_word & FXMAC_TXSR_HRESPNOK_MASK) != 0 - { - error!("Transmit DMA error"); - FXmacHandleDmaTxError(instance_p); - } - if (error_word & FXMAC_TXSR_URUN_MASK) != 0 - { - error!("Transmit under run"); - FXmacHandleTxErrors(instance_p); - } - if (error_word & FXMAC_TXSR_BUFEXH_MASK) != 0 - { - error!("Transmit buffer exhausted"); - FXmacHandleTxErrors(instance_p); - } - if (error_word & FXMAC_TXSR_RXOVR_MASK) != 0 - { - error!("Transmit retry excessed limits"); - FXmacHandleTxErrors(instance_p); - } - if (error_word & FXMAC_TXSR_FRAMERX_MASK) != 0 - { - error!("Transmit collision"); - FXmacProcessSentBds(instance_p); - } - } - _ => { error!("FXmacErrorHandler failed, unknown direction={}", direction); } - } - } -} - -pub fn FXmacRecvIsrHandler(instance: &mut FXmac) { - debug!("-> FXmacRecvIsrHandler"); - // 关中断 - write_reg((instance.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_RXCOMPL_MASK); - instance.lwipport.recv_flg += 1; - - ethernetif_input_to_recv_packets(instance); - // 处理后会开中断 -} - pub static XMAC: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); pub fn xmac_intr_handler() { @@ -413,6 +347,72 @@ pub fn FXmacIntrHandler(vector: i32, instance_p: &mut FXmac) { } } + +pub fn FXmacErrorHandler(instance_p: &mut FXmac, direction: u8, error_word: u32) +{ + debug!("-> FXmacErrorHandler, direction={}, error_word={}", direction, error_word); + if error_word != 0 + { + match direction as u32 + { + FXMAC_RECV => { + if (error_word & FXMAC_RXSR_HRESPNOK_MASK) != 0 + { + error!("Receive DMA error"); + FXmacHandleDmaTxError(instance_p); + } + if (error_word & FXMAC_RXSR_RXOVR_MASK) != 0 + { + error!("Receive over run"); + //FXmacRecvHandler(instance_p); + } + if (error_word & FXMAC_RXSR_BUFFNA_MASK) != 0 + { + error!("Receive buffer not available"); + //FXmacRecvHandler(instance_p); + } + } + FXMAC_SEND => { + if (error_word & FXMAC_TXSR_HRESPNOK_MASK) != 0 + { + error!("Transmit DMA error"); + FXmacHandleDmaTxError(instance_p); + } + if (error_word & FXMAC_TXSR_URUN_MASK) != 0 + { + error!("Transmit under run"); + FXmacHandleTxErrors(instance_p); + } + if (error_word & FXMAC_TXSR_BUFEXH_MASK) != 0 + { + error!("Transmit buffer exhausted"); + FXmacHandleTxErrors(instance_p); + } + if (error_word & FXMAC_TXSR_RXOVR_MASK) != 0 + { + error!("Transmit retry excessed limits"); + FXmacHandleTxErrors(instance_p); + } + if (error_word & FXMAC_TXSR_FRAMERX_MASK) != 0 + { + error!("Transmit collision"); + FXmacProcessSentBds(instance_p); + } + } + _ => { error!("FXmacErrorHandler failed, unknown direction={}", direction); } + } + } +} + +pub fn FXmacRecvIsrHandler(instance: &mut FXmac) { + debug!("-> FXmacRecvIsrHandler"); + // 关中断 + write_reg((instance.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_RXCOMPL_MASK); + instance.lwipport.recv_flg += 1; + + ethernetif_input_to_recv_packets(instance); + // 处理后会开中断 +} /// 网卡中断设置 pub fn FXmacSetupIsr(instance: &mut FXmac) diff --git a/src/lib.rs b/src/lib.rs index 5d2fc373c..532fa049c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![feature(linkage)] -#![allow(dead_code)] #![allow(unused)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] @@ -18,10 +17,14 @@ mod utils; mod fxmac_phy; mod fxmac_dma; mod fxmac_intr; -pub mod fxmac; +mod fxmac; pub use fxmac::{FXmac, xmac_init}; -pub use fxmac_dma::{FXmacLwipPortTx, FXmacRecvHandler}; +pub use fxmac_dma::{FXmacInitDma, FXmacLwipPortTx, FXmacRecvHandler}; +pub use fxmac_intr::{FXmacIntrHandler, xmac_intr_handler}; + +// PHY interface +pub use fxmac_phy::{FXmacPhyInit, FXmacPhyRead, FXmacPhyWrite}; #[cfg(test)] mod tests { diff --git a/src/utils.rs b/src/utils.rs index 25fd4e2ef..d5668dbf8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -7,15 +7,6 @@ pub const CORE2_AFF: u64 = 0x00; pub const CORE3_AFF: u64 = 0x100; pub const FCORE_NUM: u64 = 4; -/// Read reg: MPIDR_EL1 -pub(crate) fn read_mpidr() -> u64 { - let mut reg_r = 0; - unsafe { - core::arch::asm!("mrs {}, MPIDR_EL1", out(reg) reg_r); - } - reg_r -} - /// Converts MPIDR to CPU ID pub(crate) fn mpidr2cpuid(mpidr: u64) -> usize { // RK3588 @@ -37,20 +28,31 @@ pub(crate) fn mpidr2cpuid(mpidr: u64) -> usize { } } +#[inline] +/// Read reg: MPIDR_EL1 +fn read_mpidr() -> u64 { + let mut reg_r = 0; + unsafe { + core::arch::asm!("mrs {}, MPIDR_EL1", out(reg) reg_r); + } + reg_r +} + pub(crate) fn get_cpu_id() -> usize { let mpidr = read_mpidr(); mpidr2cpuid(mpidr) } /// Data Synchronization Barrier -pub fn DSB() { +pub(crate) fn DSB() { unsafe { core::arch::asm!("dsb sy"); } } +#[inline] // pseudo assembler instructions -pub fn MFCPSR() -> u32{ +fn MFCPSR() -> u32{ let mut rval: u32 = 0; unsafe { asm!("mrs {0:x}, DAIF", out(reg) rval); @@ -58,20 +60,21 @@ unsafe { rval } - -pub fn MTCPSR(val: u32) { +#[inline] +fn MTCPSR(val: u32) { unsafe { asm!("msr DAIF, {0:x}", in(reg) val); } } - -pub fn MTCPDC_CIVAC(adr: u64) { +#[inline] +fn MTCPDC_CIVAC(adr: u64) { unsafe { asm!("dc CIVAC, {}", in(reg) adr); } } -pub fn MTCPDC_CVAC(adr: u64) { +#[inline] +fn MTCPDC_CVAC(adr: u64) { unsafe { asm!("dc CVAC, {}", in(reg) adr); } @@ -87,7 +90,7 @@ pub const IRQ_FIQ_MASK: u32 = 0xC0; /// dc civac, virt_addr 通过虚拟地址清除和无效化cache /// adr: 64bit start address of the range to be invalidated. /// len: Length of the range to be invalidated in bytes. -pub fn FCacheDCacheInvalidateRange(mut adr: u64, len: u64) +pub(crate) fn FCacheDCacheInvalidateRange(mut adr: u64, len: u64) { let end: u64 = adr + len; adr = adr & (!CACHE_LINE_ADDR_MASK); @@ -108,7 +111,7 @@ pub fn FCacheDCacheInvalidateRange(mut adr: u64, len: u64) /// Flush Data cache /// DC CVAC, Virtual address to use. No alignment restrictions apply to vaddr /// adr: 64bit start address of the range to be flush. -pub fn FCacheDCacheFlushRange(mut adr: u64, len: u64) +pub(crate) fn FCacheDCacheFlushRange(mut adr: u64, len: u64) { let end: u64 = adr + len; adr = adr & (!CACHE_LINE_ADDR_MASK); @@ -131,29 +134,29 @@ use aarch64_cpu::registers::{CNTVCT_EL0, CNTFRQ_EL0, Readable}; use alloc::boxed::Box; #[inline] -pub fn now_tsc() -> u64 { +fn now_tsc() -> u64 { CNTVCT_EL0.get() } #[inline] -pub fn timer_freq() -> u64 { +fn timer_freq() -> u64 { CNTFRQ_EL0.get() as u64 } // 纳秒(ns) #[inline] -pub fn now_ns() -> u64 { +pub(crate) fn now_ns() -> u64 { let freq = timer_freq(); now_tsc() * (1_000_000_000 / freq) } -pub fn ticks_to_nanos(ticks: u64) -> u64 { +pub(crate) fn ticks_to_nanos(ticks: u64) -> u64 { let freq = timer_freq(); ticks * (1_000_000_000 / freq) } // 微秒(us) -pub fn usdelay(us: u64) { +pub(crate) fn usdelay(us: u64) { let mut current_ticks: u64 = now_tsc(); let delay2 = current_ticks + us * (timer_freq() / 1000000); @@ -166,20 +169,22 @@ pub fn usdelay(us: u64) { } // 毫秒(ms) -#[allow(unused)] -pub fn msdelay(ms: u64) { +pub(crate) fn msdelay(ms: u64) { usdelay(ms * 1000); } + +/// 虚拟地址转换成物理地址 #[linkage = "weak"] #[unsafe(export_name = "virt_to_phys_fxmac")] -pub fn virt_to_phys(addr: usize) -> usize { +pub(crate) fn virt_to_phys(addr: usize) -> usize { debug!("fxmac: virt_to_phys_fxmac {:#x}", addr); addr } +/// 物理地址转换成虚拟地址 #[linkage = "weak"] #[unsafe(export_name = "phys_to_virt_fxmac")] -pub fn phys_to_virt(addr: usize) -> usize { +pub(crate) fn phys_to_virt(addr: usize) -> usize { debug!("fxmac: phys_to_virt_fxmac {:#x}", addr); addr } @@ -187,7 +192,7 @@ pub fn phys_to_virt(addr: usize) -> usize { /// 申请DMA连续内存页 #[linkage = "weak"] #[unsafe(export_name = "dma_alloc_coherent_fxmac")] -pub fn dma_alloc_coherent(pages: usize) -> (usize, usize) { +pub(crate) fn dma_alloc_coherent(pages: usize) -> (usize, usize) { let paddr: Box<[u32]> = if pages == 1 { Box::new([0; 1024]) // 4096 } else if pages == 8 { @@ -210,7 +215,7 @@ pub fn dma_alloc_coherent(pages: usize) -> (usize, usize) { /// 释放DMA内存页 #[linkage = "weak"] #[unsafe(export_name = "dma_free_coherent_fxmac")] -pub fn dma_free_coherent(vaddr: usize, pages: usize) { +pub(crate) fn dma_free_coherent(vaddr: usize, pages: usize) { debug!("fxmac: dma free vaddr: {:#x}, pages={}", vaddr, pages); let palloc = vaddr as *mut [u32; 1024]; unsafe{ drop(Box::from_raw(palloc)); } @@ -219,10 +224,10 @@ pub fn dma_free_coherent(vaddr: usize, pages: usize) { /// 请求分配irq #[linkage = "weak"] #[unsafe(export_name = "dma_request_irq_fxmac")] -pub fn dma_request_irq(irq: usize, handler: fn()) { +pub(crate) fn dma_request_irq(irq: usize, handler: fn()) { warn!("dma_request_irq_fxmac unimplemented"); //unimplemented!() } // 路由中断到指定的cpu,或所有的cpu -//pub(crate) fn InterruptSetTargetCpus() {} +//pub(crate) fn InterruptSetTargetCpus() {} \ No newline at end of file From cb670e07c900887e4afdf52cc019e807d2a9e769 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Thu, 6 Feb 2025 14:05:47 +0800 Subject: [PATCH 013/132] Pass building --- Cargo.toml | 3 +-- src/fxmac.rs | 18 +++++++++--------- src/fxmac_const.rs | 4 ++-- src/fxmac_dma.rs | 5 +++-- src/fxmac_intr.rs | 26 ++++++-------------------- 5 files changed, 21 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fdd5d76cc..9d196bcf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,5 @@ repository = "https://github.com/elliott10/fxmac_rs.git" [dependencies] log = "0.4" - -[target.'cfg(target_arch = "aarch64")'.dependencies] +# [target.'cfg(target_arch = "aarch64")'.dependencies] aarch64-cpu = "10" \ No newline at end of file diff --git a/src/fxmac.rs b/src/fxmac.rs index a2118cb9f..dd926c204 100644 --- a/src/fxmac.rs +++ b/src/fxmac.rs @@ -683,25 +683,25 @@ fn FXmacClkDivGet(instance_p: &mut FXmac) -> u32 { let pclk_hz = instance_p.config.pclk_hz; // FXMAC0_PCLK; if (pclk_hz <= 20000000) { - return FXMAC_NWCFG_CLOCK_DIV8_MASK; + FXMAC_NWCFG_CLOCK_DIV8_MASK } else if (pclk_hz <= 40000000) { - return FXMAC_NWCFG_CLOCK_DIV16_MASK; + FXMAC_NWCFG_CLOCK_DIV16_MASK } else if (pclk_hz <= 80000000) { - return FXMAC_NWCFG_CLOCK_DIV32_MASK; + FXMAC_NWCFG_CLOCK_DIV32_MASK } else if (instance_p.moudle_id >= 2) { if (pclk_hz <= 120000000) { - return FXMAC_NWCFG_CLOCK_DIV48_MASK; + FXMAC_NWCFG_CLOCK_DIV48_MASK } else if (pclk_hz <= 160000000) { - return FXMAC_NWCFG_CLOCK_DIV64_MASK; + FXMAC_NWCFG_CLOCK_DIV64_MASK } else if (pclk_hz <= 240000000) { - return FXMAC_NWCFG_CLOCK_DIV96_MASK; + FXMAC_NWCFG_CLOCK_DIV96_MASK } else if (pclk_hz <= 320000000) { - return FXMAC_NWCFG_CLOCK_DIV128_MASK; + FXMAC_NWCFG_CLOCK_DIV128_MASK } else { - return FXMAC_NWCFG_CLOCK_DIV224_MASK; + FXMAC_NWCFG_CLOCK_DIV224_MASK } } else { - return FXMAC_NWCFG_CLOCK_DIV64_MASK; + FXMAC_NWCFG_CLOCK_DIV64_MASK } } diff --git a/src/fxmac_const.rs b/src/fxmac_const.rs index 2b0d7c014..e6910875e 100644 --- a/src/fxmac_const.rs +++ b/src/fxmac_const.rs @@ -1,5 +1,5 @@ //////////////////// -/// fxmac_hw.h +// fxmac_hw.h pub(crate) const FXMAC_RX_BUF_UNIT:u32 = 64; /* Number of receive buffer bytes as a unit, this is HW setup */ @@ -541,7 +541,7 @@ pub const fn GENMASK(h:u32, l: u32) -> u32 { } //////////////////// -/// fxmac.h +// fxmac.h pub(crate) const FXMAC_PROMISC_OPTION:u32 = 0x00000001; /* Accept all incoming packets. diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index 95171d4d7..c62285cb6 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -1,5 +1,6 @@ use core::cmp::min; use core::ptr::{null_mut, null}; +use core::mem::size_of; use core::slice::from_raw_parts_mut; use alloc::boxed::Box; @@ -89,7 +90,7 @@ pub type FXmacBd = [u32; FXMAC_BD_NUM_WORDS]; // sizeof(uintptr)=8 pub type uintptr = u64; -/// Enable tail Register +// Enable tail Register //pub const FXMAC_TAIL_ENABLE: u32 = 0xe7c; /// send direction @@ -887,7 +888,7 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { loop { // Returns a set of BD(s) that have been processed by hardware. let bd_processed: u32 = FXmacBdRingFromHwRx(&mut instance_p.rx_bd_queue.bdring, FXMAX_RX_PBUFS_LENGTH, &mut rxbdset); - if bd_processed <= 0 + if bd_processed == 0 { // 没有待收的网络包了 break; diff --git a/src/fxmac_intr.rs b/src/fxmac_intr.rs index 478962a4d..4a2dea50b 100644 --- a/src/fxmac_intr.rs +++ b/src/fxmac_intr.rs @@ -215,14 +215,12 @@ pub fn FXmacIntrHandler(vector: i32, instance_p: &mut FXmac) { } /* Receive error conditions interrupt */ - if ((reg_isr & FXMAC_IXR_RX_ERR_MASK) != 0) - { + if (reg_isr & FXMAC_IXR_RX_ERR_MASK) != 0 { /* Clear RX status register */ let mut reg_rxsr: u32 = read_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *const u32); write_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, reg_rxsr); - if ((reg_isr & FXMAC_IXR_RXUSED_MASK) != 0) - { + if (reg_isr & FXMAC_IXR_RXUSED_MASK) != 0 { let reg_ctrl: u32 = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); let mut reg_temp: u32 = reg_ctrl | FXMAC_NWCTRL_FLUSH_DPRAM_MASK as u32; @@ -240,32 +238,20 @@ pub fn FXmacIntrHandler(vector: i32, instance_p: &mut FXmac) { } /* add */ - if ((reg_isr & FXMAC_IXR_RXOVR_MASK) != 0) - { - if(instance_p.caps& FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 - { + if ((reg_isr & FXMAC_IXR_RXOVR_MASK) != 0) && ((instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0) { write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_RXOVR_MASK); - } } /* add */ - if ((reg_isr & FXMAC_IXR_HRESPNOK_MASK) != 0) - { - if(instance_p.caps& FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 - { + if ((reg_isr & FXMAC_IXR_HRESPNOK_MASK) != 0) && ((instance_p.caps& FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0) { write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_HRESPNOK_MASK); - } } - - if (reg_rxsr != 0) - { + if reg_rxsr != 0 { FXmacErrorHandler(instance_p, FXMAC_RECV as u8, reg_rxsr); } } - } - else /* use queue number more than 0 */ - { + } else { /* use queue number more than 0 */ reg_isr = read_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)) as *const u32); /* Receive complete interrupt */ From c0b08cde6cfa45686b27390e342d320217129d3c Mon Sep 17 00:00:00 2001 From: elliott10 Date: Fri, 7 Feb 2025 21:28:29 +0800 Subject: [PATCH 014/132] Organize arch-related functions --- src/utils.rs | 272 ++++++++++++++++++++++++++++----------------------- 1 file changed, 148 insertions(+), 124 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index d5668dbf8..75668b2fb 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,148 +1,170 @@ -use core::arch::asm; - -// PhytiumPi -pub const CORE0_AFF: u64 = 0x200; -pub const CORE1_AFF: u64 = 0x201; -pub const CORE2_AFF: u64 = 0x00; -pub const CORE3_AFF: u64 = 0x100; -pub const FCORE_NUM: u64 = 4; - -/// Converts MPIDR to CPU ID -pub(crate) fn mpidr2cpuid(mpidr: u64) -> usize { - // RK3588 - //((mpidr >> 8) & 0xff) as usize - - // Qemu - //(mpidr & 0xffffff & 0xff) as usize +#[cfg(target_arch = "aarch64")] +mod arch { + use core::arch::asm; // PhytiumPi - match (mpidr & 0xfff) { - CORE0_AFF => 0, - CORE1_AFF => 1, - CORE2_AFF => 2, - CORE3_AFF => 3, - _ => { - error!("Failed to get PhytiumPi CPU Id from mpidr={:#x}", mpidr); - 0 + pub const CORE0_AFF: u64 = 0x200; + pub const CORE1_AFF: u64 = 0x201; + pub const CORE2_AFF: u64 = 0x00; + pub const CORE3_AFF: u64 = 0x100; + pub const FCORE_NUM: u64 = 4; + + /// Converts MPIDR to CPU ID + pub(crate) fn mpidr2cpuid(mpidr: u64) -> usize { + // RK3588 + //((mpidr >> 8) & 0xff) as usize + + // Qemu + //(mpidr & 0xffffff & 0xff) as usize + + // PhytiumPi + match (mpidr & 0xfff) { + CORE0_AFF => 0, + CORE1_AFF => 1, + CORE2_AFF => 2, + CORE3_AFF => 3, + _ => { + error!("Failed to get PhytiumPi CPU Id from mpidr={:#x}", mpidr); + 0 + } } } -} -#[inline] -/// Read reg: MPIDR_EL1 -fn read_mpidr() -> u64 { - let mut reg_r = 0; - unsafe { - core::arch::asm!("mrs {}, MPIDR_EL1", out(reg) reg_r); - } - reg_r -} + #[inline] + /// Read reg: MPIDR_EL1 + fn read_mpidr() -> u64 { + let mut reg_r = 0; + unsafe { + core::arch::asm!("mrs {}, MPIDR_EL1", out(reg) reg_r); + } + reg_r + } -pub(crate) fn get_cpu_id() -> usize { - let mpidr = read_mpidr(); - mpidr2cpuid(mpidr) -} + pub(crate) fn get_cpu_id() -> usize { + let mpidr = read_mpidr(); + mpidr2cpuid(mpidr) + } -/// Data Synchronization Barrier -pub(crate) fn DSB() { - unsafe { - core::arch::asm!("dsb sy"); - } -} + /// Data Synchronization Barrier + pub(crate) fn DSB() { + unsafe { + core::arch::asm!("dsb sy"); + } + } -#[inline] -// pseudo assembler instructions -fn MFCPSR() -> u32{ - let mut rval: u32 = 0; -unsafe { - asm!("mrs {0:x}, DAIF", out(reg) rval); -} - rval -} + #[inline] + // pseudo assembler instructions + fn MFCPSR() -> u32 { + let mut rval: u32 = 0; + unsafe { + asm!("mrs {0:x}, DAIF", out(reg) rval); + } + rval + } -#[inline] -fn MTCPSR(val: u32) { -unsafe { - asm!("msr DAIF, {0:x}", in(reg) val); -} -} -#[inline] -fn MTCPDC_CIVAC(adr: u64) { - unsafe { - asm!("dc CIVAC, {}", in(reg) adr); + #[inline] + fn MTCPSR(val: u32) { + unsafe { + asm!("msr DAIF, {0:x}", in(reg) val); + } + } + #[inline] + fn MTCPDC_CIVAC(adr: u64) { + unsafe { + asm!("dc CIVAC, {}", in(reg) adr); + } } -} -#[inline] -fn MTCPDC_CVAC(adr: u64) { - unsafe { - asm!("dc CVAC, {}", in(reg) adr); + #[inline] + fn MTCPDC_CVAC(adr: u64) { + unsafe { + asm!("dc CVAC, {}", in(reg) adr); + } } -} -/// CACHE of PhytiumPi -pub const CACHE_LINE_ADDR_MASK: u64 = 0x3F; -pub const CACHE_LINE: u64 = 64; - -/// Mask IRQ and FIQ interrupts in cpsr -pub const IRQ_FIQ_MASK: u32 = 0xC0; - -/// dc civac, virt_addr 通过虚拟地址清除和无效化cache -/// adr: 64bit start address of the range to be invalidated. -/// len: Length of the range to be invalidated in bytes. -pub(crate) fn FCacheDCacheInvalidateRange(mut adr: u64, len: u64) -{ - let end: u64 = adr + len; - adr = adr & (!CACHE_LINE_ADDR_MASK); - let currmask: u32 = MFCPSR(); - MTCPSR(currmask | IRQ_FIQ_MASK); - if (len != 0) - { - while adr < end { - MTCPDC_CIVAC(adr); /* Clean and Invalidate data cache by address to Point of Coherency */ - adr += CACHE_LINE; - } - } - /* Wait for invalidate to complete */ - DSB(); - MTCPSR(currmask); -} + /// CACHE of PhytiumPi + pub const CACHE_LINE_ADDR_MASK: u64 = 0x3F; + pub const CACHE_LINE: u64 = 64; + + /// Mask IRQ and FIQ interrupts in cpsr + pub const IRQ_FIQ_MASK: u32 = 0xC0; + + /// dc civac, virt_addr 通过虚拟地址清除和无效化cache + /// adr: 64bit start address of the range to be invalidated. + /// len: Length of the range to be invalidated in bytes. + pub(crate) fn FCacheDCacheInvalidateRange(mut adr: u64, len: u64) { + let end: u64 = adr + len; + adr = adr & (!CACHE_LINE_ADDR_MASK); + let currmask: u32 = MFCPSR(); + MTCPSR(currmask | IRQ_FIQ_MASK); + if (len != 0) { + while adr < end { + MTCPDC_CIVAC(adr); /* Clean and Invalidate data cache by address to Point of Coherency */ + adr += CACHE_LINE; + } + } + /* Wait for invalidate to complete */ + DSB(); + MTCPSR(currmask); + } -/// Flush Data cache -/// DC CVAC, Virtual address to use. No alignment restrictions apply to vaddr -/// adr: 64bit start address of the range to be flush. -pub(crate) fn FCacheDCacheFlushRange(mut adr: u64, len: u64) -{ - let end: u64 = adr + len; - adr = adr & (!CACHE_LINE_ADDR_MASK); - let currmask: u32 = MFCPSR(); - MTCPSR(currmask | IRQ_FIQ_MASK); - if len != 0 - { - while (adr < end) - { - MTCPDC_CVAC(adr); /* Clean data cache by address to Point of Coherency */ - adr += CACHE_LINE; + /// Flush Data cache + /// DC CVAC, Virtual address to use. No alignment restrictions apply to vaddr + /// adr: 64bit start address of the range to be flush. + pub(crate) fn FCacheDCacheFlushRange(mut adr: u64, len: u64) { + let end: u64 = adr + len; + adr = adr & (!CACHE_LINE_ADDR_MASK); + let currmask: u32 = MFCPSR(); + MTCPSR(currmask | IRQ_FIQ_MASK); + if len != 0 { + while (adr < end) { + MTCPDC_CVAC(adr); /* Clean data cache by address to Point of Coherency */ + adr += CACHE_LINE; + } } + /* Wait for Clean to complete */ + DSB(); + MTCPSR(currmask); } - /* Wait for Clean to complete */ - DSB(); - MTCPSR(currmask); -} -use aarch64_cpu::registers::{CNTVCT_EL0, CNTFRQ_EL0, Readable}; -use alloc::boxed::Box; + use aarch64_cpu::registers::{CNTFRQ_EL0, CNTVCT_EL0, Readable}; -#[inline] -fn now_tsc() -> u64 { - CNTVCT_EL0.get() + #[inline] + pub fn now_tsc() -> u64 { + CNTVCT_EL0.get() + } + + #[inline] + pub fn timer_freq() -> u64 { + CNTFRQ_EL0.get() as u64 + } } -#[inline] -fn timer_freq() -> u64 { - CNTFRQ_EL0.get() as u64 +#[cfg(not(target_arch = "aarch64"))] +mod arch { + pub fn timer_freq() -> u64 { + unimplemented!() + } + pub fn now_tsc() -> u64 { + unimplemented!() + } + pub(crate) fn get_cpu_id() -> usize { + unimplemented!() + } + pub(crate) fn DSB() { + unimplemented!() + } + pub(crate) fn FCacheDCacheFlushRange(mut adr: u64, len: u64) { + unimplemented!() + } + pub(crate) fn FCacheDCacheInvalidateRange(mut adr: u64, len: u64) { + unimplemented!() + } } +use alloc::boxed::Box; +pub use arch::*; + // 纳秒(ns) #[inline] pub(crate) fn now_ns() -> u64 { @@ -218,7 +240,9 @@ pub(crate) fn dma_alloc_coherent(pages: usize) -> (usize, usize) { pub(crate) fn dma_free_coherent(vaddr: usize, pages: usize) { debug!("fxmac: dma free vaddr: {:#x}, pages={}", vaddr, pages); let palloc = vaddr as *mut [u32; 1024]; - unsafe{ drop(Box::from_raw(palloc)); } + unsafe { + drop(Box::from_raw(palloc)); + } } /// 请求分配irq From 44f5768267b6ca5174331b7946c0fdef4b672a3c Mon Sep 17 00:00:00 2001 From: elliott10 Date: Tue, 18 Feb 2025 14:15:44 +0800 Subject: [PATCH 015/132] Using crate_interface to define net driver interface modified macro for log::debug --- Cargo.lock | 49 ++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 6 +++++- README.md | 6 ++++++ src/fxmac.rs | 5 ++--- src/fxmac_dma.rs | 17 ++++++++-------- src/fxmac_intr.rs | 2 +- src/fxmac_phy.rs | 4 ++-- src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 113 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3a2b5ace..7e4490574 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aarch64-cpu" @@ -11,11 +11,23 @@ dependencies = [ "tock-registers", ] +[[package]] +name = "crate_interface" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70272a03a2cef15589bac05d3d15c023752f5f8f2da8be977d983a9d9e6250fb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "fxmac_rs" version = "0.1.0" dependencies = [ "aarch64-cpu", + "crate_interface", "log", ] @@ -25,8 +37,43 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tock-registers" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b9e2fdb3a1e862c0661768b7ed25390811df1947a8acbfbefe09b47078d93c4" + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" diff --git a/Cargo.toml b/Cargo.toml index 9d196bcf0..0c1b09355 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,12 @@ repository = "https://github.com/elliott10/fxmac_rs.git" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +debug = [] + [dependencies] log = "0.4" +crate_interface = "0.1.4" # [target.'cfg(target_arch = "aarch64")'.dependencies] -aarch64-cpu = "10" \ No newline at end of file +aarch64-cpu = "10" diff --git a/README.md b/README.md index 73d6aebca..f7b78a123 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,12 @@ FXmacLwipPortTx(fxmac_device, tx_vec); ``` let recv_packets = FXmacRecvHandler(fxmac_device); +``` + +### Build + +``` +cargo build --target=aarch64-unknown-none-softfloat ``` ## About ethernet PHY: Motorcomm YT8521 diff --git a/src/fxmac.rs b/src/fxmac.rs index dd926c204..eaec7432f 100644 --- a/src/fxmac.rs +++ b/src/fxmac.rs @@ -6,7 +6,6 @@ use crate::fxmac_intr::*; use crate::fxmac_phy::*; use crate::utils::*; use alloc::boxed::Box; -use log::*; pub const FXMAC_HANDLER_DMASEND: u32 = 1; /* 发送中断 */ pub const FXMAC_HANDLER_DMARECV: u32 = 2; /* 接收中断 */ @@ -96,12 +95,12 @@ pub enum FXmacPhyInterface { } pub fn read_reg(src: *const T) -> T { - unsafe { core::ptr::read_volatile(phys_to_virt(src as usize) as *const T) } + unsafe { core::ptr::read_volatile(crate_interface::call_interface!(crate::KernelFunc::phys_to_virt(src as usize)) as *const T) } } pub fn write_reg(dst: *mut T, value: T) { unsafe { - core::ptr::write_volatile(phys_to_virt(dst as usize) as *mut T, value); + core::ptr::write_volatile(crate_interface::call_interface!(crate::KernelFunc::phys_to_virt(dst as usize)) as *mut T, value); } } diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index c62285cb6..8d7575fb3 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -5,7 +5,6 @@ use core::slice::from_raw_parts_mut; use alloc::boxed::Box; use alloc::vec::Vec; -use log::*; use crate::fxmac_const::*; use crate::fxmac_phy::*; use crate::fxmac::*; @@ -159,10 +158,10 @@ impl Default for FXmacNetifBuffer { fn default() -> Self { let alloc_pages = (FXMAX_RX_BDSPACE_LENGTH + (PAGE_SIZE - 1)) / PAGE_SIZE; - let (mut rx_vaddr, mut rx_dma) = crate::utils::dma_alloc_coherent(alloc_pages); + let (mut rx_vaddr, mut rx_dma) = crate_interface::call_interface!(crate::KernelFunc::dma_alloc_coherent(alloc_pages)); let alloc_pages = (FXMAX_TX_BDSPACE_LENGTH + (PAGE_SIZE - 1)) / PAGE_SIZE; - let (mut tx_vaddr, mut tx_dma) = crate::utils::dma_alloc_coherent(alloc_pages); + let (mut tx_vaddr, mut tx_dma) = crate_interface::call_interface!(crate::KernelFunc::dma_alloc_coherent(alloc_pages)); //let rx_buf = unsafe { from_raw_parts_mut(vaddr as *mut u8, FXMAX_RX_BDSPACE_LENGTH) }; @@ -187,13 +186,13 @@ pub struct FXmacLwipPort pub fn fxmac_bd_read(bd_ptr: u64, offset: u32) -> u32 { trace!("fxmac_bd_read at {:#x}", bd_ptr + offset as u64); - read_reg((virt_to_phys(bd_ptr as usize) + offset as usize) as *const u32) + read_reg((crate_interface::call_interface!(crate::KernelFunc::virt_to_phys(bd_ptr as usize)) + offset as usize) as *const u32) } pub fn fxmac_bd_write(bd_ptr: u64, offset: u32, data: u32) { debug!("fxmac_bd_write {:#x} to {:#x}", data, bd_ptr + offset as u64); // uintptr: u64 - write_reg((virt_to_phys(bd_ptr as usize) + offset as usize) as *mut u32, data); + write_reg((crate_interface::call_interface!(crate::KernelFunc::virt_to_phys(bd_ptr as usize)) + offset as usize) as *mut u32, data); } /// FXmacBdSetRxWrap @@ -290,7 +289,8 @@ pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { { info!("FXMAC_LWIP_PORT_CONFIG_JUMBO"); FXMAC_MAX_FRAME_SIZE_JUMBO } else { info!("NO CONFIG_JUMBO"); FXMAC_MAX_FRAME_SIZE }; let alloc_rx_buffer_pages = (max_frame_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; - let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = crate::utils::dma_alloc_coherent(alloc_rx_buffer_pages); + let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = + crate_interface::call_interface!(crate::KernelFunc::dma_alloc_coherent(alloc_rx_buffer_pages)); let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; let mut rxbd: *mut FXmacBd = null_mut(); @@ -346,7 +346,8 @@ pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { FXMAC_MAX_FRAME_SIZE }; let alloc_pages = (max_fr_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; - let (mut tx_mbufs_vaddr, mut tx_mbufs_dma) = crate::utils::dma_alloc_coherent(alloc_pages); + let (mut tx_mbufs_vaddr, mut tx_mbufs_dma) = + crate_interface::call_interface!(crate::KernelFunc::dma_alloc_coherent(alloc_pages)); instance_p.lwipport.buffer.tx_pbufs_storage[index as usize] = tx_mbufs_vaddr as u64; @@ -1111,7 +1112,7 @@ fn FreeOnlyTxPbufs(instance_p: &mut FXmac) { let pbuf = instance_p.lwipport.buffer.tx_pbufs_storage[index]; let pages = (FXMAC_MAX_FRAME_SIZE as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; - crate::utils::dma_free_coherent(pbuf as usize, pages); + crate_interface::call_interface!(crate::KernelFunc::dma_free_coherent(pbuf as usize, pages)); instance_p.lwipport.buffer.tx_pbufs_storage[index] = 0; } diff --git a/src/fxmac_intr.rs b/src/fxmac_intr.rs index 4a2dea50b..0cb82a633 100644 --- a/src/fxmac_intr.rs +++ b/src/fxmac_intr.rs @@ -425,6 +425,6 @@ pub fn FXmacSetupIsr(instance: &mut FXmac) // SPI(Shared Peripheral Interrupt) rang: 32..1020 info!("register callback function for irq: {}", irq_num); - crate::utils::dma_request_irq(irq_num, xmac_intr_handler); + crate_interface::call_interface!(crate::KernelFunc::dma_request_irq(irq_num, xmac_intr_handler)); } \ No newline at end of file diff --git a/src/fxmac_phy.rs b/src/fxmac_phy.rs index 809ca4401..96ea08b10 100644 --- a/src/fxmac_phy.rs +++ b/src/fxmac_phy.rs @@ -160,7 +160,7 @@ pub fn FXmacPhyInit( speed, duplex_mode, autonegotiation_en, - reset_flag, + reset_flag ); let mut ret: u32 = 0; let mut phy_addr: u32 = 0; @@ -470,4 +470,4 @@ pub fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: } FT_SUCCESS -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 532fa049c..b070cbc6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,9 +7,30 @@ extern crate alloc; +#[cfg(feature = "debug")] #[macro_use] extern crate log; +#[cfg(not(feature = "debug"))] +#[macro_use] +mod log { + macro_rules! trace { + ($($arg:expr),*) => { $( let _ = $arg; )* }; + } + macro_rules! debug { + ($($arg:expr),*) => { $( let _ = $arg; )* }; + } + macro_rules! info { + ($($arg:expr),*) => { $( let _ = $arg; )*}; + } + macro_rules! warn { + ($($arg:expr),*) => { $( let _ = $arg; )*}; + } + macro_rules! error { + ($($arg:expr),*) => { $( let _ = $arg; )* }; + } +} + //mod mii_const; mod fxmac_const; @@ -26,6 +47,25 @@ pub use fxmac_intr::{FXmacIntrHandler, xmac_intr_handler}; // PHY interface pub use fxmac_phy::{FXmacPhyInit, FXmacPhyRead, FXmacPhyWrite}; +/// 声明网卡驱动所需的内核功能接口 +#[crate_interface::def_interface] +pub trait KernelFunc{ + /// 虚拟地址转换成物理地址 + fn virt_to_phys(addr: usize) -> usize; + + /// 物理地址转换成虚拟地址 + fn phys_to_virt(addr: usize) -> usize; + + /// 申请DMA连续内存页 + fn dma_alloc_coherent(pages: usize) -> (usize, usize); + + /// 释放DMA内存页 + fn dma_free_coherent(vaddr: usize, pages: usize); + + /// 请求分配irq + fn dma_request_irq(irq: usize, handler: fn()); +} + #[cfg(test)] mod tests { #[test] From 638b02faea0d1178fdd967f9e356f2e7f496c3f2 Mon Sep 17 00:00:00 2001 From: elliott10 Date: Tue, 18 Feb 2025 14:22:54 +0800 Subject: [PATCH 016/132] version 0.2.0 update readme about crate_interface::impl_interface --- Cargo.lock | 2 +- Cargo.toml | 4 ++-- README.md | 42 ++++++++++++++++++++++-------------------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e4490574..b76982889 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ dependencies = [ [[package]] name = "fxmac_rs" -version = "0.1.0" +version = "0.2.0" dependencies = [ "aarch64-cpu", "crate_interface", diff --git a/Cargo.toml b/Cargo.toml index 0c1b09355..547d89663 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ cargo-features = ["edition2024"] [package] name = "fxmac_rs" authors = ["xiaoluoyuan@163.com"] -version = "0.1.0" +version = "0.2.0" edition = "2024" license = "GPL-2.0" description = "fxmac ethernet driver in Rust on PhytiumPi board." @@ -20,5 +20,5 @@ debug = [] log = "0.4" crate_interface = "0.1.4" -# [target.'cfg(target_arch = "aarch64")'.dependencies] +[target.'cfg(target_arch = "aarch64")'.dependencies] aarch64-cpu = "10" diff --git a/README.md b/README.md index f7b78a123..537af2093 100644 --- a/README.md +++ b/README.md @@ -5,30 +5,31 @@ fxmac ethernet driver in Rust on PhytiumPi board. * Initialize ethernet driver ``` -/// 虚拟地址转换成物理地址 -#[no_mangle] -pub fn virt_to_phys_fxmac(addr: usize) -> usize { -} +pub struct FXmacDriver; -/// 物理地址转换成虚拟地址 -#[no_mangle] -pub fn phys_to_virt_fxmac(addr: usize) -> usize { -} +#[crate_interface::impl_interface] +impl axdriver_net::fxmac::KernelFunc for FXmacDriver{ + /// 虚拟地址转换成物理地址 + fn virt_to_phys(addr: usize) -> usize { + } -/// 使能并注册网卡中断 -#[no_mangle] -pub fn dma_request_irq_fxmac(irq: usize, handler: fn()) { -} + /// 物理地址转换成虚拟地址 + fn phys_to_virt(addr: usize) -> usize { + } -/// 申请页对齐的DMA连续内存页 -/// 返回((cpu virtual address, dma physical address)) -#[no_mangle] -pub fn dma_alloc_coherent_fxmac(pages: usize) -> (usize, usize) { -} + /// 申请页对齐的DMA连续内存页 + /// 返回((cpu virtual address, dma physical address)) + fn dma_alloc_coherent(pages: usize) -> (usize, usize) { + } + + /// 释放DMA内存页 + fn dma_free_coherent(vaddr: usize, pages: usize) { + } -/// 释放DMA内存页 -#[no_mangle] -fn dma_free_coherent_fxmac(vaddr: usize, pages: usize) { + /// 使能并注册网卡中断 + fn dma_request_irq(_irq: usize, _handler: fn()) { + warn!("unimplemented dma_request_irq for fxmax"); + } } let hwaddr: [u8; 6] = [0x55, 0x44, 0x33, 0x22, 0x11, 0x00]; @@ -53,6 +54,7 @@ let recv_packets = FXmacRecvHandler(fxmac_device); ``` cargo build --target=aarch64-unknown-none-softfloat ``` + ## About ethernet PHY: Motorcomm YT8521 From 0dbc3916d5d5a0086916deda0c4c2dd8651c69ce Mon Sep 17 00:00:00 2001 From: Luoyuan Xiao Date: Fri, 28 Feb 2025 14:46:30 +0800 Subject: [PATCH 017/132] pub use fxmac/fxmac_dma functions --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b070cbc6a..7c543d2f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,8 +40,8 @@ mod fxmac_dma; mod fxmac_intr; mod fxmac; -pub use fxmac::{FXmac, xmac_init}; -pub use fxmac_dma::{FXmacInitDma, FXmacLwipPortTx, FXmacRecvHandler}; +pub use fxmac::*; +pub use fxmac_dma::*; pub use fxmac_intr::{FXmacIntrHandler, xmac_intr_handler}; // PHY interface From deeeea6759ae7cfe4dcb43e9d89db1a046b9e50e Mon Sep 17 00:00:00 2001 From: hky1999 Date: Tue, 17 Jun 2025 18:55:02 +0800 Subject: [PATCH 018/132] [wip] Timer prototype (#2) --- Cargo.toml | 1 + src/consts.rs | 22 +- src/hal.rs | 35 ++ src/lib.rs | 42 ++- src/lvt.rs | 38 -- src/regs/apic_base.rs | 58 +++ src/regs/dfr.rs | 38 ++ src/regs/esr.rs | 66 ++++ src/regs/icr.rs | 158 ++++++++ src/regs/lvt/mod.rs | 36 ++ src/regs/mod.rs | 27 +- src/regs/timer/dcr.rs | 42 +++ src/regs/timer/mod.rs | 3 + src/timer.rs | 170 +++++++++ src/utils.rs | 61 ++++ src/vlapic.rs | 810 ++++++++++++++++++++++++++++++++++++++++-- 16 files changed, 1510 insertions(+), 97 deletions(-) create mode 100644 src/hal.rs delete mode 100644 src/lvt.rs create mode 100644 src/regs/apic_base.rs create mode 100644 src/regs/dfr.rs create mode 100644 src/regs/esr.rs create mode 100644 src/regs/icr.rs create mode 100644 src/regs/timer/dcr.rs create mode 100644 src/regs/timer/mod.rs create mode 100644 src/timer.rs create mode 100644 src/utils.rs diff --git a/Cargo.toml b/Cargo.toml index 6381a732b..984d45d52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ description = "x86 Virtual Local APIC" log = "0.4.19" paste = "1.0.15" tock-registers = "0.9.0" +bit = "0.1.1" memory_addr = "0.3" axerrno = "0.1.0" diff --git a/src/consts.rs b/src/consts.rs index 1263628d3..500151526 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -57,7 +57,7 @@ define_index_enum!(ISRIndex); define_index_enum!(TMRIndex); define_index_enum!(IRRIndex); -#[derive(Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ApicRegOffset { /// ID register 0x2. ID, @@ -111,6 +111,9 @@ pub enum ApicRegOffset { TimerCurCount, /// Divide Configuration register (for Timer) 0x3E. TimerDivConf, + /// Self IPI register 0x3F. + /// Available only in x2APIC mode. + SelfIPI, } impl ApicRegOffset { @@ -142,6 +145,7 @@ impl ApicRegOffset { 0x38 => ApicRegOffset::TimerInitCount, 0x39 => ApicRegOffset::TimerCurCount, 0x3E => ApicRegOffset::TimerDivConf, + 0x3F => ApicRegOffset::SelfIPI, _ => panic!("Invalid APIC register offset"), } } @@ -176,19 +180,27 @@ impl core::fmt::Display for ApicRegOffset { ApicRegOffset::TimerInitCount => write!(f, "TimerInitCount"), ApicRegOffset::TimerCurCount => write!(f, "TimerCurCount"), ApicRegOffset::TimerDivConf => write!(f, "TimerDivConf"), + ApicRegOffset::SelfIPI => write!(f, "SelfIPI"), } } } +pub const APIC_LVT_M: u32 = 0x00010000; +pub const APIC_LVT_DS: u32 = 0x00001000; +pub const APIC_LVT_VECTOR: u32 = 0x000000ff; + /// 11.5.1 Local Vector Table /// Figure 11-8. Local Vector Table (LVT) /// - Value After Reset: 0001 0000H -pub const RESET_LVT_REG: u32 = 0x0001_0000; +pub const RESET_LVT_REG: u32 = APIC_LVT_M; /// 11.9 SPURIOUS INTERRUPT /// - Address: FEE0 00F0H /// - Value after reset: 0000 00FFH pub const RESET_SPURIOUS_INTERRUPT_VECTOR: u32 = 0x0000_00FF; +pub const LAPIC_TRIG_LEVEL: bool = true; +pub const LAPIC_TRIG_EDGE: bool = false; + pub mod xapic { use axaddrspace::GuestPhysAddr; @@ -197,6 +209,8 @@ pub mod xapic { pub const DEFAULT_APIC_BASE: usize = 0xFEE0_0000; pub const APIC_MMIO_SIZE: usize = 0x1000; + pub const XAPIC_BROADCAST_DEST_ID: u32 = 0xFF; + pub(crate) const fn xapic_mmio_access_reg_offset(addr: GuestPhysAddr) -> ApicRegOffset { ApicRegOffset::from((addr.as_usize() & (APIC_MMIO_SIZE - 1)) >> 4) } @@ -210,6 +224,10 @@ pub mod x2apic { pub const X2APIC_MSE_REG_BASE: usize = 0x800; pub const X2APIC_MSE_REG_SIZE: usize = 0x100; + /// A destination ID value of FFFF_FFFFH is used for broadcast of interrupts + /// in both logical destination and physical destination modes. + pub const X2APIC_BROADCAST_DEST_ID: u32 = 0xFFFF_FFFF; + pub(crate) const fn x2apic_msr_access_reg(addr: SysRegAddr) -> ApicRegOffset { ApicRegOffset::from(addr.addr() - X2APIC_MSE_REG_BASE) } diff --git a/src/hal.rs b/src/hal.rs new file mode 100644 index 000000000..e97c53943 --- /dev/null +++ b/src/hal.rs @@ -0,0 +1,35 @@ +use axerrno::AxResult; + +/// The interfaces which the underlying software (kernel or hypervisor) must implement. +pub trait AxVMHal: Sized { + /// Current time in nanoseconds. + fn current_time_nanos() -> u64; + + /// Current VM ID. + fn current_vm_id() -> usize; + + /// Current Virtual CPU ID. + fn current_vcpu_id() -> usize; + + /// Current Physical CPU ID. + fn current_pcpu_id() -> usize; + + fn vcpu_num() -> usize; + + fn active_vcpus() -> usize; + + fn vm_apicid2vcpu_id() -> usize; + + /// Get the Physical CPU ID where the specified VCPU of the current VM resides. + /// + /// Returns an error if the VCPU is not found. + fn vcpu_resides_on(vm_id: usize, vcpu_id: usize) -> AxResult; + + /// Inject an IRQ to the specified VCPU. + /// + /// This method should find the physical CPU where the specified VCPU resides and inject the IRQ + /// to it on that physical CPU with [`axvcpu::AxVCpu::inject_interrupt`]. + /// + /// Returns an error if the VCPU is not found. + fn inject_irq_to_vcpu(vm_id: usize, vcpu_id: usize, irq: usize) -> AxResult; +} diff --git a/src/lib.rs b/src/lib.rs index 0beeff8ab..63a871a52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,11 +7,15 @@ extern crate alloc; extern crate log; mod consts; -mod lvt; +mod hal; mod regs; +mod timer; +mod utils; mod vlapic; use alloc::boxed::Box; +use core::cell::UnsafeCell; +use hal::AxVMHal; use axerrno::AxResult; use memory_addr::{AddrRange, PAGE_SIZE_4K}; @@ -30,20 +34,28 @@ struct APICAccessPage([u8; PAGE_SIZE_4K]); static VIRTUAL_APIC_ACCESS_PAGE: APICAccessPage = APICAccessPage([0; PAGE_SIZE_4K]); /// A emulated local APIC device. -pub struct EmulatedLocalApic { - vlapic_regs: VirtualApicRegs, +pub struct EmulatedLocalApic { + vlapic_regs: UnsafeCell>, } -impl EmulatedLocalApic { +impl EmulatedLocalApic { /// Create a new `EmulatedLocalApic`. - pub fn new() -> Self { + pub fn new(vcpu_id: u32) -> Self { EmulatedLocalApic { - vlapic_regs: VirtualApicRegs::new(), + vlapic_regs: UnsafeCell::new(VirtualApicRegs::new(vcpu_id)), } } + + fn get_vlapic_regs(&self) -> &VirtualApicRegs { + unsafe { &*self.vlapic_regs.get() } + } + + fn get_mut_vlapic_regs(&self) -> &mut VirtualApicRegs { + unsafe { &mut *self.vlapic_regs.get() } + } } -impl EmulatedLocalApic { +impl EmulatedLocalApic { /// APIC-access address (64 bits). /// This field contains the physical address of the 4-KByte APIC-access page. /// If the “virtualize APIC accesses” VM-execution control is 1, @@ -60,11 +72,11 @@ impl EmulatedLocalApic { /// The processor uses the virtual-APIC page to virtualize certain accesses to APIC registers and to manage virtual interrupts; /// see Chapter 30. pub fn virtual_apic_page_addr(&self) -> HostPhysAddr { - self.vlapic_regs.virtual_apic_page_addr() + self.get_vlapic_regs().virtual_apic_page_addr() } } -impl BaseDeviceOps> for EmulatedLocalApic { +impl BaseDeviceOps> for EmulatedLocalApic { fn emu_type(&self) -> EmuDeviceType { EmuDeviceType::EmuDeviceTInterruptController } @@ -88,7 +100,7 @@ impl BaseDeviceOps> for EmulatedLocalApic BaseDeviceOps> for EmulatedLocalApic) { @@ -111,7 +124,7 @@ impl BaseDeviceOps> for EmulatedLocalApic BaseDeviceOps for EmulatedLocalApic { +impl BaseDeviceOps for EmulatedLocalApic { fn emu_type(&self) -> EmuDeviceType { EmuDeviceType::EmuDeviceTInterruptController } @@ -135,7 +148,7 @@ impl BaseDeviceOps for EmulatedLocalApic { addr, width, context.vcpu_id ); let reg_off = x2apic_msr_access_reg(addr); - self.vlapic_regs.handle_read(reg_off, width, context) + self.get_vlapic_regs().handle_read(reg_off, width, context) } fn handle_write( @@ -150,7 +163,8 @@ impl BaseDeviceOps for EmulatedLocalApic { addr, width, val, context.vcpu_id ); let reg_off = x2apic_msr_access_reg(addr); - self.vlapic_regs.handle_write(reg_off, width, context) + self.get_mut_vlapic_regs() + .handle_write(reg_off, val, width, context) } fn set_interrupt_injector(&mut self, _injector: Box) { diff --git a/src/lvt.rs b/src/lvt.rs deleted file mode 100644 index ad96733db..000000000 --- a/src/lvt.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Local Vector Table - -use crate::consts::RESET_LVT_REG; -use crate::regs::lvt::{ - LvtCmciRegisterLocal, LvtErrorRegisterLocal, LvtLint0RegisterLocal, LvtLint1RegisterLocal, - LvtPerformanceCounterRegisterLocal, LvtThermalMonitorRegisterLocal, LvtTimerRegisterLocal, -}; - -pub struct LocalVectorTable { - /// LVT CMCI Register (FEE0 02F0H) - pub lvt_cmci: LvtCmciRegisterLocal, - /// LVT Timer Register (FEE0 0320H) - pub lvt_timer: LvtTimerRegisterLocal, - /// LVT Thermal Monitor Register (FEE0 0330H) - pub lvt_thermal: LvtThermalMonitorRegisterLocal, - /// LVT Performance Counter Register (FEE0 0340H) - pub lvt_perf_count: LvtPerformanceCounterRegisterLocal, - /// LVT LINT0 Register (FEE0 0350H) - pub lvt_lint0: LvtLint0RegisterLocal, - /// LVT LINT1 Register (FEE0 0360H) - pub lvt_lint1: LvtLint1RegisterLocal, - /// LVT Error register 0x37. - pub lvt_err: LvtErrorRegisterLocal, -} - -impl Default for LocalVectorTable { - fn default() -> Self { - LocalVectorTable { - lvt_cmci: LvtCmciRegisterLocal::new(RESET_LVT_REG), - lvt_timer: LvtTimerRegisterLocal::new(RESET_LVT_REG), - lvt_thermal: LvtThermalMonitorRegisterLocal::new(RESET_LVT_REG), - lvt_perf_count: LvtPerformanceCounterRegisterLocal::new(RESET_LVT_REG), - lvt_lint0: LvtLint0RegisterLocal::new(RESET_LVT_REG), - lvt_lint1: LvtLint1RegisterLocal::new(RESET_LVT_REG), - lvt_err: LvtErrorRegisterLocal::new(RESET_LVT_REG), - } - } -} diff --git a/src/regs/apic_base.rs b/src/regs/apic_base.rs new file mode 100644 index 000000000..18c389ee8 --- /dev/null +++ b/src/regs/apic_base.rs @@ -0,0 +1,58 @@ +//! 11.4.4 Local APIC Status and Location +//! The status and location of the local APIC are contained in the IA32_APIC_BASE MSR (see Figure 11-5). +//! Figure 11-26. IA32_APIC_BASE MSR Supporting x2APIC +//! Processor support for x2APIC mode can be detected by executing CPUID with EAX=1 and then checking ECX, bit 21 ECX. +//! If CPUID.(EAX=1):ECX.21 is set , the processor supports the x2APIC capability and can be placed into the x2APIC mode. +//! System software can place the local APIC in the x2APIC mode by setting the x2APIC mode enable bit (bit 10) in the IA32_APIC_BASE MSR at MSR address 01BH. + +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; + +register_bitfields! { + u64, + pub APIC_BASE [ + /// Reserved2 + Reserved2 OFFSET(36) NUMBITS(28) [], + /// APIC Base field, bits 12 through 35 + /// Specifies the base address of the APIC registers. + /// This 24-bit value is extended by 12 bits at the low end to form the base address. + /// This automatically aligns the address on a 4-KByte boundary. + /// Following a power-up or reset, the field is set to FEE0 0000H. + APIC_BASE OFFSET(12) NUMBITS(24) [], + /// APIC Global Enable flag, bit 11 + /// Enables or disables the local APIC (see Section 11.4.3, “Enabling or Disabling the Local APIC”). + /// This flag is available in the Pentium 4, Intel Xeon, and P6 family processors. + /// It is not guaranteed to be available or available at the same location in future Intel 64 or IA-32 processors. + /// EN—xAPIC global enable/disable + /// - 0: xAPIC disabled + /// - 1: xAPIC enabled + XAPIC_ENABLED OFFSET(11) NUMBITS(1) [], + /// EXTD—Enable x2APIC mode + /// - 0: xAPIC mode + /// - 1: x2APIC mode + X2APIC_Enabled OFFSET(10) NUMBITS(1) [], + /// Reserved1 + Reserved1 OFFSET(9) NUMBITS(1) [], + /// BSP flag, bit 8 + /// Indicates if the processor is the bootstrap processor (BSP). + /// See Section 9.4, “MultipleProcessor (MP) Initialization.” + /// Following a power-up or reset, this flag is set to 1 for the processor selected as the BSP and set to 0 for the remaining processors (APs). + /// - 0: Processor is not BSP + /// - 1: Processor is BSP + BSP OFFSET(8) NUMBITS(1) [], + /// Reserved0 + Reserved0 OFFSET(0) NUMBITS(8) [], + ] +} + +/// IA32_APIC_BASE MSR (Model Specific Register) supporting x2APIC. +/// - Address: 1B0H +/// - Value after reset: FEE_0000_0000H +/// Table 11-5, “x2APIC operating mode configurations” describe the possible combinations of the enable bit (EN - bit 11) +/// and the extended mode bit (EXTD - bit 10) in the IA32_APIC_BASE MSR. +/// (xAPIC global enable (IA32_APIC_BASE[11]),x2APIC enable (IA32_APIC_BASE[10])) = Description +/// - (0, 0) = local APIC is disabled +/// - (0, 1) = Invalid +/// - (1, 0) = local APIC is enabled in xAPIC mode +/// - (1, 1) = local APIC is enabled in x2APIC mode +pub type ApicBaseRegisterMsr = LocalRegisterCopy; diff --git a/src/regs/dfr.rs b/src/regs/dfr.rs new file mode 100644 index 000000000..b942fafee --- /dev/null +++ b/src/regs/dfr.rs @@ -0,0 +1,38 @@ +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub DESTINATION_FORMAT [ + /// Model + Model OFFSET(28) NUMBITS(4) [ + /// Flat model + Flat = 0b1111, + /// Cluster model + Cluster = 0b0000 + ], + /// Reserved (All 1s) + ReservedALL1 OFFSET(0) NUMBITS(28) [], + ] +} + +/// Destination Format Register using MMIO. +/// - Address: FEE0 00E0H +/// - Value after reset: FFFF FFFFH +/// +/// The 4-bit model field in this register selects one of two models (flat or cluster) that can be used to interpret the MDA when using logical destination mode. +/// **Flat Model** — This model is selected by programming DFR bits 28 through 31 to 1111. +/// Here, a unique logical APIC ID can be established for up to 8 local APICs by setting a different bit in the logical APIC ID field of the LDR for each local APIC. +/// A group of local APICs can then be selected by setting one or more bits in the MDA. +/// +/// **Cluster Model** — This model is selected by programming DFR bits 28 through 31 to 0000. +/// This model supports two basic destination schemes: flat cluster and hierarchical cluster. +pub type DestinationFormatRegisterMmio = ReadWrite; + +/// A read-write copy of Destination Format Register (FEE0 00E0H). +/// +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type DestinationFormatRegisterLocal = LocalRegisterCopy; diff --git a/src/regs/esr.rs b/src/regs/esr.rs new file mode 100644 index 000000000..ab86a1142 --- /dev/null +++ b/src/regs/esr.rs @@ -0,0 +1,66 @@ +//! Figure 11-9. Error Status Register (ESR) +//! 11.5.3 Error Handling +//! The local APIC records errors detected during interrupt handling in the error status register (ESR). + +use tock_registers::LocalRegisterCopy; +use tock_registers::fields::FieldValue; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub ERROR_STATUS [ + /// Reserved + Reserved2 OFFSET(8) NUMBITS(24) [], + /// Bit 7: Illegal Register Address + /// Set when the local APIC is in xAPIC mode and software attempts to access a register that is reserved in the processor's local-APIC register-address space; see Table 10-1. + /// (The local-APIC register-address space comprises the 4 KBytes at the physical address specified in the IA32_APIC_BASE MSR.) + /// Used only on Intel Core, Intel Atom, Pentium 4, Intel Xeon, and P6 family processors. + IllegalRegisterAddress OFFSET(7) NUMBITS(1) [], + /// Bit 6: Receive Illegal Vector. + /// Set when the local APIC detects an illegal vector (one in the range 0 to 15) in an interrupt message it receives or in an interrupt generated locally from the local vector table or via a self IPI. + /// Such interrupts are not delivered to the processor; the local APIC will never set an IRR bit in the range 0 to 15. + ReceiveIllegalVector OFFSET(6) NUMBITS(1) [], + /// Bit 5: Send Illegal Vector. + /// Set when the local APIC detects an illegal vector (one in the range 0 to 15) in the message that it is sending. + /// This occurs as the result of a write to the ICR (in both xAPIC and x2APIC modes) or to SELF IPI register (x2APIC mode only) with an illegal vector. + /// If the local APIC does not support the sending of lowest-priority IPIs and software writes the ICR to send a lowest-priority IPI with an illegal vector, the local APIC sets only the “redirectable IPI” error bit. + /// The interrupt is not processed and hence the “Send Illegal Vector” bit is not set in the ESR. + SendIllegalVector OFFSET(5) NUMBITS(1) [], + /// Bit 4: Redirectable IPI. + /// Set when the local APIC detects an attempt to send an IPI with the lowest-priority delivery mode and the local APIC does not support the sending of such IPIs. + /// This bit is used on some Intel Core and Intel Xeon processors. ] + /// As noted in Section 11.6.2, the ability of a processor to send a lowest-priority IPI is model-specific and should be avoided. + RedirectableIPI OFFSET(4) NUMBITS(1) [], + /// Bit 3: Receive Accept Error. + /// Set when the local APIC detects that the message it received was not accepted by any APIC on the APIC bus, including itself. + /// Used only on P6 family and Pentium processors. + ReceiveAcceptError OFFSET(3) NUMBITS(1) [], + /// Bit 2: Send Accept Error. + /// Set when the local APIC detects that a message it sent was not accepted by any APIC on the APIC bus. + /// Used only on P6 family and Pentium processors. + SendAcceptError OFFSET(2) NUMBITS(1) [], + /// Bit 1: Receive Checksum Error. + /// Set when the local APIC detects a checksum error for a message that it received on the APIC bus. + /// Used only on P6 family and Pentium processors. + ReceiveChecksumError OFFSET(1) NUMBITS(1) [], + /// Bit 0: Send Checksum Error. + /// Set when the local APIC detects a checksum error for a message that it sent on the APIC bus. + /// Used only on P6 family and Pentium processors. + SendChecksumError OFFSET(0) NUMBITS(1) [], + ] +} + +/// Error Status Register (ESR) using MMIO. +/// The local APIC records errors detected during interrupt handling in the error status register (ESR). +/// - Address: FEE0 0280H +/// - Value after reset: 0H +pub type ErrorStatusRegisterMmio = ReadWrite; + +/// A read-write copy of Error Status Register (ESR). +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type ErrorStatusRegisterLocal = LocalRegisterCopy; + +pub type ErrorStatusRegisterValue = FieldValue; diff --git a/src/regs/icr.rs b/src/regs/icr.rs new file mode 100644 index 000000000..55a8f47de --- /dev/null +++ b/src/regs/icr.rs @@ -0,0 +1,158 @@ +//! 11.6.1 Interrupt Command Register (ICR) +//! The interrupt command register (ICR) is a 64-bit1 local APIC register (see Figure 11-12) +//! that allows software running on the processor to specify and send interprocessor interrupts (IPIs) to other processors in the system. + +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub INTERRUPT_COMMAND_LOW [ + /// Reserved + Reserved2 OFFSET(20) NUMBITS(12) [], + /// Destination Shorthand + /// Indicates whether a shorthand notation is used to specify the destination of the interrupt and, if so, which shorthand is used. Destination shorthands are used in place of the 8-bit destination field, and can be sent by software using a single write to the low doubleword of the ICR. Shorthands are defined for the following cases: software self interrupt, IPIs to all processors in the system including the sender, IPIs to all processors in the system excluding the sender. + /// - 00: (No Shorthand) + /// The destination is specified in the destination field. + /// - 01: (Self) + /// The issuing APIC is the one and only destination of the IPI. This destination shorthand allows software to interrupt the processor on which it is executing. An APIC implementation is free to deliver the self-interrupt message internally or to issue the message to the bus and “snoop” it as with any other IPI message. + /// - 10: (All Including Self) + /// The IPI is sent to all processors in the system including the processor sending the IPI. The APIC will broadcast an IPI message with the destination field set to FH for Pentium and P6 family processors and to FFH for Pentium 4 and Intel Xeon processors. + /// - 11: (All Excluding Self) + /// The IPI is sent to all processors in a system with the exception of the processor sending the IPI. The APIC broadcasts a message with the physical destination mode and destination field set to FH for Pentium and P6 family processors and to FFH for Pentium 4 and Intel Xeon processors. Support for this destination shorthand in conjunction with the lowest-priority delivery mode is model specific. For Pentium 4 and Intel Xeon processors, when this shorthand is used together with lowest priority delivery mode, the IPI may be redirected back to the issuing processor. + DestinationShorthand OFFSET(18) NUMBITS(2) [ + /// No Shorthand + NoShorthand = 0b00, + /// Self + SELF = 0b01, + /// All Including Self + AllIncludingSelf = 0b10, + /// All Excluding Self + AllExcludingSelf = 0b11 + ], + /// Reserved + Reserved1 OFFSET(16) NUMBITS(2) [], + /// Trigger Mode + /// Selects the trigger mode when using the INIT level de-assert delivery mode: + /// edge (0) or level (1). + /// It is ignored for all other delivery modes. + /// (This flag has no meaning in Pentium 4 and Intel Xeon processors, and will always be issued as a 0.) + TriggerMode OFFSET(15) NUMBITS(1) [ + /// Edge + Edge = 0, + /// Level + Level = 1 + ], + /// Level + /// For the INIT level de-assert delivery mode this flag must be set to 0; + /// for all other delivery modes it must be set to 1. + /// (This flag has no meaning in Pentium 4 and Intel Xeon processors, and will always be issued as a 1.) + Level OFFSET(14) NUMBITS(1) [ + /// De-assert + DeAssert = 0, + /// Assert + Assert = 1 + ], + /// Reserved + Reserved0 OFFSET(13) NUMBITS(1) [], + /// Delivery Status (Read Only) Indicates the IPI delivery status, as follows: + /// - 0 (Idle) Indicates that this local APIC has completed sending any previous IPIs. + /// - 1 (Send Pending) Indicates that this local APIC has not completed sending the last IPI. + DeliveryStatus OFFSET(12) NUMBITS(1) [ + /// Idle + Idle = 0, + /// Send Pending + SendPending = 1 + ], + /// Destination Mode Selects either physical (0) or logical (1) destination mode (see Section 11.6.2, “Determining IPI Destination”). + DestinationMode OFFSET(11) NUMBITS(1) [ + /// Physical + Physical = 0, + /// Logical + Logical = 1 + ], + /// Delivery Mode Specifies the type of IPI to be sent. + /// This field is also know as the IPI message type field. + /// - 000 (Fixed) + /// Delivers the interrupt specified in the vector field to the target processor or processors. + /// - 001 (Lowest Priority) + /// Same as fixed mode, except that the interrupt is delivered to the processor executing at the lowest priority among the set of processors specified in the destination field. The ability for a processor to send a lowest priority IPI is model specific and should be avoided by BIOS and operating system software. + /// - 010 (SMI) + /// Delivers an SMI interrupt to the target processor or processors. The vector field must be programmed to 00H for future compatibility. + /// - 011 (Reserved) + /// - 100 (NMI) + /// Delivers an NMI interrupt to the target processor or processors. The vector information is ignored. + /// - 101 (INIT) + /// Delivers an INIT request to the target processor or processors, which causes them to perform an INIT. + /// As a result of this IPI message, all the target processors perform an INIT. + /// The vector field must be programmed to 00H for future compatibility. + /// - 101 (INIT Level De-assert) + /// (Not supported in the Pentium 4 and Intel Xeon processors.) + /// Sends a synchronization message to all the local APICs in the system to set their arbitration IDs (stored in their Arb ID registers) to the values of their APIC IDs (see Section 11.7, “System and APIC Bus Arbitration”). + /// For this delivery mode, the level flag must be set to 0 and trigger mode flag to 1. + /// This IPI is sent to all processors, regardless of the value in the destination field or the destination shorthand field; + /// however, software should specify the “all including self” shorthand. + /// - 110 (Start-Up) + /// Sends a special “start-up” IPI (called a SIPI) to the target processor or processors. + /// The vector typically points to a start-up routine that is part of the BIOS boot-strap code + /// (see Section 9.4, “Multiple-Processor (MP) Initialization”). + /// IPIs sent with this delivery mode are not automatically retried if the source APIC is unable to deliver it. + /// It is up to the software to determine if the SIPI was not successfully delivered and to reissue the SIPI if necessary. + /// - 111 (Reserved) + DeliveryMode OFFSET(8) NUMBITS(3) [ + /// Fixed + Fixed = 0b000, + /// Lowest Priority + LowestPriority = 0b001, + /// SMI + SMI = 0b010, + /// Reserved + Reserved011 = 0b011, + /// NMI + NMI = 0b100, + /// INIT + INIT = 0b101, + /// Start-Up + StartUp = 0b110, + /// Start-Up + Reserved111 = 0b111 + ], + /// Vector The vector number of the interrupt being sent. + Vector OFFSET(0) NUMBITS(8) [] + ] +} + +register_bitfields! { + u32, + pub INTERRUPT_COMMAND_HIGH [ + /// Destination [56:63] + /// Specifies the target processor or processors. + /// This field is only used when the destination shorthand field is set to 00B. + /// If the destination mode is set to physical, then bits 56 through 59 contain the APIC ID of the target processor for Pentium and P6 family processors and bits 56 through 63 contain the APIC ID of the target processor the for Pentium 4 and Intel Xeon processors. + /// If the destination mode is set to logical, the interpretation of the 8-bit destination field depends on the settings of the DFR and LDR registers of the local APICs in all the processors in the system + /// (see Section 11.6.2, “Determining IPI Destination”). + /// + /// 11.12.9 ICR Operation in x2APIC Mode + /// The destination ID field is expanded to 32 bits in x2APIC mode. + Destination OFFSET(24) NUMBITS(8) [], + /// Reserved + Reserved OFFSET(0) NUMBITS(24) [] + ] +} + +/// Interrupt Command Register (ICR) LOW using MMIO. +/// - Address: FEE0 0300H (0 - 31) +/// - Value after Reset: 0H +pub type InterruptCommandRegisterLowMmio = ReadWrite; + +/// A read-write copy of Interrupt Command Register (ICR) LOW. +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type InterruptCommandRegisterLowLocal = LocalRegisterCopy; + +/// Interrupt Command Register (ICR) HIGH using MMIO. +/// - Address: FEE0 0310H (32 - 63) +/// - Value after Reset: 0H +pub type InterruptCommandRegisterHighMmio = ReadWrite; diff --git a/src/regs/lvt/mod.rs b/src/regs/lvt/mod.rs index 1f7c88915..ccbb4e375 100644 --- a/src/regs/lvt/mod.rs +++ b/src/regs/lvt/mod.rs @@ -1,3 +1,5 @@ +//! Local Vector Table + mod cmci; mod error; mod lint0; @@ -13,3 +15,37 @@ pub use lint1::*; pub use perfmon::*; pub use thermal::*; pub use timer::*; + +use crate::consts::RESET_LVT_REG; + +/// A read-write copy of LVT registers. +pub struct LocalVectorTable { + /// LVT CMCI Register (FEE0 02F0H) + pub lvt_cmci: LvtCmciRegisterLocal, + /// LVT Timer Register (FEE0 0320H) + pub lvt_timer: LvtTimerRegisterLocal, + /// LVT Thermal Monitor Register (FEE0 0330H) + pub lvt_thermal: LvtThermalMonitorRegisterLocal, + /// LVT Performance Counter Register (FEE0 0340H) + pub lvt_perf_count: LvtPerformanceCounterRegisterLocal, + /// LVT LINT0 Register (FEE0 0350H) + pub lvt_lint0: LvtLint0RegisterLocal, + /// LVT LINT1 Register (FEE0 0360H) + pub lvt_lint1: LvtLint1RegisterLocal, + /// LVT Error register 0x37. + pub lvt_err: LvtErrorRegisterLocal, +} + +impl Default for LocalVectorTable { + fn default() -> Self { + LocalVectorTable { + lvt_cmci: LvtCmciRegisterLocal::new(RESET_LVT_REG), + lvt_timer: LvtTimerRegisterLocal::new(RESET_LVT_REG), + lvt_thermal: LvtThermalMonitorRegisterLocal::new(RESET_LVT_REG), + lvt_perf_count: LvtPerformanceCounterRegisterLocal::new(RESET_LVT_REG), + lvt_lint0: LvtLint0RegisterLocal::new(RESET_LVT_REG), + lvt_lint1: LvtLint1RegisterLocal::new(RESET_LVT_REG), + lvt_err: LvtErrorRegisterLocal::new(RESET_LVT_REG), + } + } +} diff --git a/src/regs/mod.rs b/src/regs/mod.rs index 80037b4f3..ef3448d09 100644 --- a/src/regs/mod.rs +++ b/src/regs/mod.rs @@ -1,5 +1,16 @@ pub mod lvt; +pub mod timer; + +mod apic_base; +mod dfr; +mod esr; +mod icr; mod svr; + +pub use apic_base::*; +pub use dfr::*; +pub use esr::*; +pub use icr::*; pub use svr::*; use tock_registers::register_structs; @@ -9,6 +20,7 @@ use lvt::{ LvtCmciRegisterMmio, LvtErrorRegisterMmio, LvtLint0RegisterMmio, LvtLint1RegisterMmio, LvtPerformanceCounterRegisterMmio, LvtThermalMonitorRegisterMmio, LvtTimerRegisterMmio, }; +use timer::DivideConfigurationRegisterMmio; register_structs! { #[allow(non_snake_case)] @@ -27,7 +39,7 @@ register_structs! { (0x90 => pub APR: ReadOnly), (0x94 => _reserved4), /// Virtual processor-priority register (VPPR): the 32-bit field located at offset 0A0H on the virtual-APIC page. - (0xA0 => pub PPR: ReadOnly), + (0xA0 => pub PPR: ReadWrite), (0xA4 => _reserved5), /// Virtual end-of-interrupt register (VEOI): the 32-bit field located at offset 0B0H on the virtual-APIC page. (0xB0 => pub EOI: WriteOnly), @@ -39,7 +51,7 @@ register_structs! { (0xD0 => pub LDR: ReadWrite), (0xD4 => _reserved8), /// Virtual Destination Format Register (DFR): the 32-bit field located at offset 0E0H on the virtual-APIC page. - (0xE0 => pub DFR: ReadWrite), + (0xE0 => pub DFR: DestinationFormatRegisterMmio), (0xE4 => _reserved9), /// Virtual Spurious Interrupt Vector Register (SVR): the 32-bit field located at offset 0F0H on the virtual-APIC page. (0xF0 => pub SVR: SpuriousInterruptVectorRegisterMmio), @@ -47,7 +59,7 @@ register_structs! { /// Virtual interrupt-service register (VISR): /// the 256-bit value comprising eight non-contiguous 32-bit fields at offsets /// 100H, 110H, 120H, 130H, 140H, 150H, 160H, and 170H on the virtual-APIC page. - (0x100 => pub ISR: [ReadOnly; 8]), + (0x100 => pub ISR: [ReadWrite; 8]), /// Virtual trigger-mode register (VTMR): /// the 256-bit value comprising eight non-contiguous 32-bit fields at offsets /// 180H, 190H, 1A0H, 1B0H, 1C0H, 1D0H, 1E0H, and 1F0H on the virtual-APIC page. @@ -59,15 +71,15 @@ register_structs! { /// The processor uses only the low 4 bytes of each of the 16-Byte fields at offsets 200H, 210H, 220H, 230H, 240H, 250H, 260H, and 270H. (0x200 => pub IRR: [ReadOnly; 8]), /// Virtual error-status register (VESR): the 32-bit field located at offset 280H on the virtual-APIC page. - (0x280 => pub ESR: ReadWrite), + (0x280 => pub ESR: ErrorStatusRegisterMmio), (0x284 => _reserved11), /// Virtual LVT Corrected Machine Check Interrupt (CMCI) Register (0x2F0 => pub LVT_CMCI: LvtCmciRegisterMmio), (0x2F4 => _reserved12), /// Virtual Interrupt Command Register (ICR): the 64-bit field located at offset 300H on the virtual-APIC page. - (0x300 => pub ICR_LO: ReadWrite), + (0x300 => pub ICR_LO: InterruptCommandRegisterLowMmio), (0x304 => _reserved13), - (0x310 => pub ICR_HI: ReadWrite), + (0x310 => pub ICR_HI: InterruptCommandRegisterHighMmio), (0x314 => _reserved14), /// Virtual LVT Timer Register: the 32-bit field located at offset 320H on the virtual-APIC page. (0x320 => pub LVT_TIMER: LvtTimerRegisterMmio), @@ -94,9 +106,10 @@ register_structs! { (0x390 => pub CCR_TIMER: ReadOnly), (0x394 => _reserved22), /// Virtual Divide Configuration Register (for Timer): the 32-bit field located at offset 3E0H on the virtual-APIC page. - (0x3E0 => pub DCR_TIMER: ReadWrite), + (0x3E0 => pub DCR_TIMER: DivideConfigurationRegisterMmio), (0x3E4 => _reserved23), /// Virtual SELF IPI Register: the 32-bit field located at offset 3F0H on the virtual-APIC page. + /// Available only in x2APIC mode. (0x3F0 => pub SELF_IPI: WriteOnly), (0x3F4 => _reserved24), (0x1000 => @END), diff --git a/src/regs/timer/dcr.rs b/src/regs/timer/dcr.rs new file mode 100644 index 000000000..f96913e6d --- /dev/null +++ b/src/regs/timer/dcr.rs @@ -0,0 +1,42 @@ +use tock_registers::LocalRegisterCopy; +use tock_registers::register_bitfields; +use tock_registers::registers::ReadWrite; + +register_bitfields! { + u32, + pub DCR_TIMER [ + /// Reserved + Reserved OFFSET(4) NUMBITS(28) [], + /// Divide Value (bits 0, 1, and 3) + /// 000: Divide by 2 + /// 001: Divide by 4 + /// 010: Divide by 8 + /// 011: Divide by 16 + /// 100: Divide by 32 + /// 101: Divide by 64 + /// 110: Divide by 128 + /// 111: Divide by 1 + DivideValue OFFSET(0) NUMBITS(4) [ + DivideBy2 = 0b0000, + DivideBy4 = 0b0001, + DivideBy8 = 0b0010, + DivideBy16 = 0b0101, + DivideBy32 = 0b1000, + DivideBy64 = 0b1001, + DivideBy128 = 0b1010, + DivideBy1 = 0b1011 + ] + ] +} + +/// Divide Configuration Register (FEE0 03E0H) using MMIO. +/// - Address: FEE0 03E0H +/// - Value after reset: 0H +pub type DivideConfigurationRegisterMmio = ReadWrite; + +/// A read-write copy of the Divide Configuration Register (FEE0 03E0H). +/// +/// This behaves very similarly to a MMIO read-write register, but instead of doing a +/// volatile read to MMIO to get the value for each function call, a copy of the +/// register contents are stored locally in memory. +pub type DivideConfigurationRegisterLocal = LocalRegisterCopy; diff --git a/src/regs/timer/mod.rs b/src/regs/timer/mod.rs new file mode 100644 index 000000000..2e59ac0c6 --- /dev/null +++ b/src/regs/timer/mod.rs @@ -0,0 +1,3 @@ +mod dcr; + +pub use dcr::*; diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 000000000..d29f31570 --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,170 @@ +use axerrno::AxResult; + +/// Local APIC timer modes. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u8)] +#[allow(dead_code)] +pub enum TimerMode { + /// Timer only fires once. + OneShot = 0b00, + /// Timer fires periodically. + Periodic = 0b01, + /// Timer fires at an absolute time. + TscDeadline = 0b10, +} + +/// A virtual local APIC timer. (SDM Vol. 3C, Section 11.5.4) +pub struct ApicTimer { + lvt_timer_bits: u32, + divide_shift: u8, + initial_count: u32, + last_start_cycle: u64, + deadline_ns: u64, + timer_mode: TimerMode, +} + +impl ApicTimer { + pub(crate) const fn new() -> Self { + Self { + lvt_timer_bits: 0x1_0000, // masked + divide_shift: 0, + initial_count: 0, + last_start_cycle: 0, + deadline_ns: 0, + timer_mode: TimerMode::OneShot, + } + } + + // /// Check if an interrupt generated. if yes, update it's states. + // pub fn check_interrupt(&mut self) -> bool { + // if self.deadline_ns == 0 { + // false + // } else if H::current_time_nanos() >= self.deadline_ns { + // if self.is_periodic() { + // self.deadline_ns += self.interval_ns(); + // } else { + // self.deadline_ns = 0; + // } + // !self.is_masked() + // } else { + // false + // } + // } + + /// Gets the timer mode. + pub fn timer_mode(&self) -> TimerMode { + self.timer_mode + } + + /// Update the timer mode if it is different from the current mode. + pub fn update_timer_mode(&mut self, mode: TimerMode) -> AxResult { + if self.timer_mode != mode { + self.timer_mode = mode; + unimplemented!("del timer and update timer") + } + + Ok(()) + } + + pub fn start_timer(&mut self) -> AxResult { + unimplemented!("start timer") + } + + pub fn delete_timer(&mut self) -> AxResult { + self.initial_count = 0; + self.deadline_ns = 0; + unimplemented!("del timer") + } + + /// Whether the timer interrupt is masked. + pub const fn is_masked(&self) -> bool { + self.lvt_timer_bits & (1 << 16) != 0 + } + + /// Whether the timer mode is periodic. + pub const fn is_periodic(&self) -> bool { + let timer_mode = (self.lvt_timer_bits >> 17) & 0b11; + timer_mode == TimerMode::Periodic as _ + } + + /// The timer interrupt vector number. + pub const fn vector(&self) -> u8 { + (self.lvt_timer_bits & 0xff) as u8 + } + + /// LVT Timer Register. (SDM Vol. 3A, Section 10.5.1, Figure 10-8) + pub const fn lvt_timer(&self) -> u32 { + self.lvt_timer_bits + } + + /// Divide Configuration Register. (SDM Vol. 3A, Section 10.5.4, Figure 10-10) + pub const fn divide(&self) -> u32 { + let dcr = self.divide_shift.wrapping_sub(1) as u32 & 0b111; + (dcr & 0b11) | ((dcr & 0b100) << 1) + } + + /// Initial Count Register. + pub const fn initial_count(&self) -> u32 { + self.initial_count + } + + /// Current Count Register. + pub fn current_counter(&self) -> u32 { + if self.initial_count == 0 || self.timer_mode == TimerMode::TscDeadline { + return 0; + } + + // Todo: get current cycles + let current_cycles = 0; + + let elapsed_cycles = (current_cycles - self.last_start_cycle) >> self.divide_shift; + match self.timer_mode { + TimerMode::OneShot => self.initial_count - elapsed_cycles as u32, + TimerMode::Periodic => { + self.initial_count - (elapsed_cycles % self.initial_count as u64) as u32 + } + TimerMode::TscDeadline => 0, + } + } + + // /// Set LVT Timer Register. + // pub fn set_lvt_timer(&mut self, bits: u32) -> RvmResult { + // let timer_mode = bits.get_bits(17..19); + // if timer_mode == TimerMode::TscDeadline as _ { + // return rvm_err!(Unsupported); // TSC deadline mode was not supported + // } else if timer_mode == 0b11 { + // return rvm_err!(InvalidParam); // reserved + // } + // self.lvt_timer_bits = bits; + // self.start_timer(); + // Ok(()) + // } + + // /// Set Initial Count Register. + // pub fn set_initial_count(&mut self, initial: u32) -> RvmResult { + // self.initial_count = initial; + // self.start_timer(); + // Ok(()) + // } + + // /// Set Divide Configuration Register. + // pub fn set_divide(&mut self, dcr: u32) -> RvmResult { + // let shift = (dcr & 0b11) | ((dcr & 0b1000) >> 1); + // self.divide_shift = (shift + 1) as u8 & 0b111; + // self.start_timer(); + // Ok(()) + // } + + // const fn interval_ns(&self) -> u64 { + // (self.initial_count as u64 * APIC_CYCLE_NANOS) << self.divide_shift + // } + + // fn start_timer(&mut self) { + // if self.initial_count != 0 { + // self.last_start_cycle = H::current_time_nanos(); + // self.deadline_ns = self.last_start_cycle + self.interval_ns(); + // } else { + // self.deadline_ns = 0; + // } + // } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 000000000..13c48dd94 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,61 @@ +/// Find the last (most significant) bit set in a 32-bit value. +/// +/// Bits are numbered starting at 0 (the least significant bit). +/// A return value of `INVALID_BIT_INDEX` indicates that the input value was zero, +/// and no bits are set. +/// +/// # Parameters +/// - `value`: A `u32` input value. +/// +/// # Returns +/// - Zero-based bit index of the most significant bit set, or `INVALID_BIT_INDEX` if `value` is zero. +pub fn fls32(value: u32) -> u16 { + const INVALID_BIT_INDEX: u16 = 0xFFFF; // Define invalid bit index for zero input + if value == 0 { + return INVALID_BIT_INDEX; + } + 31 - value.leading_zeros() as u16 +} + +#[cfg(test)] +mod tests { + use super::*; + + const INVALID_BIT_INDEX: u16 = 0xFFFF; + + #[test] + fn test_fls32() { + // Test case: input is 0, no bits set + assert_eq!(fls32(0x0), INVALID_BIT_INDEX); + + // Test case: input is 1 (0b00000001), bit 0 is set + assert_eq!(fls32(0x01), 0); + + // Test case: input is 128 (0b10000000), bit 7 is set + assert_eq!(fls32(0x80), 7); + + // Test case: input is 0x80000001, bit 31 is the most significant bit set + assert_eq!(fls32(0x80000001), 31); + + // Test case: input is 0xFFFFFFFF, bit 31 is the most significant bit set + assert_eq!(fls32(0xFFFFFFFF), 31); + + // Test case: input is 0x7FFFFFFF, bit 30 is the most significant bit set + assert_eq!(fls32(0x7FFFFFFF), 30); + } + + #[test] + fn test_fls32_edge_cases() { + // Test case: input is 0x00000010, bit 4 is set + assert_eq!(fls32(0x10), 4); + + // Test case: input is 0x00001000, bit 12 is set + assert_eq!(fls32(0x1000), 12); + + // Test case: input is the maximum value (0xFFFFFFFF), bit 31 is set + assert_eq!(fls32(u32::MAX), 31); + + // Test case: input is 0x8000_0000 (highest bit set), bit 31 is set + assert_eq!(fls32(0x8000_0000), 31); + } +} diff --git a/src/vlapic.rs b/src/vlapic.rs index af38388c5..69cd76801 100644 --- a/src/vlapic.rs +++ b/src/vlapic.rs @@ -1,23 +1,55 @@ +use core::marker::PhantomData; use core::ptr::NonNull; -use axerrno::AxResult; -use tock_registers::interfaces::Readable; +use axerrno::{AxError, AxResult}; +use bit::BitIndex; +use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use axaddrspace::device::AccessWidth; use axaddrspace::{AxMmHal, HostPhysAddr, PhysFrame}; use axdevice_base::DeviceRWContext; -use crate::consts::{ApicRegOffset, RESET_SPURIOUS_INTERRUPT_VECTOR}; -use crate::lvt::LocalVectorTable; -use crate::regs::{LocalAPICRegs, SpuriousInterruptVectorRegisterLocal}; +use crate::consts::{ + APIC_LVT_DS, APIC_LVT_M, APIC_LVT_VECTOR, ApicRegOffset, LAPIC_TRIG_EDGE, + RESET_SPURIOUS_INTERRUPT_VECTOR, +}; +use crate::hal::AxVMHal; +use crate::regs::DESTINATION_FORMAT::Model::Value as APICDestinationFormat; +use crate::regs::INTERRUPT_COMMAND_LOW::DeliveryMode::Value as APICDeliveryMode; +use crate::regs::INTERRUPT_COMMAND_LOW::DestinationShorthand::Value as APICDestination; +use crate::regs::lvt::{ + LVT_CMCI, LVT_ERROR, LVT_LINT0, LVT_LINT1, LVT_PERFORMANCE_COUNTER, LVT_THERMAL_MONITOR, + LVT_TIMER, LocalVectorTable, +}; +use crate::regs::{APIC_BASE, ApicBaseRegisterMsr, DESTINATION_FORMAT, LocalAPICRegs}; +use crate::regs::{ERROR_STATUS, ErrorStatusRegisterLocal, ErrorStatusRegisterValue}; +use crate::regs::{ + INTERRUPT_COMMAND_HIGH, INTERRUPT_COMMAND_LOW, InterruptCommandRegisterLowLocal, +}; +use crate::regs::{SPURIOUS_INTERRUPT_VECTOR, SpuriousInterruptVectorRegisterLocal}; +use crate::timer::{ApicTimer, TimerMode}; +use crate::utils::fls32; /// Virtual-APIC Registers. -pub struct VirtualApicRegs { +pub struct VirtualApicRegs { /// The virtual-APIC page is a 4-KByte region of memory /// that the processor uses to virtualize certain accesses to APIC registers and to manage virtual interrupts. /// The physical address of the virtual-APIC page is the virtual-APIC address, /// a 64-bit VM-execution control field in the VMCS (see Section 25.6.8). virtual_lapic: NonNull, + + /// Todo: distinguish between APIC ID and vCPU ID. + vapic_id: u32, + esr_pending: ErrorStatusRegisterLocal, + esr_firing: i32, + + virtual_timer: ApicTimer, + + /// Vector number for the highest priority bit that is set in the ISR + isrv: u32, + + apic_base: ApicBaseRegisterMsr, + /// Copies of some registers in the virtual APIC page, /// to be able to detect what changed (e.g. svr_last) svr_last: SpuriousInterruptVectorRegisterLocal, @@ -25,17 +57,26 @@ pub struct VirtualApicRegs { /// to maintain a coherent snapshot of the register (e.g. lvt_last) lvt_last: LocalVectorTable, apic_page: PhysFrame, + _marker: PhantomData, } -impl VirtualApicRegs { +impl VirtualApicRegs { /// Create new virtual-APIC registers by allocating a 4-KByte page for the virtual-APIC page. - pub fn new() -> Self { + pub fn new(vcpu_id: u32) -> Self { let apic_frame = PhysFrame::alloc_zero().expect("allocate virtual-APIC page failed"); Self { + // virtual-APIC ID is the same as the VCPU ID. + vapic_id: vcpu_id, + esr_pending: ErrorStatusRegisterLocal::new(0), + esr_firing: 0, virtual_lapic: NonNull::new(apic_frame.as_mut_ptr().cast()).unwrap(), apic_page: apic_frame, svr_last: SpuriousInterruptVectorRegisterLocal::new(RESET_SPURIOUS_INTERRUPT_VECTOR), lvt_last: LocalVectorTable::default(), + isrv: 0, + apic_base: ApicBaseRegisterMsr::new(0), + virtual_timer: ApicTimer::new(), + _marker: PhantomData, } } @@ -50,15 +91,601 @@ impl VirtualApicRegs { pub fn virtual_apic_page_addr(&self) -> HostPhysAddr { self.apic_page.start_paddr() } + + /// Gets the APIC base MSR value. + pub fn apic_base(&self) -> u64 { + self.apic_base.get() + } + + /// Returns whether the x2APIC mode is enabled. + pub fn is_x2apic_enabled(&self) -> bool { + self.apic_base.is_set(APIC_BASE::XAPIC_ENABLED) + && self.apic_base.is_set(APIC_BASE::X2APIC_Enabled) + } + + /// Returns whether the xAPIC mode is enabled. + pub fn is_xapic_enabled(&self) -> bool { + self.apic_base.is_set(APIC_BASE::XAPIC_ENABLED) + && !self.apic_base.is_set(APIC_BASE::X2APIC_Enabled) + } + + /// Returns the current timer mode. + pub fn timer_mode(&self) -> AxResult { + match self.regs().LVT_TIMER.read_as_enum(LVT_TIMER::TimerMode) { + Some(LVT_TIMER::TimerMode::Value::OneShot) => Ok(TimerMode::OneShot), + Some(LVT_TIMER::TimerMode::Value::Periodic) => Ok(TimerMode::Periodic), + Some(LVT_TIMER::TimerMode::Value::TSCDeadline) => Ok(TimerMode::TscDeadline), + Some(LVT_TIMER::TimerMode::Value::Reserved) | None => Err(AxError::InvalidData), + } + } + + /// 30.1.4 EOI Virtualization + /// IF any bits set in VISR + /// THEN SVI := highest index of bit set in VISR + /// ELSE SVI := 0; + /// FI; + fn find_isrv(&self) -> u32 { + let mut isrv = 0; + /* i ranges effectively from 7 to 1 */ + for i in (1..8).rev() { + let val = self.regs().ISR[i].get() as u32; + if val != 0 { + isrv = ((i as u32) << 5) | fls32(val) as u32; + break; + } + } + + isrv + } + + fn update_ppr(&mut self) { + let isrv = self.isrv; + let tpr = self.regs().TPR.get() as u32; + // IF VTPR[7:4] ≥ SVI[7:4] + let ppr = if prio(tpr) >= prio(isrv) { + // THEN VPPR := VTPR & FFH; + tpr + } else { + // ELSE VPPR := SVI & F0H; + isrv & 0xf0 + }; + self.regs().PPR.set(ppr as _); + } + + /// Process the EOI operation triggered by a write to the EOI register. + /// 11.8.5 Signaling Interrupt Servicing Completion + /// 30.1.4 EOI Virtualization + fn process_eoi(&mut self) { + let vector = self.isrv; + + if vector == 0 { + return; + } + + let (idx, bitpos) = extract_index_and_bitpos_u32(vector); + + // Upon receiving an EOI, the APIC clears the highest priority bit in the ISR + // and dispatches the next highest priority interrupt to the processor. + + // VISR[Vector] := 0; (see Section 30.1.1 for definition of VISR) + let mut isr = self.regs().ISR[idx].get(); + isr &= !(1 << bitpos); + self.regs().ISR[idx].set(isr); + + // IF any bits set in VISR + // THEN SVI := highest index of bit set in VISR + // ELSE SVI := 0; + self.isrv = self.find_isrv(); + + // perform PPR virtualiation (see Section 30.1.3); + self.update_ppr(); + + // The trigger mode register (TMR) indicates the trigger mode of the interrupt (see Figure 11-20). + // Upon acceptance of an interrupt into the IRR, the corresponding TMR bit is cleared for edge-triggered interrupts and set for leveltriggered interrupts. + // If a TMR bit is set when an EOI cycle for its corresponding interrupt vector is generated, an EOI message is sent to all I/O APICs. + // (see 11.8.4 Interrupt Acceptance for Fixed Interrupts) + if (self.regs().TMR[idx].get() as u32).bit(bitpos) { + // Send EOI to all I/O APICs + /* + * Per Intel SDM 10.8.5, Software can inhibit the broadcast of + * EOI by setting bit 12 of the Spurious Interrupt Vector + * Register of the LAPIC. + * TODO: Check if the bit 12 "Suppress EOI Broadcasts" is set. + */ + unimplemented!("vioapic_broadcast_eoi(vlapic2vcpu(vlapic)->vm, vector);") + } + + debug!("Gratuitous EOI vector: {:#010X}", vector); + + unimplemented!("vcpu_make_request(vlapic2vcpu(vlapic), ACRN_REQUEST_EVENT);") + } + + /// Post an interrupt to the vcpu running on 'hostcpu'. + /// This will use a hardware assist if available (e.g. Posted Interrupt) + /// or fall back to sending an 'ipinum' to interrupt the 'hostcpu'. + fn set_err(&mut self, mask: ErrorStatusRegisterValue) { + self.esr_pending.modify(mask); + + self.esr_firing = 1; + if self.esr_firing == 0 { + self.esr_firing = 1; + let _lvt = self.regs().LVT_ERROR.get(); + // if ((lvt & APIC_LVT_M) == 0U) { + // vec = lvt & APIC_LVT_VECTOR; + // if (vec >= 16U) { + // vlapic_accept_intr(vlapic, vec, LAPIC_TRIG_EDGE); + // } + // } + unimplemented!("vlapic_accept_intr(vlapic, vec, LAPIC_TRIG_EDGE)"); + // self.esr_firing = 0; + } + } + + fn is_dest_field_matched(&self, dest: u32) -> AxResult { + let mut ret = false; + + let ldr = self.regs().LDR.get(); + + if self.is_x2apic_enabled() { + return Ok(true); + } else { + match self + .regs() + .DFR + .read_as_enum::(DESTINATION_FORMAT::Model) + .ok_or(AxError::InvalidData)? + { + APICDestinationFormat::Flat => { + /* + * In the "Flat Model" the MDA is interpreted as an 8-bit wide + * bitmask. This model is available in the xAPIC mode only. + */ + let logical_id = ldr >> 24; + let dest_logical_id = dest & 0xff; + if logical_id & dest_logical_id != 0 { + ret = true; + } + } + APICDestinationFormat::Cluster => { + /* + * In the "Cluster Model" the MDA is used to identify a + * specific cluster and a set of APICs in that cluster. + */ + let logical_id = (ldr >> 24) & 0xf; + let cluster_id = ldr >> 28; + let dest_logical_id = dest & 0xf; + let dest_cluster_id = (dest >> 4) & 0xf; + if (cluster_id == dest_cluster_id) && ((logical_id & dest_logical_id) != 0) { + ret = true; + } + } + } + } + Ok(ret) + } + + /// This function populates 'dmask' with the set of vcpus that match the + /// addressing specified by the (dest, phys, lowprio) tuple. + fn calculate_dest_no_shorthand( + &self, + is_broadcast: bool, + dest: u32, + is_phys: bool, + lowprio: bool, + ) -> AxResult { + let mut dmask = 0; + + if is_broadcast { + // Broadcast in both logical and physical modes. + dmask = VM::active_vcpus() as u64; + } else if is_phys { + // Physical mode: "dest" is local APIC ID. + // Todo: distinguish between APIC ID and vCPU ID. + dmask = 1 << dest; + } else if lowprio { + // lowprio is not supported. + // Refer to 11.6.2.4 Lowest Priority Delivery Mode. + unimplemented!("lowprio"); + } else { + // Logical mode: "dest" is message destination addr + // to be compared with the logical APIC ID in LDR. + + let vcpu_mask = VM::active_vcpus(); + for i in 0..VM::vcpu_num() { + if vcpu_mask & (1 << i) != 0 { + if !self.is_dest_field_matched(dest)? { + continue; + } + dmask |= 1 << i; + } + } + } + + Ok(dmask) + } + + fn calculate_dest( + &self, + shorthand: APICDestination, + is_broadcast: bool, + dest: u32, + is_phys: bool, + lowprio: bool, + ) -> AxResult { + let mut dmask = 0; + match shorthand { + APICDestination::NoShorthand => { + dmask = self.calculate_dest_no_shorthand(is_broadcast, dest, is_phys, lowprio)?; + } + APICDestination::SELF => { + dmask.set_bit(self.vapic_id as usize, true); + } + APICDestination::AllIncludingSelf => { + dmask = VM::active_vcpus() as u64; + } + APICDestination::AllExcludingSelf => { + dmask = VM::active_vcpus() as u64; + dmask &= !(1 << self.vapic_id); + } + } + + Ok(dmask) + } + + fn handle_self_ipi(&mut self) { + unimplemented!("x2apic handle_self_ipi"); + } + + fn set_intr(&mut self, vcpu_id: u32, vector: u32, level: bool) { + unimplemented!( + "set_intr, vcpu_id: {}, vector: {}, level: {}", + vcpu_id, + vector, + level + ); + } + + fn inject_nmi(&mut self, vcpu_id: u32) { + unimplemented!("inject_nmi vcpu_id: {}", vcpu_id); + } + + fn process_init_sipi( + &mut self, + vcpu_id: u32, + mode: APICDeliveryMode, + icr_low: InterruptCommandRegisterLowLocal, + ) { + unimplemented!( + "process_init_sipi, vcpu_id: {}, mode: {:?} icr_low: {:#010X}", + vcpu_id, + mode, + icr_low.get() + ); + } + + /// Figure 11-13. Logical Destination Register (LDR) + fn write_ldr(&mut self) { + const LDR_RESERVED: u32 = 0x00ffffff; + + let mut ldr = self.regs().LDR.get(); + let apic_id = ldr >> 24; + ldr &= !LDR_RESERVED; + + self.regs().LDR.set(ldr); + debug!( + "[VLAPIC] apic_id={:#010X} write LDR register to {:#010X}", + apic_id, ldr + ); + } + + fn write_dfr(&mut self) { + use crate::regs::DESTINATION_FORMAT; + + const APIC_DFR_RESERVED: u32 = 0x0fff_ffff; + const APIC_DFR_MODEL_MASK: u32 = 0xf000_0000; + + let mut dfr = self.regs().DFR.get(); + dfr &= APIC_DFR_MODEL_MASK; + dfr |= APIC_DFR_RESERVED; + self.regs().DFR.set(dfr); + + debug!("[VLAPIC] write DFR register to {:#010X}", dfr); + + match self.regs().DFR.read_as_enum(DESTINATION_FORMAT::Model) { + Some(DESTINATION_FORMAT::Model::Value::Flat) => { + debug!("[VLAPIC] DFR in Flat Model"); + } + Some(DESTINATION_FORMAT::Model::Value::Cluster) => { + debug!("[VLAPIC] DFR in Cluster Model"); + } + None => { + debug!("[VLAPIC] DFR in Unknown Model {:#010X}", dfr); + } + } + } + + /// Figure 11-14. Spurious-Interrupt Vector Register (SVR) + /// Handle writes to the SVR register. + fn write_svr(&mut self) -> AxResult { + let new = self.regs().SVR.extract(); + let old = self.svr_last; + + self.svr_last = new; + + if old.is_set(SPURIOUS_INTERRUPT_VECTOR::APICSoftwareEnableDisable) + && !new.is_set(SPURIOUS_INTERRUPT_VECTOR::APICSoftwareEnableDisable) + { + debug!("[VLAPIC] vlapic [{}] is software-disabled", self.vapic_id); + // The apic is now disabled so stop the apic timer + // and mask all the LVT entries. + self.virtual_timer.delete_timer()?; + self.mask_lvts()?; + warn!("VM wire mode should be changed to INTR here, unimplemented"); + } else if !old.is_set(SPURIOUS_INTERRUPT_VECTOR::APICSoftwareEnableDisable) + && new.is_set(SPURIOUS_INTERRUPT_VECTOR::APICSoftwareEnableDisable) + { + debug!("[VLAPIC] vlapic [{}] is software-enabled", self.vapic_id); + + // The apic is now enabled so restart the apic timer + // if it is configured in periodic mode. + if self.virtual_timer.is_periodic() { + debug!("Restarting the apic timer"); + self.virtual_timer.start_timer()?; + } + } + + Ok(()) + } + + fn write_esr(&mut self) { + let esr = self.regs().ESR.get(); + debug!("[VLAPIC] write ESR register to {:#010X}", esr); + self.regs().ESR.set(self.esr_pending.get()); + self.esr_pending.set(0); + } + + fn write_icr(&mut self) -> AxResult { + self.regs() + .ICR_LO + .modify(INTERRUPT_COMMAND_LOW::DeliveryStatus::Idle); + + let icr_low = self.regs().ICR_LO.extract(); + + let (dest, is_broadcast) = if self.is_x2apic_enabled() { + use crate::consts::x2apic::X2APIC_BROADCAST_DEST_ID; + let dest = self.regs().ICR_HI.get(); + (dest, dest == X2APIC_BROADCAST_DEST_ID) + } else { + use crate::consts::xapic::XAPIC_BROADCAST_DEST_ID; + let dest = self.regs().ICR_HI.read(INTERRUPT_COMMAND_HIGH::Destination); + (dest, dest == XAPIC_BROADCAST_DEST_ID) + }; + + let vec = icr_low.read(INTERRUPT_COMMAND_LOW::Vector); + let mode = icr_low + .read_as_enum::(INTERRUPT_COMMAND_LOW::DeliveryMode) + .ok_or(AxError::InvalidData)?; + let is_phys = icr_low.is_set(INTERRUPT_COMMAND_LOW::DestinationMode); + let shorthand = icr_low + .read_as_enum::(INTERRUPT_COMMAND_LOW::DestinationShorthand) + .ok_or(AxError::InvalidData)?; + + if mode == APICDeliveryMode::Fixed && vec < 16 { + self.set_err(ERROR_STATUS::SendIllegalVector::SET); + debug!("[VLAPIC] Ignoring invalid IPI {:#010X}", vec); + } else if (shorthand == APICDestination::SELF + || shorthand == APICDestination::AllIncludingSelf) + && (mode == APICDeliveryMode::NMI + || mode == APICDeliveryMode::INIT + || mode == APICDeliveryMode::StartUp) + { + debug!("[VLAPIC] Invalid ICR value {:#010X}", vec); + } else { + debug!( + "icrlow {:#010X} icrhi {:#010X} triggered ipi {:#010X}", + icr_low.get(), + self.regs().ICR_HI.get(), + vec + ); + let dmask = self.calculate_dest(shorthand, is_broadcast, dest, is_phys, false)?; + + // TODO: we need to get the specific vcpu number somehow. + for i in 0..VM::vcpu_num() as u32 { + if dmask & (1 << i) != 0 { + match mode { + APICDeliveryMode::Fixed => { + self.set_intr(i, vec, LAPIC_TRIG_EDGE); + debug!("[VLAPIC] sending IPI {} to vcpu {}", vec, i); + } + APICDeliveryMode::NMI => { + self.inject_nmi(i); + debug!("[VLAPIC] sending NMI to vcpu {}", i); + } + APICDeliveryMode::INIT | APICDeliveryMode::StartUp => { + self.process_init_sipi(i, mode, icr_low); + } + APICDeliveryMode::SMI => { + warn!("[VLPAIC] SMI IPI do not support"); + } + _ => { + error!("Unhandled icrlo write with mode {:?}\n", mode); + } + } + } + } + } + + Ok(()) + } + + fn extract_lvt_val(&self, offset: ApicRegOffset) -> u32 { + match offset { + ApicRegOffset::LvtCMCI => self.regs().LVT_CMCI.get(), + ApicRegOffset::LvtTimer => self.regs().LVT_TIMER.get(), + ApicRegOffset::LvtThermal => self.regs().LVT_THERMAL.get(), + ApicRegOffset::LvtPmc => self.regs().LVT_PMI.get(), + ApicRegOffset::LvtLint0 => self.regs().LVT_LINT0.get(), + ApicRegOffset::LvtLint1 => self.regs().LVT_LINT1.get(), + ApicRegOffset::LvtErr => self.regs().LVT_ERROR.get(), + _ => { + warn!("[VLAPIC] read unsupported APIC register: {:?}", offset); + 0 + } + } + } + + fn write_lvt(&mut self, offset: ApicRegOffset) -> AxResult { + let mut val = self.extract_lvt_val(offset); + + if self + .regs() + .SVR + .is_set(SPURIOUS_INTERRUPT_VECTOR::APICSoftwareEnableDisable) + { + val |= APIC_LVT_M; + } + + // Mask::Masked, Delivery Status:SendPending, Vector::SET(0xff) + let mut mask = APIC_LVT_M | APIC_LVT_DS | APIC_LVT_VECTOR; + + match offset { + ApicRegOffset::LvtTimer => { + mask |= LVT_TIMER::TimerMode::SET.mask(); + val &= mask; + self.regs().LVT_TIMER.set(val); + self.lvt_last.lvt_timer.set(val); + + let new_timer_mode = self.timer_mode()?; + // A write to the LVT Timer Register that changes the timer mode disarms the local APIC timer. + if new_timer_mode != self.virtual_timer.timer_mode() { + self.virtual_timer.update_timer_mode(new_timer_mode)?; + } + } + ApicRegOffset::LvtErr => { + val &= mask; + self.regs().LVT_ERROR.set(val); + self.lvt_last.lvt_err.set(val); + } + ApicRegOffset::LvtLint0 => { + mask |= LVT_LINT0::TriggerMode::SET.mask(); + mask |= LVT_LINT0::RemoteIRR::SET.mask(); + mask |= LVT_LINT0::InterruptInputPinPolarity::SET.mask(); + mask |= LVT_LINT0::DeliveryMode::SET.mask(); + val &= mask; + + // vlapic mask/unmask LINT0 for ExtINT? + if (val & LVT_LINT0::DeliveryMode::SET.mask()) + == LVT_LINT0::DeliveryMode::ExtINT.mask() + { + let last = self.lvt_last.lvt_lint0; + if last.is_set(LVT_LINT0::Mask) && val & LVT_LINT0::Mask::SET.mask() == 0 { + // mask -> unmask: may from every vlapic in the vm + warn!("vpic wire mode change to LAPIC, unimplemented"); + } else if !last.is_set(LVT_LINT0::Mask) + && val & LVT_LINT0::Mask::SET.mask() != 0 + { + // unmask -> mask: only from the vlapic LINT0-ExtINT enabled + warn!("vpic wire mode change to NULL, unimplemented"); + } else { + // APIC_LVT_M unchanged. No action required. + } + } + + self.regs().LVT_LINT0.set(val); + self.lvt_last.lvt_lint0.set(val); + } + ApicRegOffset::LvtLint1 => { + mask |= LVT_LINT1::TriggerMode::SET.mask(); + mask |= LVT_LINT1::RemoteIRR::SET.mask(); + mask |= LVT_LINT1::InterruptInputPinPolarity::SET.mask(); + mask |= LVT_LINT1::DeliveryMode::SET.mask(); + val &= mask; + + self.regs().LVT_LINT1.set(val); + self.lvt_last.lvt_lint1.set(val); + } + ApicRegOffset::LvtCMCI => { + mask |= LVT_CMCI::DeliveryMode::SET.mask(); + val &= mask; + self.regs().LVT_CMCI.set(val); + self.lvt_last.lvt_cmci.set(val); + } + ApicRegOffset::LvtPmc => { + mask |= LVT_PERFORMANCE_COUNTER::DeliveryMode::SET.mask(); + val &= mask; + self.regs().LVT_PMI.set(val); + self.lvt_last.lvt_perf_count.set(val); + } + ApicRegOffset::LvtThermal => { + mask |= LVT_THERMAL_MONITOR::DeliveryMode::SET.mask(); + val &= mask; + self.regs().LVT_THERMAL.set(val); + self.lvt_last.lvt_thermal.set(val); + } + _ => { + warn!("[VLAPIC] write unsupported APIC register: {:?}", offset); + return Err(AxError::InvalidInput); + } + } + Ok(()) + } + + fn mask_lvts(&mut self) -> AxResult { + self.regs().LVT_CMCI.modify(LVT_CMCI::Mask::SET); + self.write_lvt(ApicRegOffset::LvtCMCI)?; + + self.regs().LVT_TIMER.modify(LVT_TIMER::Mask::SET); + self.write_lvt(ApicRegOffset::LvtTimer)?; + + self.regs() + .LVT_THERMAL + .modify(LVT_THERMAL_MONITOR::Mask::SET); + self.write_lvt(ApicRegOffset::LvtThermal)?; + + self.regs() + .LVT_PMI + .modify(LVT_PERFORMANCE_COUNTER::Mask::SET); + self.write_lvt(ApicRegOffset::LvtPmc)?; + + self.regs().LVT_LINT0.modify(LVT_LINT0::Mask::SET); + self.write_lvt(ApicRegOffset::LvtLint0)?; + + self.regs().LVT_LINT1.modify(LVT_LINT1::Mask::SET); + self.write_lvt(ApicRegOffset::LvtLint1)?; + + self.regs().LVT_ERROR.modify(LVT_ERROR::Mask::SET); + self.write_lvt(ApicRegOffset::LvtErr)?; + + Ok(()) + } + + fn write_icrtmr(&mut self) {} + + fn write_dcr(&mut self) {} +} + +fn extract_index_u32(vector: u32) -> usize { + vector as usize >> 5 +} + +fn extract_index_and_bitpos_u32(vector: u32) -> (usize, usize) { + (extract_index_u32(vector), vector as usize & 0x1F) +} + +/// Figure 11-18. Task-Priority Register (TPR) +/// [7:4]: Task-Priority Class +/// [3:0]: Task-Priority Sub-Class +fn prio(x: u32) -> u32 { + (x >> 4) & 0xf } -impl Drop for VirtualApicRegs { +impl Drop for VirtualApicRegs { fn drop(&mut self) { H::dealloc_frame(self.apic_page.start_paddr()); } } -impl VirtualApicRegs { +impl VirtualApicRegs { pub fn handle_read( &self, offset: ApicRegOffset, @@ -69,19 +696,15 @@ impl VirtualApicRegs { match offset { ApicRegOffset::ID => { value = self.regs().ID.get() as _; - debug!("[VLAPIC] read APIC ID register: {:#010X}", value); } ApicRegOffset::Version => { value = self.regs().VERSION.get() as _; - debug!("[VLAPIC] read APIC Version register: {:#010X}", value); } ApicRegOffset::TPR => { value = self.regs().TPR.get() as _; - debug!("[VLAPIC] read TPR register: {:#010X}", value); } ApicRegOffset::PPR => { value = self.regs().PPR.get() as _; - debug!("[VLAPIC] read PPR register: {:#010X}", value); } ApicRegOffset::EOI => { // value = self.regs().EOI.get() as _; @@ -89,98 +712,213 @@ impl VirtualApicRegs { } ApicRegOffset::LDR => { value = self.regs().LDR.get() as _; - debug!("[VLAPIC] read LDR register: {:#010X}", value); } ApicRegOffset::DFR => { value = self.regs().DFR.get() as _; - debug!("[VLAPIC] read DFR register: {:#010X}", value); } ApicRegOffset::SIVR => { value = self.regs().SVR.get() as _; - debug!("[VLAPIC] read SVR register: {:#010X}", value); } ApicRegOffset::ISR(index) => { value = self.regs().ISR[index.as_usize()].get() as _; - debug!("[VLAPIC] read ISR[{}] register: {:#010X}", index, value); } ApicRegOffset::TMR(index) => { value = self.regs().TMR[index.as_usize()].get() as _; - debug!("[VLAPIC] read TMR[{}] register: {:#010X}", index, value); } ApicRegOffset::IRR(index) => { value = self.regs().IRR[index.as_usize()].get() as _; - debug!("[VLAPIC] read IRR[{}] register: {:#010X}", index, value); } ApicRegOffset::ESR => { value = self.regs().ESR.get() as _; - debug!("[VLAPIC] read ESR register: {:#010X}", value); } ApicRegOffset::ICRLow => { value = self.regs().ICR_LO.get() as _; - debug!("[VLAPIC] read ICR_LOW register: {:#010X}", value); - if width == AccessWidth::Qword { + if self.is_x2apic_enabled() && width == AccessWidth::Qword { let icr_hi = self.regs().ICR_HI.get() as usize; value |= icr_hi << 32; debug!("[VLAPIC] read ICR register: {:#018X}", value); + } else if self.is_x2apic_enabled() ^ (width == AccessWidth::Qword) { + warn!( + "[VLAPIC] Illegal read attempt of ICR register at width {:?} with X2APIC {}", + width, + if self.is_x2apic_enabled() { + "enabled" + } else { + "disabled" + } + ); + return Err(AxError::InvalidInput); } } ApicRegOffset::ICRHi => { value = self.regs().ICR_HI.get() as _; - debug!("[VLAPIC] read ICR_HI register: {:#010X}", value); } + // Local Vector Table registers. ApicRegOffset::LvtCMCI => { value = self.lvt_last.lvt_cmci.get() as _; - debug!("[VLAPIC] read LVT_CMCI register: {:#010X}", value); } ApicRegOffset::LvtTimer => { value = self.lvt_last.lvt_timer.get() as _; - debug!("[VLAPIC] read LVT_TIMER register: {:#010X}", value); } ApicRegOffset::LvtThermal => { value = self.lvt_last.lvt_thermal.get() as _; - debug!("[VLAPIC] read LvtThermal register: {:#010X}", value); } ApicRegOffset::LvtPmc => { value = self.lvt_last.lvt_perf_count.get() as _; - debug!("[VLAPIC] read LvtPmi register: {:#010X}", value); } ApicRegOffset::LvtLint0 => { value = self.lvt_last.lvt_lint0.get() as _; - debug!("[VLAPIC] read LvtLint0 register: {:#010X}", value); } ApicRegOffset::LvtLint1 => { value = self.lvt_last.lvt_lint1.get() as _; - debug!("[VLAPIC] read LvtLint1 register: {:#010X}", value); } ApicRegOffset::LvtErr => { value = self.lvt_last.lvt_err.get() as _; - debug!("[VLAPIC] read LvtErr register: {:#010X}", value); } + // Timer registers. ApicRegOffset::TimerInitCount => { - value = self.regs().ICR_TIMER.get() as _; + match self.timer_mode() { + Ok(TimerMode::OneShot) | Ok(TimerMode::Periodic) => { + value = self.regs().ICR_TIMER.get() as _; + } + Ok(TimerMode::TscDeadline) => { + /* if TSCDEADLINE mode always return 0*/ + value = 0; + } + Err(_) => { + warn!("[VLAPIC] read TimerInitCount register: invalid timer mode"); + } + } debug!("[VLAPIC] read TimerInitCount register: {:#010X}", value); } ApicRegOffset::TimerCurCount => { - value = self.regs().CCR_TIMER.get() as _; - debug!("[VLAPIC] read TimerCurCount register: {:#010X}", value); + value = self.virtual_timer.current_counter() as _; } ApicRegOffset::TimerDivConf => { value = self.regs().DCR_TIMER.get() as _; - debug!("[VLAPIC] read TimerDivConf register: {:#010X}", value); } _ => { warn!("[VLAPIC] read unknown APIC register: {:?}", offset); } } + debug!("[VLAPIC] read {} register: {:#010X}", offset, value); Ok(value) } pub fn handle_write( - &self, + &mut self, offset: ApicRegOffset, + val: usize, width: AccessWidth, context: DeviceRWContext, ) -> AxResult { + let data32 = val as u32; + + match offset { + ApicRegOffset::ID => { + // Force APIC ID to be read-only. + // self.regs().ID.set(val as _); + } + ApicRegOffset::EOI => { + self.process_eoi(); + } + ApicRegOffset::LDR => { + self.regs().LDR.set(data32); + self.write_ldr(); + } + ApicRegOffset::DFR => { + self.regs().DFR.set(data32); + self.write_dfr(); + } + ApicRegOffset::SIVR => { + self.regs().SVR.set(data32); + self.write_svr()?; + } + ApicRegOffset::ESR => { + self.regs().ESR.set(data32); + self.write_esr(); + } + ApicRegOffset::ICRLow => { + if self.is_x2apic_enabled() && width == AccessWidth::Qword { + debug!("[VLAPIC] write ICR register: {:#018X} in X2APIC mode", val); + self.regs().ICR_HI.set((val >> 32) as u32); + } else if self.is_x2apic_enabled() ^ (width == AccessWidth::Qword) { + warn!( + "[VLAPIC] Illegal read attempt of ICR register at width {:?} with X2APIC {}", + width, + if self.is_x2apic_enabled() { + "enabled" + } else { + "disabled" + } + ); + return Err(AxError::InvalidInput); + } + self.regs().ICR_LO.set(data32); + self.write_icr()?; + } + // Local Vector Table registers. + ApicRegOffset::LvtCMCI => { + self.regs().LVT_CMCI.set(data32); + self.write_lvt(offset)?; + } + ApicRegOffset::LvtTimer => { + self.regs().LVT_TIMER.set(data32); + self.write_lvt(offset)?; + } + ApicRegOffset::LvtThermal => { + self.regs().LVT_THERMAL.set(data32); + self.write_lvt(offset)?; + } + ApicRegOffset::LvtPmc => { + self.regs().LVT_PMI.set(data32); + self.write_lvt(offset)?; + } + ApicRegOffset::LvtLint0 => { + self.regs().LVT_LINT0.set(data32); + self.write_lvt(offset)?; + } + ApicRegOffset::LvtLint1 => { + self.regs().LVT_LINT1.set(data32); + self.write_lvt(offset)?; + } + ApicRegOffset::LvtErr => { + self.regs().LVT_ERROR.set(data32); + self.write_lvt(offset)?; + } + // Timer registers. + ApicRegOffset::TimerInitCount => { + // if TSCDEADLINE mode ignore icr_timer + if self.timer_mode()? == TimerMode::TscDeadline { + warn!( + "[VLAPIC] write TimerInitCount register: ignore icr_timer in TSCDEADLINE mode" + ); + return Ok(()); + } + self.regs().ICR_TIMER.set(data32); + self.write_icrtmr(); + } + ApicRegOffset::TimerDivConf => { + self.regs().DCR_TIMER.set(data32); + self.write_dcr(); + } + ApicRegOffset::SelfIPI => { + if self.is_x2apic_enabled() { + self.regs().SELF_IPI.set(data32); + self.handle_self_ipi(); + } else { + warn!("[VLAPIC] write SelfIPI register: unsupported in xAPIC mode"); + return Err(AxError::InvalidInput); + } + } + _ => { + warn!("[VLAPIC] write unsupported APIC register: {:?}", offset); + return Err(AxError::InvalidInput); + } + } + + debug!("[VLAPIC] write {} register: {:#010X}", offset, val); + Ok(()) } } From 7fdd40cd67711cdf8cfa1673f1d62d326d66b1d0 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Wed, 18 Jun 2025 15:33:31 +0800 Subject: [PATCH 019/132] A simple but working virtual local APIC timer. (#3) * [wip] on lapic timer * [wip] in in handle_write * [wip] in write_icr * [wip] finish partial icr_write * [wip] calculate_dest * make timer somewhat and somehow work * migrate to `api` branch of `axaddrspace` and `axdevice_base` * remove generic parameters completely * update dependency version * use main-branch (merged) `axdevice_base` and `axvisor_api` * formatted * allow `clippy::mut_from_ref` on `get_mut_vlapic_regs` * replace `u32` operations with `LvtTimerRegisterLocal` in timer * import `arceos_api::` to reduce code length * update parameters type * formatted --------- Co-authored-by: hky1999 <976929993@qq.com> --- Cargo.toml | 10 +- src/hal.rs | 35 ------ src/lib.rs | 102 ++++++----------- src/regs/lvt/mod.rs | 2 +- src/regs/timer/dcr.rs | 2 +- src/timer.rs | 258 ++++++++++++++++++++++++++++++------------ src/vlapic.rs | 124 +++++++++----------- 7 files changed, 283 insertions(+), 250 deletions(-) delete mode 100644 src/hal.rs diff --git a/Cargo.toml b/Cargo.toml index 984d45d52..3f5b539d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,11 +13,7 @@ bit = "0.1.1" memory_addr = "0.3" axerrno = "0.1.0" -axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git", branch = "inject_interrupt" } -axdevice_base = { git = "https://github.com/arceos-hypervisor/axdevice_crates.git", branch = "inject_interrupt"} +axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git" } +axdevice_base = { git = "https://github.com/arceos-hypervisor/axdevice_crates.git" } -# [patch."https://github.com/arceos-hypervisor/axaddrspace.git"] -# axaddrspace = { path = "../axaddrspace" } - -# [patch."https://github.com/arceos-hypervisor/axdevice_crates.git"] -# axdevice_base = { path = "../axdevice_crates/axdevice_base" } \ No newline at end of file +axvisor_api = { git = "https://github.com/arceos-hypervisor/axvisor_api.git" } diff --git a/src/hal.rs b/src/hal.rs deleted file mode 100644 index e97c53943..000000000 --- a/src/hal.rs +++ /dev/null @@ -1,35 +0,0 @@ -use axerrno::AxResult; - -/// The interfaces which the underlying software (kernel or hypervisor) must implement. -pub trait AxVMHal: Sized { - /// Current time in nanoseconds. - fn current_time_nanos() -> u64; - - /// Current VM ID. - fn current_vm_id() -> usize; - - /// Current Virtual CPU ID. - fn current_vcpu_id() -> usize; - - /// Current Physical CPU ID. - fn current_pcpu_id() -> usize; - - fn vcpu_num() -> usize; - - fn active_vcpus() -> usize; - - fn vm_apicid2vcpu_id() -> usize; - - /// Get the Physical CPU ID where the specified VCPU of the current VM resides. - /// - /// Returns an error if the VCPU is not found. - fn vcpu_resides_on(vm_id: usize, vcpu_id: usize) -> AxResult; - - /// Inject an IRQ to the specified VCPU. - /// - /// This method should find the physical CPU where the specified VCPU resides and inject the IRQ - /// to it on that physical CPU with [`axvcpu::AxVCpu::inject_interrupt`]. - /// - /// Returns an error if the VCPU is not found. - fn inject_irq_to_vcpu(vm_id: usize, vcpu_id: usize, irq: usize) -> AxResult; -} diff --git a/src/lib.rs b/src/lib.rs index 63a871a52..44c25247d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,22 +7,25 @@ extern crate alloc; extern crate log; mod consts; -mod hal; mod regs; mod timer; mod utils; mod vlapic; -use alloc::boxed::Box; use core::cell::UnsafeCell; -use hal::AxVMHal; use axerrno::AxResult; +use axvisor_api::{ + memory, + vmm::{VCpuId, VMId}, +}; use memory_addr::{AddrRange, PAGE_SIZE_4K}; -use axaddrspace::device::{AccessWidth, SysRegAddr, SysRegAddrRange}; -use axaddrspace::{AxMmHal, GuestPhysAddr, HostPhysAddr, HostVirtAddr}; -use axdevice_base::{BaseDeviceOps, DeviceRWContext, EmuDeviceType, InterruptInjector}; +use axaddrspace::{ + GuestPhysAddr, HostPhysAddr, HostVirtAddr, + device::{AccessWidth, SysRegAddr, SysRegAddrRange}, +}; +use axdevice_base::{BaseDeviceOps, EmuDeviceType}; use crate::consts::x2apic::x2apic_msr_access_reg; use crate::consts::xapic::xapic_mmio_access_reg_offset; @@ -34,35 +37,36 @@ struct APICAccessPage([u8; PAGE_SIZE_4K]); static VIRTUAL_APIC_ACCESS_PAGE: APICAccessPage = APICAccessPage([0; PAGE_SIZE_4K]); /// A emulated local APIC device. -pub struct EmulatedLocalApic { - vlapic_regs: UnsafeCell>, +pub struct EmulatedLocalApic { + vlapic_regs: UnsafeCell, } -impl EmulatedLocalApic { +impl EmulatedLocalApic { /// Create a new `EmulatedLocalApic`. - pub fn new(vcpu_id: u32) -> Self { + pub fn new(vm_id: VMId, vcpu_id: VCpuId) -> Self { EmulatedLocalApic { - vlapic_regs: UnsafeCell::new(VirtualApicRegs::new(vcpu_id)), + vlapic_regs: UnsafeCell::new(VirtualApicRegs::new(vm_id, vcpu_id)), } } - fn get_vlapic_regs(&self) -> &VirtualApicRegs { + fn get_vlapic_regs(&self) -> &VirtualApicRegs { unsafe { &*self.vlapic_regs.get() } } - fn get_mut_vlapic_regs(&self) -> &mut VirtualApicRegs { + #[allow(clippy::mut_from_ref)] // SAFETY: get_mut_vlapic_regs is never called concurrently. + fn get_mut_vlapic_regs(&self) -> &mut VirtualApicRegs { unsafe { &mut *self.vlapic_regs.get() } } } -impl EmulatedLocalApic { +impl EmulatedLocalApic { /// APIC-access address (64 bits). /// This field contains the physical address of the 4-KByte APIC-access page. /// If the “virtualize APIC accesses” VM-execution control is 1, /// access to this page may cause VM exits or be virtualized by the processor. /// See Section 30.4. pub fn virtual_apic_access_addr() -> HostPhysAddr { - H::virt_to_phys(HostVirtAddr::from_usize( + memory::virt_to_phys(HostVirtAddr::from_usize( VIRTUAL_APIC_ACCESS_PAGE.0.as_ptr() as usize, )) } @@ -76,7 +80,7 @@ impl EmulatedLocalApic { } } -impl BaseDeviceOps> for EmulatedLocalApic { +impl BaseDeviceOps> for EmulatedLocalApic { fn emu_type(&self) -> EmuDeviceType { EmuDeviceType::EmuDeviceTInterruptController } @@ -89,42 +93,26 @@ impl BaseDeviceOps> for Emulat ) } - fn handle_read( - &self, - addr: GuestPhysAddr, - width: AccessWidth, - context: DeviceRWContext, - ) -> AxResult { + fn handle_read(&self, addr: GuestPhysAddr, width: AccessWidth) -> AxResult { debug!( - "EmulatedLocalApic::handle_read: addr={:?}, width={:?}, context={:?}", - addr, width, context.vcpu_id + "EmulatedLocalApic::handle_read: addr={:?}, width={:?}", + addr, width, ); let reg_off = xapic_mmio_access_reg_offset(addr); - self.get_vlapic_regs().handle_read(reg_off, width, context) + self.get_vlapic_regs().handle_read(reg_off, width) } - fn handle_write( - &self, - addr: GuestPhysAddr, - width: AccessWidth, - val: usize, - context: DeviceRWContext, - ) -> AxResult { + fn handle_write(&self, addr: GuestPhysAddr, width: AccessWidth, val: usize) -> AxResult { debug!( - "EmulatedLocalApic::handle_write: addr={:?}, width={:?}, val={:#x}, context={:?}", - addr, width, val, context.vcpu_id + "EmulatedLocalApic::handle_write: addr={:?}, width={:?}, val={:#x}", + addr, width, val, ); let reg_off = xapic_mmio_access_reg_offset(addr); - self.get_mut_vlapic_regs() - .handle_write(reg_off, val, width, context) - } - - fn set_interrupt_injector(&mut self, _injector: Box) { - todo!() + self.get_mut_vlapic_regs().handle_write(reg_off, val, width) } } -impl BaseDeviceOps for EmulatedLocalApic { +impl BaseDeviceOps for EmulatedLocalApic { fn emu_type(&self) -> EmuDeviceType { EmuDeviceType::EmuDeviceTInterruptController } @@ -137,37 +125,21 @@ impl BaseDeviceOps for EmulatedLocalAp ) } - fn handle_read( - &self, - addr: SysRegAddr, - width: AccessWidth, - context: DeviceRWContext, - ) -> AxResult { + fn handle_read(&self, addr: SysRegAddr, width: AccessWidth) -> AxResult { debug!( - "EmulatedLocalApic::handle_read: addr={:?}, width={:?}, context={:?}", - addr, width, context.vcpu_id + "EmulatedLocalApic::handle_read: addr={:?}, width={:?}", + addr, width, ); let reg_off = x2apic_msr_access_reg(addr); - self.get_vlapic_regs().handle_read(reg_off, width, context) + self.get_vlapic_regs().handle_read(reg_off, width) } - fn handle_write( - &self, - addr: SysRegAddr, - width: AccessWidth, - val: usize, - context: DeviceRWContext, - ) -> AxResult { + fn handle_write(&self, addr: SysRegAddr, width: AccessWidth, val: usize) -> AxResult { debug!( - "EmulatedLocalApic::handle_write: addr={:?}, width={:?}, val={:#x}, context={:?}", - addr, width, val, context.vcpu_id + "EmulatedLocalApic::handle_write: addr={:?}, width={:?}, val={:#x}", + addr, width, val ); let reg_off = x2apic_msr_access_reg(addr); - self.get_mut_vlapic_regs() - .handle_write(reg_off, val, width, context) - } - - fn set_interrupt_injector(&mut self, _injector: Box) { - todo!() + self.get_mut_vlapic_regs().handle_write(reg_off, val, width) } } diff --git a/src/regs/lvt/mod.rs b/src/regs/lvt/mod.rs index ccbb4e375..8251ea222 100644 --- a/src/regs/lvt/mod.rs +++ b/src/regs/lvt/mod.rs @@ -16,7 +16,7 @@ pub use perfmon::*; pub use thermal::*; pub use timer::*; -use crate::consts::RESET_LVT_REG; +pub use crate::consts::RESET_LVT_REG; /// A read-write copy of LVT registers. pub struct LocalVectorTable { diff --git a/src/regs/timer/dcr.rs b/src/regs/timer/dcr.rs index f96913e6d..f0f4b1445 100644 --- a/src/regs/timer/dcr.rs +++ b/src/regs/timer/dcr.rs @@ -20,7 +20,7 @@ register_bitfields! { DivideBy2 = 0b0000, DivideBy4 = 0b0001, DivideBy8 = 0b0010, - DivideBy16 = 0b0101, + DivideBy16 = 0b0011, DivideBy32 = 0b1000, DivideBy64 = 0b1001, DivideBy128 = 0b1010, diff --git a/src/timer.rs b/src/timer.rs index d29f31570..8a12f959d 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,37 +1,73 @@ -use axerrno::AxResult; - -/// Local APIC timer modes. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[repr(u8)] -#[allow(dead_code)] -pub enum TimerMode { - /// Timer only fires once. - OneShot = 0b00, - /// Timer fires periodically. - Periodic = 0b01, - /// Timer fires at an absolute time. - TscDeadline = 0b10, -} +use alloc::boxed::Box; +use axerrno::{AxResult, ax_err}; +use axvisor_api::{ + time::{self, current_ticks, register_timer, ticks_to_nanos, ticks_to_time}, + vmm::{VCpuId, VMId, inject_interrupt}, +}; + +use crate::{ + consts::RESET_LVT_REG, + regs::lvt::{ + LVT_TIMER::{self, TimerMode::Value as TimerMode}, + LvtTimerRegisterLocal, + }, +}; /// A virtual local APIC timer. (SDM Vol. 3C, Section 11.5.4) +/// +/// This struct virtualizes the access to 4 registers in the Local APIC: +/// +/// - LVT Timer Register. (SDM Vol. 3A, Section 11.5.1, Figure 11-8, offset 0x320, MSR 0x832, Read/Write) +/// - Divide Configuration Register. (SDM Vol. 3A, Section 11.5.4, Figure 11-10, offset 0x3E0, MSR 0x83E, Read/Write) +/// - Initial Count Register. (SDM Vol. 3A, Section 11.5.4, Figure 11-11, offset 0x380, MSR 0x838, Read/Write) +/// - Current Count Register. (SDM Vol. 3A, Section 11.5.4, Figure 11-11, offset 0x390, MSR 0x839, Read Only) +/// +/// The timer works in the following way: +/// +/// - Timer is started by and only by writing to the Initial Count Register. +/// - The deadline is determined by the Initial Count Register and the Divide Configuration Register, at the time of the start. +/// - Any modification to the Divide Configuration Register or the LVT Timer Register will not affect the current timer. +/// - Any write to the Initial Count Register will restart the timer. +/// - The value of the LVT Timer is read, at the time the deadline is reached, to determine +/// - if an interrupt should be generated (not masked), +/// - if the timer should be restarted (periodic mode), and +/// - the interrupt vector number to be used. +/// - The delivery status field in the LVT Timer Register is not supported and always returns 0. +/// - The timer stops when: +/// - the deadline is reached, and the timer is in one-shot mode, or +/// - a 0 is written to the Initial Count Register. pub struct ApicTimer { - lvt_timer_bits: u32, + // the raw value of writable registers + /// Local Vector Table Timer Register. These's another copy in [`VirtualApicRegs`](crate::VirtualApicRegs), but we + /// keep a separate copy here for easier access. + lvt_timer_register: LvtTimerRegisterLocal, + /// Initial Count Register. This is the value that determines when the timer will fire. + initial_count_register: u32, + /// Divide Configuration Register. This determines the frequency of the timer. + divide_configuration_register: u32, + + // internal states divide_shift: u8, - initial_count: u32, - last_start_cycle: u64, + last_start_ticks: u64, deadline_ns: u64, - timer_mode: TimerMode, + + // temporary fields untils we find a permanent place for apic and its timer + cancel_token: Option, + where_am_i: (VMId, VCpuId), // (vm_id, vcpu_id) } impl ApicTimer { - pub(crate) const fn new() -> Self { + pub(crate) const fn new(vm_id: VMId, vcpu_id: VCpuId) -> Self { Self { - lvt_timer_bits: 0x1_0000, // masked - divide_shift: 0, - initial_count: 0, - last_start_cycle: 0, + lvt_timer_register: LvtTimerRegisterLocal::new(RESET_LVT_REG), // masked, one-shot, vector 0 + initial_count_register: 0, // 0 (stopped) + divide_configuration_register: 0, // divide by 2 + + divide_shift: 1, // as `divide_configuration_register` is 0, the shift is 1 (divide by 2) + last_start_ticks: 0, deadline_ns: 0, - timer_mode: TimerMode::OneShot, + cancel_token: None, + where_am_i: (vm_id, vcpu_id), } } @@ -51,80 +87,158 @@ impl ApicTimer { // } // } - /// Gets the timer mode. - pub fn timer_mode(&self) -> TimerMode { - self.timer_mode + pub fn read_lvt(&self) -> u32 { + self.lvt_timer_register.get() } - /// Update the timer mode if it is different from the current mode. - pub fn update_timer_mode(&mut self, mode: TimerMode) -> AxResult { - if self.timer_mode != mode { - self.timer_mode = mode; - unimplemented!("del timer and update timer") - } + pub fn write_lvt(&mut self, mut value: u32) -> AxResult { + // valid bits: 0-7, 12, 16-18 + const LVT_MASK: u32 = 0x0007_10FF; + value &= LVT_MASK; + self.lvt_timer_register.set(value); Ok(()) } - pub fn start_timer(&mut self) -> AxResult { - unimplemented!("start timer") + pub fn read_icr(&self) -> u32 { + self.initial_count_register } - pub fn delete_timer(&mut self) -> AxResult { - self.initial_count = 0; - self.deadline_ns = 0; - unimplemented!("del timer") + pub fn write_icr(&mut self, value: u32) -> AxResult { + // stop the timer no matter whether it is started, and no matter the value + self.stop_timer()?; + self.initial_count_register = value; + + if value > 0 { + self.start_timer() + } else { + Ok(()) + } } - /// Whether the timer interrupt is masked. - pub const fn is_masked(&self) -> bool { - self.lvt_timer_bits & (1 << 16) != 0 + /// Read from the Divide Configuration Register. + pub fn read_dcr(&self) -> u32 { + self.divide_configuration_register } - /// Whether the timer mode is periodic. - pub const fn is_periodic(&self) -> bool { - let timer_mode = (self.lvt_timer_bits >> 17) & 0b11; - timer_mode == TimerMode::Periodic as _ + /// Write to the Divide Configuration Register. + pub fn write_dcr(&mut self, mut value: u32) { + const DCR_MASK: u32 = 0b1011; + + value &= DCR_MASK; + let shift = match value { + 0b0000 => 1, // divide by 2 + 0b0001 => 2, // divide by 4 + 0b0010 => 3, // divide by 8 + 0b0011 => 4, // divide by 16 + 0b1000 => 5, // divide by 32 + 0b1001 => 6, // divide by 64 + 0b1010 => 7, // divide by 128 + 0b1011 => 0, // divide by 1 + _ => unreachable!( + "internal error: invalid divide configuration register value after mask" + ), + }; + + self.divide_configuration_register = value; + self.divide_shift = shift as u8; } - /// The timer interrupt vector number. - pub const fn vector(&self) -> u8 { - (self.lvt_timer_bits & 0xff) as u8 + /// Current Count Register. + pub fn read_ccr(&self) -> u32 { + if !self.is_started() { + return 0; + } + let remaining_ns = self.deadline_ns.wrapping_sub(time::current_time_nanos()); + let remaining_ticks = time::nanos_to_ticks(remaining_ns); + return (remaining_ticks >> self.divide_shift) as _; } - /// LVT Timer Register. (SDM Vol. 3A, Section 10.5.1, Figure 10-8) - pub const fn lvt_timer(&self) -> u32 { - self.lvt_timer_bits + /// Get the timer mode. + pub fn timer_mode(&self) -> TimerMode { + self.lvt_timer_register + .read_as_enum(LVT_TIMER::TimerMode) + .unwrap() // just panic if the value is invalid } - /// Divide Configuration Register. (SDM Vol. 3A, Section 10.5.4, Figure 10-10) - pub const fn divide(&self) -> u32 { - let dcr = self.divide_shift.wrapping_sub(1) as u32 & 0b111; - (dcr & 0b11) | ((dcr & 0b100) << 1) + /// Check whether the timer interrupt is masked. + pub fn is_masked(&self) -> bool { + self.lvt_timer_register.is_set(LVT_TIMER::Mask) } - /// Initial Count Register. - pub const fn initial_count(&self) -> u32 { - self.initial_count + /// The timer interrupt vector number. + pub fn vector(&self) -> u8 { + self.lvt_timer_register.read(LVT_TIMER::Vector) as u8 } - /// Current Count Register. - pub fn current_counter(&self) -> u32 { - if self.initial_count == 0 || self.timer_mode == TimerMode::TscDeadline { - return 0; + /// Check whether the timer is started. + pub fn is_started(&self) -> bool { + // these two conditions are equivalent actually, we check both for clarity and robustness + self.initial_count_register > 0 && self.cancel_token.is_some() + } + + /// Restart the timer. Will not start the timer if it is not started. + pub fn restart_timer(&mut self) -> AxResult { + if !self.is_started() { + return Ok(()); + } else { + self.stop_timer()?; + self.start_timer() + } + } + + /// Start the timer. + pub fn start_timer(&mut self) -> AxResult { + if self.is_started() { + return ax_err!(BadState, "Timer already started"); } - // Todo: get current cycles - let current_cycles = 0; + let current_ticks = current_ticks(); + let deadline_ticks = + current_ticks + ((self.initial_count_register as u64) << self.divide_shift); + let (vm_id, vcpu_id) = self.where_am_i; + let vector = self.vector(); + + trace!( + "vlapic @ (vm {}, vcpu {}) starts timer @ tick {:?}, deadline tick {:?}", + vm_id, vcpu_id, current_ticks, deadline_ticks + ); + + self.last_start_ticks = current_ticks; + self.deadline_ns = ticks_to_nanos(deadline_ticks); + + self.cancel_token = Some(register_timer( + ticks_to_time(deadline_ticks), + Box::new(move |_| { + // TODO: read the LVT Timer Register here + trace!( + "vlapic @ (vm {}, vcpu {}) timer expired, inject interrupt {}", + vm_id, vcpu_id, vector + ); + inject_interrupt(vm_id, vcpu_id, vector); + }), + )); - let elapsed_cycles = (current_cycles - self.last_start_cycle) >> self.divide_shift; - match self.timer_mode { - TimerMode::OneShot => self.initial_count - elapsed_cycles as u32, - TimerMode::Periodic => { - self.initial_count - (elapsed_cycles % self.initial_count as u64) as u32 - } - TimerMode::TscDeadline => 0, + Ok(()) + } + + pub fn stop_timer(&mut self) -> AxResult { + // TODO: maybe disable irq here? + if self.is_started() { + self.last_start_ticks = 0; + self.deadline_ns = 0; + + time::cancel_timer(self.cancel_token.take().unwrap()); + } else { + warn!("`stop_timer` called when timer is not started, bad operation tolerated"); } + + Ok(()) + } + + /// Whether the timer mode is periodic. + pub fn is_periodic(&self) -> bool { + self.timer_mode() == TimerMode::Periodic } // /// Set LVT Timer Register. diff --git a/src/vlapic.rs b/src/vlapic.rs index 69cd76801..9b07bd6f8 100644 --- a/src/vlapic.rs +++ b/src/vlapic.rs @@ -1,37 +1,38 @@ -use core::marker::PhantomData; use core::ptr::NonNull; -use axerrno::{AxError, AxResult}; +use axvisor_api::vmm::{VCpuId, VMId}; use bit::BitIndex; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; -use axaddrspace::device::AccessWidth; -use axaddrspace::{AxMmHal, HostPhysAddr, PhysFrame}; -use axdevice_base::DeviceRWContext; +use axaddrspace::{HostPhysAddr, device::AccessWidth}; +use axerrno::{AxError, AxResult, ax_err_type}; +use axvisor_api::{memory::PhysFrame, vmm}; use crate::consts::{ APIC_LVT_DS, APIC_LVT_M, APIC_LVT_VECTOR, ApicRegOffset, LAPIC_TRIG_EDGE, RESET_SPURIOUS_INTERRUPT_VECTOR, }; -use crate::hal::AxVMHal; -use crate::regs::DESTINATION_FORMAT::Model::Value as APICDestinationFormat; -use crate::regs::INTERRUPT_COMMAND_LOW::DeliveryMode::Value as APICDeliveryMode; -use crate::regs::INTERRUPT_COMMAND_LOW::DestinationShorthand::Value as APICDestination; -use crate::regs::lvt::{ - LVT_CMCI, LVT_ERROR, LVT_LINT0, LVT_LINT1, LVT_PERFORMANCE_COUNTER, LVT_THERMAL_MONITOR, - LVT_TIMER, LocalVectorTable, -}; -use crate::regs::{APIC_BASE, ApicBaseRegisterMsr, DESTINATION_FORMAT, LocalAPICRegs}; -use crate::regs::{ERROR_STATUS, ErrorStatusRegisterLocal, ErrorStatusRegisterValue}; use crate::regs::{ - INTERRUPT_COMMAND_HIGH, INTERRUPT_COMMAND_LOW, InterruptCommandRegisterLowLocal, + APIC_BASE, ApicBaseRegisterMsr, + DESTINATION_FORMAT::{self, Model::Value as APICDestinationFormat}, + ERROR_STATUS, ErrorStatusRegisterLocal, ErrorStatusRegisterValue, INTERRUPT_COMMAND_HIGH, + INTERRUPT_COMMAND_LOW::{ + self, DeliveryMode::Value as APICDeliveryMode, + DestinationShorthand::Value as APICDestination, + }, + InterruptCommandRegisterLowLocal, LocalAPICRegs, SPURIOUS_INTERRUPT_VECTOR, + SpuriousInterruptVectorRegisterLocal, + lvt::{ + LVT_CMCI, LVT_ERROR, LVT_LINT0, LVT_LINT1, LVT_PERFORMANCE_COUNTER, LVT_THERMAL_MONITOR, + LVT_TIMER, LocalVectorTable, + }, }; -use crate::regs::{SPURIOUS_INTERRUPT_VECTOR, SpuriousInterruptVectorRegisterLocal}; -use crate::timer::{ApicTimer, TimerMode}; -use crate::utils::fls32; +use crate::{timer::ApicTimer, utils::fls32}; + +pub use crate::regs::lvt::LVT_TIMER::TimerMode::Value as TimerMode; /// Virtual-APIC Registers. -pub struct VirtualApicRegs { +pub struct VirtualApicRegs { /// The virtual-APIC page is a 4-KByte region of memory /// that the processor uses to virtualize certain accesses to APIC registers and to manage virtual interrupts. /// The physical address of the virtual-APIC page is the virtual-APIC address, @@ -56,17 +57,16 @@ pub struct VirtualApicRegs { /// Copies of some registers in the virtual APIC page, /// to maintain a coherent snapshot of the register (e.g. lvt_last) lvt_last: LocalVectorTable, - apic_page: PhysFrame, - _marker: PhantomData, + apic_page: PhysFrame, } -impl VirtualApicRegs { +impl VirtualApicRegs { /// Create new virtual-APIC registers by allocating a 4-KByte page for the virtual-APIC page. - pub fn new(vcpu_id: u32) -> Self { + pub fn new(vm_id: VMId, vcpu_id: VCpuId) -> Self { let apic_frame = PhysFrame::alloc_zero().expect("allocate virtual-APIC page failed"); Self { // virtual-APIC ID is the same as the VCPU ID. - vapic_id: vcpu_id, + vapic_id: vcpu_id as _, esr_pending: ErrorStatusRegisterLocal::new(0), esr_firing: 0, virtual_lapic: NonNull::new(apic_frame.as_mut_ptr().cast()).unwrap(), @@ -75,8 +75,7 @@ impl VirtualApicRegs { lvt_last: LocalVectorTable::default(), isrv: 0, apic_base: ApicBaseRegisterMsr::new(0), - virtual_timer: ApicTimer::new(), - _marker: PhantomData, + virtual_timer: ApicTimer::new(vm_id, vcpu_id), } } @@ -111,12 +110,10 @@ impl VirtualApicRegs { /// Returns the current timer mode. pub fn timer_mode(&self) -> AxResult { - match self.regs().LVT_TIMER.read_as_enum(LVT_TIMER::TimerMode) { - Some(LVT_TIMER::TimerMode::Value::OneShot) => Ok(TimerMode::OneShot), - Some(LVT_TIMER::TimerMode::Value::Periodic) => Ok(TimerMode::Periodic), - Some(LVT_TIMER::TimerMode::Value::TSCDeadline) => Ok(TimerMode::TscDeadline), - Some(LVT_TIMER::TimerMode::Value::Reserved) | None => Err(AxError::InvalidData), - } + self.regs() + .LVT_TIMER + .read_as_enum(LVT_TIMER::TimerMode) + .ok_or_else(|| ax_err_type!(InvalidData, "Failed to read timer mode from LVT_TIMER")) } /// 30.1.4 EOI Virtualization @@ -277,7 +274,7 @@ impl VirtualApicRegs { if is_broadcast { // Broadcast in both logical and physical modes. - dmask = VM::active_vcpus() as u64; + dmask = vmm::current_vm_active_vcpus() as u64; } else if is_phys { // Physical mode: "dest" is local APIC ID. // Todo: distinguish between APIC ID and vCPU ID. @@ -290,8 +287,8 @@ impl VirtualApicRegs { // Logical mode: "dest" is message destination addr // to be compared with the logical APIC ID in LDR. - let vcpu_mask = VM::active_vcpus(); - for i in 0..VM::vcpu_num() { + let vcpu_mask = vmm::active_vcpus(vmm::current_vm_id()).unwrap(); + for i in 0..vmm::current_vm_vcpu_num() { if vcpu_mask & (1 << i) != 0 { if !self.is_dest_field_matched(dest)? { continue; @@ -321,10 +318,10 @@ impl VirtualApicRegs { dmask.set_bit(self.vapic_id as usize, true); } APICDestination::AllIncludingSelf => { - dmask = VM::active_vcpus() as u64; + dmask = vmm::current_vm_active_vcpus() as u64; } APICDestination::AllExcludingSelf => { - dmask = VM::active_vcpus() as u64; + dmask = vmm::current_vm_active_vcpus() as u64; dmask &= !(1 << self.vapic_id); } } @@ -418,7 +415,7 @@ impl VirtualApicRegs { debug!("[VLAPIC] vlapic [{}] is software-disabled", self.vapic_id); // The apic is now disabled so stop the apic timer // and mask all the LVT entries. - self.virtual_timer.delete_timer()?; + self.virtual_timer.stop_timer()?; self.mask_lvts()?; warn!("VM wire mode should be changed to INTR here, unimplemented"); } else if !old.is_set(SPURIOUS_INTERRUPT_VECTOR::APICSoftwareEnableDisable) @@ -430,7 +427,7 @@ impl VirtualApicRegs { // if it is configured in periodic mode. if self.virtual_timer.is_periodic() { debug!("Restarting the apic timer"); - self.virtual_timer.start_timer()?; + self.virtual_timer.restart_timer()?; } } @@ -490,7 +487,7 @@ impl VirtualApicRegs { let dmask = self.calculate_dest(shorthand, is_broadcast, dest, is_phys, false)?; // TODO: we need to get the specific vcpu number somehow. - for i in 0..VM::vcpu_num() as u32 { + for i in 0..vmm::current_vm_vcpu_num() as u32 { if dmask & (1 << i) != 0 { match mode { APICDeliveryMode::Fixed => { @@ -552,14 +549,10 @@ impl VirtualApicRegs { ApicRegOffset::LvtTimer => { mask |= LVT_TIMER::TimerMode::SET.mask(); val &= mask; - self.regs().LVT_TIMER.set(val); + self.regs().LVT_TIMER.set(val); // Duplicated, which one should be removed? self.lvt_last.lvt_timer.set(val); - let new_timer_mode = self.timer_mode()?; - // A write to the LVT Timer Register that changes the timer mode disarms the local APIC timer. - if new_timer_mode != self.virtual_timer.timer_mode() { - self.virtual_timer.update_timer_mode(new_timer_mode)?; - } + self.virtual_timer.write_lvt(val)?; } ApicRegOffset::LvtErr => { val &= mask; @@ -659,9 +652,14 @@ impl VirtualApicRegs { Ok(()) } - fn write_icrtmr(&mut self) {} + fn write_icrtmr(&mut self) -> AxResult { + self.virtual_timer.write_icr(self.regs().ICR_TIMER.get()) + } - fn write_dcr(&mut self) {} + fn write_dcr(&mut self) -> AxResult { + self.virtual_timer.write_dcr(self.regs().DCR_TIMER.get()); + Ok(()) + } } fn extract_index_u32(vector: u32) -> usize { @@ -679,19 +677,8 @@ fn prio(x: u32) -> u32 { (x >> 4) & 0xf } -impl Drop for VirtualApicRegs { - fn drop(&mut self) { - H::dealloc_frame(self.apic_page.start_paddr()); - } -} - -impl VirtualApicRegs { - pub fn handle_read( - &self, - offset: ApicRegOffset, - width: AccessWidth, - context: DeviceRWContext, - ) -> AxResult { +impl VirtualApicRegs { + pub fn handle_read(&self, offset: ApicRegOffset, width: AccessWidth) -> AxResult { let mut value: usize = 0; match offset { ApicRegOffset::ID => { @@ -781,18 +768,18 @@ impl VirtualApicRegs { Ok(TimerMode::OneShot) | Ok(TimerMode::Periodic) => { value = self.regs().ICR_TIMER.get() as _; } - Ok(TimerMode::TscDeadline) => { + Ok(TimerMode::TSCDeadline) => { /* if TSCDEADLINE mode always return 0*/ value = 0; } - Err(_) => { + _ => { warn!("[VLAPIC] read TimerInitCount register: invalid timer mode"); } } debug!("[VLAPIC] read TimerInitCount register: {:#010X}", value); } ApicRegOffset::TimerCurCount => { - value = self.virtual_timer.current_counter() as _; + value = self.virtual_timer.read_ccr() as _; } ApicRegOffset::TimerDivConf => { value = self.regs().DCR_TIMER.get() as _; @@ -810,7 +797,6 @@ impl VirtualApicRegs { offset: ApicRegOffset, val: usize, width: AccessWidth, - context: DeviceRWContext, ) -> AxResult { let data32 = val as u32; @@ -889,18 +875,18 @@ impl VirtualApicRegs { // Timer registers. ApicRegOffset::TimerInitCount => { // if TSCDEADLINE mode ignore icr_timer - if self.timer_mode()? == TimerMode::TscDeadline { + if self.timer_mode()? == TimerMode::TSCDeadline { warn!( "[VLAPIC] write TimerInitCount register: ignore icr_timer in TSCDEADLINE mode" ); return Ok(()); } self.regs().ICR_TIMER.set(data32); - self.write_icrtmr(); + self.write_icrtmr()?; } ApicRegOffset::TimerDivConf => { self.regs().DCR_TIMER.set(data32); - self.write_dcr(); + self.write_dcr()?; } ApicRegOffset::SelfIPI => { if self.is_x2apic_enabled() { From 1f3edfb03f9ac99bfe567b73fb59779eaac32235 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Sat, 12 Jul 2025 22:26:03 +0800 Subject: [PATCH 020/132] Update to newest `axvmconfig` (#4) --- Cargo.toml | 4 ++-- src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f5b539d5..da4e2c2f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,10 @@ description = "x86 Virtual Local APIC" [dependencies] log = "0.4.19" paste = "1.0.15" -tock-registers = "0.9.0" +tock-registers = "0.10.0" bit = "0.1.1" -memory_addr = "0.3" +memory_addr = "0.4" axerrno = "0.1.0" axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git" } diff --git a/src/lib.rs b/src/lib.rs index 44c25247d..9c08909b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,7 +82,7 @@ impl EmulatedLocalApic { impl BaseDeviceOps> for EmulatedLocalApic { fn emu_type(&self) -> EmuDeviceType { - EmuDeviceType::EmuDeviceTInterruptController + EmuDeviceType::InterruptController } fn address_range(&self) -> AddrRange { @@ -114,7 +114,7 @@ impl BaseDeviceOps> for EmulatedLocalApic { impl BaseDeviceOps for EmulatedLocalApic { fn emu_type(&self) -> EmuDeviceType { - EmuDeviceType::EmuDeviceTInterruptController + EmuDeviceType::InterruptController } fn address_range(&self) -> SysRegAddrRange { From 19979f2ea639f516eb7665e1c3e22b0402323655 Mon Sep 17 00:00:00 2001 From: Luoyuan Xiao Date: Wed, 23 Jul 2025 19:33:42 +0800 Subject: [PATCH 021/132] Add files via upload --- doc/PhytiumPi-ethernet-arceos.jpg | Bin 0 -> 586927 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/PhytiumPi-ethernet-arceos.jpg diff --git a/doc/PhytiumPi-ethernet-arceos.jpg b/doc/PhytiumPi-ethernet-arceos.jpg new file mode 100644 index 0000000000000000000000000000000000000000..31c8eeae165c7c9da1cf0007278036af6621b39d GIT binary patch literal 586927 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

*gi-!ypV+smg}YoE9SJ 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%0AflvyLyLYXe>!w{#q$970;Q#L14II%b8&sJQ@vUk z>eDpjk_qr3$M#KugUV|F??aqu2pm+Zj-i54>=cv8uYpw4Hm#Ea4L0SAM)Fm?zh-kF z8*FZ~n*>cqIW-a&HG;^j4@Ib%LT77sc%p9iWV_3eYXJB{_*c6tI6HdzdYPE*Om6HK zL_6xh`&<&o=6wxtXO8OtIg1_}ec*WFH1iv@RtWiC8`nAc7w_(D!r6%Cz60lvYr)7f z`{VR&!aCQkXP}JGd!Jq~3R)l>k&wGPu-ny#4^X57TYDA?`5Lu;IfvBCkYJCc?dv>Q zLW;bi%^Nqf?|+mQ{^A*c^nlR!c0rx}4XyMMmfKhJKh8)&*L09^y?^z=9{7I42^)(6 zcm$)5u^_Z^B9M|HX?xI3iHr#Q^%B%p{w89z(bFZqx)uFtuNV9vu5&-I;M9#5wTG%T$EYLK z-A=kJK&Xz>yk_jJvC{ZtTsgV1PXj1GbVHP?IAdzF3+-eaW2GZ*GMyQoM@q@!pF$%M9N^QY| z49rjwe+p(lWfP5zjNWfrUe2>9kbxzR#POO@@kw(5vU5UiIS>FI3kkTy&p>!?h z;OxixZXi_NH0Y8E#w91=Bndn~JQxJe7yO~N%3@q{A(b~=P}J$ma(OaLMWM=pHyy+-N*@(zkT*7U_b=YR z-eh~$?Wth(sW-iAN-~0#0CsJ{2@Qx^5S{PeWOwr5M&+b2{%fnO=F58vBjJnlCc}kJ zbVy@@^0`$$lK7r{RRH~uss##+?qFa@yR4Y@*FK9?L61v-_crrb;luE#PwEq)yB_Zh z^m}c;Jzq;$&%G%k%gPKTYFB@;H$GA_2t%a_!JYQ&spJJ$bm{LQP)oO8Ak(eVF0Sk>wg zfQkyU!0hZUdH9lKK4jVwQPW>dcLNfgi*HlAnnRV=XIh_sauu*WhRnSFCK>=a^3;m_ zsIkd2`?0CB1Jz$ziQ$1mh)>=!? zVOA03;?0wN`!G#hjWNgkmaay!7k}r>)tfH@$Cs8oH)odSy9=4c>%b%o*8hgKPxLtm zgdp^zRefY7i5zO2Dhj^ot0I0D0Sh?gBSe6v5>;-UB$Vir` zx>wGdMbXhbj47dWq?2fvkds(*V>XUmgXY`U6*QPAAk>{$ffWORZ#y~T z$Cse4W1Y&ta%_e2@CEqFuo2Dg*5gTe*IE2Q^cJ8nVR4O zYblf1iI9I>Lu!5oH~T-OX`(HKx^45rjTXH3=YdG%+xb5^mWxH2A39WJ*7 z5WRn|x^sa`#>VK=KXqjzF2w-byc#B;I{10AW@F}nIqdtC0FOtHevmcyw0g;7g2DMg z**n5|KBl!K%G-i*VtEFe6ssK95;$I+Jb$;7hiQJ`oo0q3i|Z2{8798l;KHw!GXC_L zZ;qPuaI&1K65sd`aT?waL-`LJocDA}(q@N`oZlk21fuyX&9To@=O-0@%c#10gl=R7 zj~Oh%iJi zJK5+l($(B)?oSo1*ByMf+8f$6kA|4G9uLW=RE#`-623{Q+680mMu)p%Vl>mluF)}uu6jA)@_eE^_F3#9=z zW9qYHN|WFh1wAILi63&jt=z?OmUWy98caFA8g+fuN}S&&9i@M7<{onjqX>T5HYo!j zLXp$WF=s~a-l=!8x79npx}%WYn!v$=exGJsuc8;96rHd3MLfNmXL6${t%mK6w$4rX zUp$;#Qi1mdWK0{M;4fY$uoNEQ#L@oo?mz`CCs4RCUA?p?$A#vurYJ^yEdSndR+Gad z1bvJ72@s^{$#*aQAfs@mWCA1#K;H#AfKq3GFWQy9UrIeMQbcH;fZZ-VQVVarY$o~p zs{09{wk#i*BM@Kx`f-by%?N(u6FVM`p^C>wfEl3eOh)x0+~F^E}PdIFbX4?XYgQ809p2qIS>2n-^7ebITpOJ5A@` zRf9NUGMAo+G(?8cI}mI`4;$ENurfJFiL6RfEZ2J<*plK+rf*L34Q><2CVsMnurKW# zm|GtBSA*Xkt!u`+Hn89Mi)Xi+X|lBHG#$F?DY+<84wk-kg}o-+kpK4T78j{c8+fB6q(E;jJa{&fvtUSMFe>j5Z2LIw2^z zKsCxJ5V645gBoCebhyiJ0oGA{2<&k31}VM!pVNQ#>aFG4KYLb0`+U;x&{E8@cZ~a5 z7{Es=H8te}v3wb1d>D;bd-+ZZqG%6}B``ImOrxH9FHdbd%!4aq%{a*NNmEC^p;0f(LJ zX(hAYTg@-79}X<2mmNa3tIZ})&6O>Z&KhK&9$)$_8g{ljgPC5syM0>bT|fLhILvm( z`HvrsODVVRL^o%)SgB9W$^xwOczcIw+({ar*_FN<3@v*LDl!- zb7`lW{^Ci%#cRG;>Mno8%1fs!^%M%rWP%w~`1bpBI0(xpVjnu1r%IWA&-KzMWF;Bx^F!JEb zOhz=7PL@}D4+E%Ex6YCCI!DmQzj*vy$2s;lXH7YTITltX^JAV#Is7Y@H$>v6g8R#W z29N7u+dJq{{2_lRBSvm=6ir(a6QOQ;?*~IZp2RR;*@o83w;`T-AUmK?nqvZ(%>3+X zD7RAIl0W$zv#<%i2UeZ73~4UvxWOxAgR9#ssAK0qq_U&$bBWCJv_Ylc3Lb|kUeXll zX(nbS?BB--evj<%xxjuI+93q#_wwn`XMHPC>+!Wj;>$~!7bYFwMH|1~k#Mw9gVBbW>RLEwVE|UGyiS`>VcjEd<`o^>( z{&p7$Dln-*dlq_)uY*y$mRSThxswBDuv@jE+x-%u)F+_k&719oqUH1afDGV;_qvb2CB!(X$tJV#jsmUko#3Un4*{R09m^oxJ4b?;YM};KJ z_&jmPUI#5oB6n64!2)gMjZH1(iwisSCT7B#j}&dZRH0T!)# zXvBfYwBJlUE{gl_2&?|*wPp9t)gwrh=>%wc3_w4?&b9TKkD9Mn5eBEpXTSwc4N zIA#TlxuMJkI1`AW0Jud@`!A&rV7eIxq@1_s*EKYsY~H}jBG7H4v9Cp(37rWH!ml2u0KMXv*)B1QcCo;z%&bF^ALlYp_Ji|KD#yv|H)+v*Y z)>i%DbEg4t3%Tp6=Xbmk20R~Ap5mopt4z|Oo_bZt7Uf(gMVGL|U9=unmz#c-%sN>j zaOo!=A+B;#^JObMzkcr*Mk(npGFTKyUGg=|eYyO&VbzOX@|%a7=%I`Vuh&3ttTpmK(|TdN`PG5-oj*nH-z z(W~|PcB+|iP$rzdxoH|S9d)RgY6BbD{;D45a7h?e|+l*lWIL>)xU39^SQ}g zp5b}^W@}8Ze@zoDRtVv7n>~VQTD}VX4ogQ=`;aD9i?eev*xQtP zpmbqXKFFDNx!s?usfUH8A?(FlDeoPPB!Q^F$waBpv%IGI#^$N=*?7OY5zAesqzV~- zlSJ$Vjl%7cqlg3^lE?s;H(z=+ z1OLQkd`fFS%od&uTz;-)R{fthF#gxCfXhjG^#Ar*GW*9rRKWq`$o57wK!ibA zoHLLaXnAfcwdln6kU(LaA8vYZS$Z$mK9pF?bD^d70}N0j1j;*%$IZYwrY*l4a?&#z zz>PrVfJ4M>n+Kap0XUk|>X4u{Kx&!!^8im0?y<=Z8&81-Q-Tzk$(xHH?a5%_5szqF zoo9>3DV{%U<9=ZTQSYwT|5eN%xhVO1=^?ne{Eg71g`?W=Y$h#oLR% zhA7uZJu~M>M*GM5f*8R7w6qu6xIEH(Q7@KL!zCj1B~))I;Gq)UmBCn#JG&xkp#Qv6 zFh|kcnE&&S$OW-Z6Ic$7a;jg`i{(_|!F7&m?}e>TKIQ-&?5`EiZ>^bgL(OhCNrryX z_KbbLAfjL*8#GV`N4`3c(3PEhzCJR>l^)YULIUt?}k+zMjo*pJwV4Q-5#~_y4*Qagr1g`MapPHzM6ppC*iH>SEbNj7ZQ$c{SE{#vi-{$%|A)kPqIv& zJKNe;aB)t~{lU_s@!_qY2w%`GFueO@FHjLzNLNZf@5^2{;wtsTG9$r_Bt?@zxBUs4 z2p;JEp|+iN9duW<=8woK#KDNq@W9#EMnX6}o#Qsi8kJ#QajE+bd%a{It2?3`6Hx+I zGj9#>z2DIMEne;8LXds*FZ$PO$Jv))-LcT=ZU?s8=&^WT={#mQ8_l$@+TJ6Bt(A%C z%}i=y0+Dh~ue;c%rT3SI)9=UcAVD9Z`vgNI;S#`+-DlyFxOAXWrJfkHUDxs;b*0bq zz(DR%up)pSFa5*DjJk+E6icLsdyZy$EL%42Z~Xc>YNAv1F`ePo3zk{APD=Ef&PQz2 z$?(8*=;|kHDX&G)LU8wV@K?7ngf6D6D_rgZGI*rF`Grq&o${I6@h+DAgiNiP^!u1@qB7N}h+j_03<{uEoKZ%hvJ zUByon8oFtO*o6T9%cj-5dhD`z;EvI9KOB_?TCGBxEp9 zGjm#13}{7oBmGf2U`k8es@@3*Akoio1BM#>Ww|Q|Su*OWA;k*h%b2Wb#FNt2`s#kW zwu|~AIwUo&1N@t5zS;js)q!<*!8xtH7s|oW$Xt`d>?+6M&pGY~>h!NCBtuGslJ{Ri z0sySF(fhe344Ti!9kT#`{n7;x8d`cA|k?hi46gj?Uk%v5LxE~ zuuea~Sc0RN5gS%<>+IvakkA=m(oDE?`Stluzs6G?g??yK*;73V-f20eU^R^W8jNg@ zNuvX`tv`K69yoNmxcI}j+bz+}*7*L@0RG1JFGtk_LdgRyBSP;v$O!?o672K~ScPVF zd-#G6H23PZW1376S2oAxmb8tIfB5YqxoM@0MyX;vcl;{WW%tT~#jt;bL@Jahr1He1X0l$x%*=x6r`m&3eFqbU$*p%*OEpSMETmbKG z;iGw{uW0$txu$#hD(Cg0Zu+-J@65;}$r5bO_RJ}JVNrCU#N$4K3}E|yx|JuWu^1C0 zS8;}F&c=^CG4DBb;M}i;mhNtBc=vh8f=E=2d=S3^Ya>G+V|h^9@I1{fXhF}-JDjhRc^IlBwY}ByQpH4LwMwx`%8OslVnuZc3sr*_28(=Y1j!dD2+=X9ad_^6_2z#Ov z=Q!LGn-UW6X9@Iw$WdVYwJwL)%R4Phh1GFMr=cRpGS(VECsO2aIMpZ3iXa5-!1n*=r z$A{;$ggf|LE44_9x@D>#5B*=Moq9THzNUur}m8K%SDM%A3BE1Dfq(lgv z0D%YyNEZ+g5F)*XNC}Y|O6by?gdTcNloBE2{nz*J^Zgy~eULGdv2vEOp8KA2UQ@VG z^7vg3v6OSm940`_en~Q&jy;kk%@-j)!UXfJTl^~T`e%uz4t(ly$3=ci{`1Z88&KGW zLqxeQiG|QwkHKC+2z<0%?2zE1?VGbIwT%T|S1s?rvt-RVyLAp_lDpHkiCypClkEyDygy7A>(6frfrqi##d z3=_Eo!skWOLkmVb|KZw@p@6`^IE~*uPhF>ZobtGk@biFyqCUPy6$Q3G9pt!W7Z?cQ72p*JFCLUj$&k7)ymnmw z&>)C=SNYbNnrrTz5oH-T0Tp8fi9#Fl=n04NjF;4;`}nifi>LG!UcFh^5w78k4AjEr zZHIDC9W<*|;?XU~IbI@Yoc^S+)`QN)`!}zSnVSp*@fR!yLs*a}pI}Npr7ls< zu1|LKR$TAIrk-#pVa}abRGfXSz*DhmAcFYj zpdE2*4@mhb63F4Ux=)UOG+LY0{~erjBPB_EO_TSUnAEjp z8HJgGm*7!1$Y2Z7cm#oGrA%eQyQjIAWZhwMRmhb|XTYmFcq+Ysgk1_nFNM&x;Y-@p zSVVHeJf3_0hL*W6W$i@|`w5) zYL%h8Bk(IHtp|PEpo2$VAA?JxXdAyM5gX~pXYd!da2Z3AL9b`}Ik~#~cnY2I{*e@F zyuS8BO2Fu$bZT#v4(YWv*iWT%R@h)ni?ikdyAy5MZKjBZQnyjB5zP02d~2}Tw5rhf zJ_?}B+RWnTB${hPJ!@D!OBP*GM&t&PIZ#lg!lQ*Aq4MuE8WenYm2H$so9u4k!@-?D zemnSNU3GHj)6jDK#+Bw0gYg42_zb{E)#mQ+Z{a*UuXzOiKQ%34NOIcJeP!BGS1XnM z7=ntZQO1k#6(jW2p6amRyew?^p1ntO7&=LmPNovA*GG-d)Xs9V1;Bdb$t5_?thHOK zD%k*!0i+A^;qm0sR8f8SlD0qwYwc_~Z*N8X=WQO|#INBSE6viIw_Kt-#VbU^#VI`N z=P2R8TZP3N9zy}|XKR(6l@cG>_#=qJ1SaJ2KV|bGGLKEuD8cw3pQ!G*#K0U6*ka%zBYEJFv%e^9>R2tnrT&lR2D<4CDXm46Z3*#l@J=`P zdREeV0P&o`8PN3Cv;AOJ<}MzGE-vp^1Q!MZZk#B;%YtG**)89%2|KU(aROZlN2Q*I zC==@18y`$KhK-mk{DIMkRPNO^OJQC2)p(unt5sgz(2cA^cXPtV2f#&gk(|*k za9x#qVUw9K%4qcsbGnKd?P2v64|9mmzbx_vwjoZ(CDXP59q(35Yp9)|SR(DL)tDJi z7n%)8g2QoGYcs2XBqv)2SVsn z07i^ram|vWh#!26#wo5xmUI?tz}=C3Z!kvck8b=-aCHCm{lUJOv3eN3j0`56M13dv z^?acC5s6^DvemDwh0p2u>={*r@CUkRPp!x5>ZpEy#246PFAxT~u6l=@r2dm67frcU zwasl>>L{_+iV*8)3R;`sMlK^Rk$bu!zPUGvRmjS0&gPv02baH}Zq=%eyoiX1*mbK+ znd|q|osq`3DpAZyKsmOQTVqBimUW&S$_IKeW(BQVXW-W?bTWD?N8dz?KziZ>dJI{1 z_(ew`t&bMm>{LeTz?03XIxS5WW8O+0zdaS--tdVxrv(aPHln0>^hxYVfyK7v1?ZLu zErp9UQCj#wnu;s@xYrb^Vppo*^ln{_o*l4YlN83`Bk@g|9v@gN!!P!f&|K8LZS3;X zDAp{!q>%E4TtLJSthc$R(ugOxAm>l0b#3dN;ad#7w_^-X3o&PkGzgl8tpFQ}FQ8I~EyKCqH&LugwBd-j!2k@(r z14x3?U8h>vrO%4puq27Bj^O*n!(X};bR}*%UTEKGr={aobZ#5I2CaC3ttZlTtBB(G z?~VKR$ynA!j!U{Pr{Bx)ExSAs@jkIYna#+*o|)CEV=bP)MmZU$`5Tx0TGT~Un5k1` zr<`m`x&n%%D*PaQ2~t+f$5)OFyNV}p=F;awi}Y8NbhYK>P@1~9^NFOJ~_&}yx<#U+cU`Y<2Mf6kPi-ziO2>;zaqnz z=QT8|_Tu&{0>V zFEn5C9*JqDUs`GkGG$PU0NvVG2GNC;5M>)6mA}g5X81b{72F5hrCD#wwgpd>biu>z zM9I>GodnUYQ-dGA@-nwvnabbu&r3NP7i9^~e&H+p{CF~Z{!w51Y}%Z@2EjZ6Fi_0u z5Hn(x>6P&(k-Mukqem6`FFRR1>xS>?BIDmG81G(SEV+{2c^pSxE1JqcO~uC>q`@=CFJn(l*UK?n=Es1~Pf*R-+;OBiT#XTJQNM<~fOR za>dY&e3kWV6L;<0kpL6SbG(GuTC}tnA9@SXjrTi7o;R?LZmT&og&(E9trWhxR1oi> z-%@i`c|uj>{ChLJoZNrbWyd@v!%Wxi2X-y#bS?zxa(B79b0%_THny@d8N#xVO9Bz6 z@K}m;@Cud_x-i|*%7t#SRpr6dr2Gk<&2P%OXCEIvYvL4Re?=SVjZcbl0OrnN>P@LD zZV3d7{kw?_45bowwEosK?lDv+r5H}tG}roTISLXo)zcJP@&*ALhX+Oa4&QL&E{s`j zdGx-BLov=qWWF~*!q=qq_nOW9wL}h-%zD};f?M?lrv2RT`8ZD(d)A8Esj-`sM5X4N zC=H=*k{AoD4avpRePQ>}^w4&4;oUE6$(wjX9OTL{acF$`AW@mQ6wI>4kzt-?kmp!& zp1haq_q2a=^k*`y7AGUhSWJ6au1hLvk;%8k?K*gQ@smt~2GITF3vT)Sa`-}MTyy0hmv;W>1&d7ME7fX>uRAi{xtgWb{!hTGeU>7M zv?nt!A05OrE1}3WXPwl$*_z`|&N3p5#&%y$krr;7z4E#%TX_4Cc-uyiv}mKV_A)jh zmxlqW~?w6vRQZel?B%9MLjXBeUfW5L@4~14ZWUF!d0?~sFUc4UMEuO zRk>r|j!DWO>aFK>*PoT*-Sjh&IkE}grFd$*pUQJ~dFFDH;VX+=GlQj2jrO1+G#GLx z%5@iM!6s!d;Y}R0%aS0~*`%Z_Q|Wxsh6az4uJuPEKc> zV>C|3PA5w~oEm`tg|GWIo+P&NT0H~%5E08mdCMvu3#Uqx6C2ZSTcFo?%bz2;FWb%< zDkb$Rlnk6{QlJf(tf9Yz`WBfMoIYbUmpNF7@$pjGQhe-&vgwi4KOq-J8?&|F}x#$RavmjH;3m#OzCYGlimVX}ctQZ&A}_AUa7Q55$-T|&#+LoPer z<@j4uc_q|ND|mAFB8-)&mN@-lWH(gS)su|1jt_~}QPdxJI{x{^(-WAWLOYe^Lhmq} z-qK0Cy8t06{w@F@K)iE{|A`32)EWR9Z@2Yz7*hiLMDz$?~t9iS#))Kwr zfY5oFIS#Gz5+*QF<*ToL0b_{3B}&>ii2wR4(*ImuDZ1Ba1!Culy`_531?XEkj$8s= zfl4ULEQegQj{E5@+yb82XvlU6bjl1*m)8W^>AQSXnTeRlBefd~uE42eLY1q!ZUO7= zt_toHk3CZte&v(ez8W@cHABuIb_}$0+eH(GuS4;(U3T20+utleUCt7P;W`%dj_F*F zUqzWW(5X%^v953o2K<$mo|vw;4IR55u)>hUhK+b3PJL2M@~r2M@{fadWQR~KhIXAp z<%j2G2v0ZokMNjw=J>4#8loLZY~2UEHJ3vk*!%Y+Z0xTvBqLsqfg6f6^(m0kYV;Z1)FArGzCA+dYTRYYU12Ld*v#T-2bCVYIdrG zj=zq*=i!(A549S5^V5neXHLbgJlD3@pzbmxI#-$0Z>tue%u~4&j$^JvM`jOI!<$Wb z%~*+sVC8ho!{*oO>k2|%QCf7DZvKvV8S5sd(-fZ{`yip?e`%P#K7KNLzGD6)=_o+p zXclnWu)RD7v`6UVpwPK(&$&O47}kUxGmw);{(XZGGecwVp`MeB0A}(rE#z7AxB;Tn z#j5fE!j!l6cuqVRXRKvW=J5GNPLKR?ImXMv_2jcmZoX7=PKvJRmp%$;&u6@$%ZTru zC{5PVX{Cd)Pkc^E;}(AXab#n5?t@glmIt=Pu(r8}z5;8`>b+D z#d+Kw?i1k5e;#U11IYfjG&--7e@`VvqosD;-;KY%{UiJ-j;YXBEl(y_hcEQYTSo-~ z<{O)iBBZ>!z}!qRN5gom@-S{s&0q>)9P(^MXGT0`nJF`eI@g}_*mOpO>9a zn}chYdfgs!axZu@eIQ?3d8^%0H|vMZCO#^zKwGu`$;{y(6|$d08P4`!o27tkOva%l z+EbE#ajqV-`r*g>Vgr`H-v0AOT}lIi>}IxXJUjtb)QeFx-@B#Rt@;ivJKMO+ru;=o zv?Ljs$IVadHxy*n@ikKiA^Yv>vT`V8L#AxFTO%X`?(Y2dIJiovE&Er<{;W@raJ+Mi z#U#wS#Mag7f$Oo^O?`-uP^0F}Dj865;(e%cRb@*C z&g_YxS^lY8>J!E*^ zB`);h`tST@9W94^HuJP;(gxh^M7q93mhb-9D5nP z1VtT!5yOu(-FVFW0v*s>7^?@lzH=J&4UeqP7HYrkc+-`YgMQjx83B9ujVP~bdT;7a z%T%NLx!cP!V{4t+B&%^hJu&3n9i!BjY7p>_t1LmE5EtdE&BnL^o6PIxB{Qp@K#x={P!>+yTxq(#5rSKQ&1GZK!CA6BmP`fAfpkq z{)1%+hNara3Ujtt>HRg zTB=JmNUoe@?3BDPGvDFtFTWGp&pTZa@$GH#mkD&RwdS~E@#A<@f9J(`<`?&>I*r;6 z3uwn;@6qJP-g^LG+>O|Qg1HYrfd8e-|6{Nb2fQ{H?15_xmswvuwE(O<|F>uE{|9f1 zpdCx49ZQeB*kK=JW>g|lwxu3vP6;C#h35j8wN4V?+0EOM! z-DErWD|o|*ti>om&hb9JnN9c6g2hAlTYSuzrpeG!zsWte4R~!cg1+iVDl(L9JX9Fu z8dRrxC+_v6Zw8T)a*ln)^8S-dNAJb%>QaWZ_w1rAunuKS!rRfsPB1~@r7Mf<@%YsK zv-gAFR=aXC0kQ_VXnsC&!n|J22S2CYzLHCCrQql=Qc-mc;Rz1lV=hbQL%%xm! zG>o+hjVz(OAo1mfRjF3LYfS58D5;gjH6+K0rdxi(dp8Wr@7aZ&31|c@^7|H!t?76q zL>1d7Q@8bw?}w*^7B{=;T3O}>GU$rOTdw+x@As4m|1oSRJKSk88{5?{n6sUo`{>C( zrL8N=rqpfzjcq9S<~Ncl-2i)YX44qE%8qqJPbk}YwhV%71)4Pi1M6A*z<2HPOw}hy;DLNI$xHF2=TF=uMlzUM}6YnHX zyacZe%mot{b8FEQN+RP_Hk_PSZOH6b{06BssRCvoyx&}}Oq9}Kk zGn-YG5!YvX>(gJ`Wj8qeOpDV~PGK|p)kFK?A?vjm+Y01=GzQq@q-wWUX~)=qRG|O$ zx>A>&;Cb57Ec3P5?X0x5r1-sDi^?qXEcwtrjQ`}uyZv)%Te=PkCI8$AzA6{!I>teu~G3$qmO9Dam1tfhuIMAcKII8 zDyicSM3s_A-=r%$Q|6&k_0b?qbDFOjtKs_mRswHq3D`k5R^7X>JQZCrklMvB68G9Y zNZ`h{*S5GWc%muUc^LJTY4>csd^@PB0=W>r1s!wOA5kqY*+2qlxYKP9T}RP27;CQ4LKFG@RW{;i+_mrq^Iq43)kmB+5WgBn0Vsk!keYQOHRw4K4r zSz6;k#OCHRJ{32|*8Cg+|2~BMv%8ogmj!`3(xnVf=#Bx9;kq)B`$lt@eb5{GmIcv_ ztj>1vQ2$W{Caf2gg6*!6-YXX8`S`J_(i3gAQ(xE!ciocwe(2ax6Ffa=492fc2XMzX zt`~^R6P^T^OZl!_f0UoAQBcY;xT3+rbj4L<3LP`1Q#>U@Z{{*~pKU+qV{*xy)+I`R zOW2Zs<)o^m0_YTQPd|3lF9V%!QMJr22meR2JG%tpb_=B=SCntHvQ9$8(acwMA+A1` zza{YD7TrGVCe$x~r7@r@g3=Q(b*NAL$|JsOqldr*6|Mh6OT50`s*w1cep_AWLh500 z4qCSCvXbxgk;?1k0E)08;v*XIG4~FcgJ8(J#cov>u;@It0M^TqdUelHOz43s`?Jrc zU-++FJ(z*nlKsjl9%NDhOp%zL4F=>B1igh7`0nq_y~Cm~%k`J2$ANdoR3HCZ;RA2* zsBu71hRqGwYLXeo-3R6qsLUNdI^gTA`}oKE2I17d&PeTIDA01vHEs0YG3R$Hm} zZO_QyhVzd+$)XO#gX#kiJ(ZWdVSJw5daorZ;wUbxd?3Fbv1ZW^u%ezfx(~8*Y|!}i z7-4;TlLlF_)3K)9|oU?p3B-zUIBB>8Qk0_}al`_fVJG+*#KU9clZ zAer_d-^6tUg zuwvTUG<%!iF|Vfg1KTI9EpR;n+c_7nP0-C&86aPC?J!$x5Aw2R{0)fFsv~vg#dx5a z$KCO?9Ea3^zTWEqQ2W7o@$!TROocd$v%5cT{+wh`P-xRr=HhiHTdF_XJKRO{lxnRXg&ES#mC302rxq!!Zn**I+Fc+O|Pc( z(mmzeqm8KrVSu0v_A1^Aq82zg1j)|LTHETf`Y?S^EW0-6)m!c#R8>-Fs~uA4#vv>B z$ax?OZx%DY+5b!+N2ai$1%6A>giUsSU*B>09Be-MAI*iQpvHKAU*|gvV$3>l(J99{Qa{8*aH8j?V~%)A+1mgzHS`?ZZ$ z0^o7keJXzt3&UG97&m#G6}O%VnAy2f?*Y(9)rYz(a9Rr&ZugAngJvagg|Nh}ooRDS zjJTu?rIzo*52qCYtCeOUBBp2Y3XHK;t&YfqE+^08(jVWAzFvh8-YWRrA8B=L{2@$Q za+$^NDFa5H?eE^kCv=O!*tFr44ARi3u9`5muzjri2E(6Rz*0uOVATi20G@k^Gy@ib zSGi#l%BO|))zNC8IApHY5h~h z4Go?hX|*%%iYz+XTQ##XMRpCYMxQ)viyu>h*U$_G8}5lNFS6k+V2V>RQq0?NjQq5t zd;3*)!#XtYzU8s~v|KZpa-Do$4hV~tD)%kzHYq+Ejte5Qf>h$`3{^|HFTxY$JOJ+WFVEKG6r@lJSoe$fd z!2djvYle-%4#)L@?!u3a3I5-$V%Nbo8av6?B`PuDJhr}A>kbtf`$G~jrEqO&q?OMApabrPx|opvIN(!!e3B>#)}jJpINuH4laYihb!`Guk$c1|G66XsUu-3I)FsiPE{pmAjts) z<`3u=MZMtcb2XFJw8Ml~t7+!_VQ-CgZl7%<9$pBxHxFCf3Z;!|-#;|u*a?uUUe1eM zd7e1W2_zt%`L2du1FM;n)M8w$Udgj=DXW z9wCu{MGU9t2$KV>WrpEdnApP|2-8-lE_Mu^@JwPQQ*c(TJzxSM3n`LHA?w< zKXju@;IjMd^1Gy9$8znJbK?zMI`>uJm-6Q;wXbPFsk!ca(@gndEBbG14k3wkb4q%v zQ7+~vM0ILuT}J})HugUAnM(;XdD30Xnsnu(hrqLGG@s0Vfy+Ms*tU+9lZwWkkms=i z+wdzNF}OAA&%2$oa4MK8q+1P@nP0CVUpw+qS7*YqA?&`2Ef$uLOb=Gnwsm!*PzNOMLG?>3#=%_ zgY!Rymo&Bwlto{WV^$V#;Q6G@NmEU5=bviHfk0}(DXjNR;+pr>rIpxPsV=n`>9Xo< zf_a=U#_6}cd(dh(U{;Yk>~R~ftz*CYK-{{Wwe@DPQnzsJ?%%K8qZiY^R3 z3Q?|EWVfB*<)93lB;F<4J-LnRS-V}ue5HBVoWz7h&4o09rjGaw4kB+Y%=dXNYd_^y z*P$JkF9wmLaY4wU`m{1~ws}iUeNVEh%3O`G1?NAuR@0TF^pqF9kxZ|rRPT{}aNIrZ z2a%!TV^6?M^AE;5G3+Pip3jG`HzzL)HoUceSa?Na_#`r5frr!+QF$n#sz8;8-LHFy z@4Tg<)?q0M{VKR-_1Z)4$EHD`kj!7J?FF$h@w+2JQ|LGzo?qw1INO0skKeBD$H~7( z7=hJzv_=f*ka~&fe<24?OeRjKSlS&PI4iXA?~*VH#tGI3S&vb`0U~=t(RsVR^|>N0 zMNzXJ4pHh&h6AJiq%0I6ZZR^Hf!t0AUc3b+k~MI(P_TZYQi7V+nN3hAQl>m^Rigj6GLt~cidS+ao zaJ*$sQ;MPRSym|^c~{{o70qqtxzC2btGP+=bY#IlQg03Bk%u-czCw-f3Ffxy6p+*kTJR^FfDfM}N&8oy%;BbdOcQ?&NnU%6N<_ zG~ih5_IC>dbhxZ&CDJpp3^cOgo9(eOE2oAr6usrm63hrpqulk!JjshMXZMHrK=@V9 zc-o81u*rF`o=~OHx`W*$Cg?D((JzZVb@w=^2=_`dsJ?gZ-idrUws9KXm(LrRLT#Ko z6*vz5N#cl97FnK`?r$9{&LB|JE>FC)y8FO=g3Y0Q)2FvbW(p=ddAo%Kij;yqWvZxr zu^^2Zy}mZkJsA3S9>Owo)n)eJ()%@Q5l`^Szh+Cq%s%%vh#mZRP^5pMX)r{r)~5W! z-M;t#KG4u^c+xOVTB0QkTpVI;U#vgJBo{>rH~L%5H@q_)zH}mle4h6U3E%Dujq*sx zV2^?k+R&t~*1NUDuHpy+bHo@~=((yWx;4X+#4&sHq`uL17VG7Q{bk(T8z^= zma9Va&3Z{om#)S%5z?TI{>}BN) zUT0}7E$M!7@vyMI*)~2Ev4?nVG?I^6>Hk{nj@j6Ji2qDkUIexqOOa)(sfbe9(7m(#WJcjQ--O5&f;7d4E) zY*AY^3-vo?;%EhXaKz3X^8v(3*{r&)nzvEwhNUBMoUn#SI_Qoe&YCw)`T|tdI(&bK z@Z2r>&yfKbC)GEy%$II>*`Dm(%@vyfFzs?vt+!ovma#Xb-2kn!i>5Y8$Y9IS_d> z+v)P7Z|=8wldQ-#_J0D0gGS*uq45UOjyS~vgA)edF_qo;J;n1GJ@(( zW>k#f;kAhtj5UOe(1Kpmx3(EM2<34TxDNC4S?$*OBh>G;hb+DuK?Uajs;qx?jscuFg~+y=1yxKz)1#5P<}pHX<@lbSjNR_yE!l^R`1 zzkaJLYXQMGz|o**tL2jZCitfrPtw^;Wx6-6S~#^&w8b&l;6IwFUX$g%yTBhu{-`Ef zaLyfI>wDq<<#A=}Y>!H+$kIM-N;=*9kESNJIP?tO?6#gvty-Zr!FTgpgD+FGH(^6^ zAj5~efhDDifY&C#eL6O+^n0xVAEvxh=I2l$v;c=6J|?Z6Np!~tat+i zdu8L-D6V4{(N{CVf=^P6)PBnIH~H8YAW)#Kn|hajgzHFNb&iF8jOF#UXVy4)4ezk6 z8ZNco_OXQ*T6G>nf8XxONd1#0bjd!fcMqf%Gf?eEVyYDW^8OURqbJc~;A~(}G&<3C zKuBV8sos9n&N2snbH6b54(I3hMp_)4Jx3)WrE^_`L3!8X^y(R_#%HS?Z27MQ{}__q zhV>m*=^2w$8_H)p-~82JYiVwUJ?gyJ&GOPw$c3+555)z}@UOL)lc`QzQ*Jn(Ew3v` zYSw<;R`npzOp+nZYPyvgu=~;zsnmx&z~&4F>m80#go;5{zb#c(r)x{HWg7d=&=ghDn3aPWKU-bmPSS6vZy@fM`IKp{X~>q* z&8FaG^nXz19-PVI*R5?P^wCB6z12PTYfCr38*|p{rOr3TPsSPE+V;=Da7-;aYqdZQ zzs6sC^ToeEYaNwADDT%rJ3D*C4({z>(|T9^CwARmWVbwiF0sMPyb#C;pK|ogHK!_> z9R4lP&3h^+js7~d7atlRg^)U#7t7ATHU7%Vcl(dV!>J+8qx&iAJtG>U@Aiz0j;vXz zPiU1lSKTF@V(&S4lX@;fZ444g3J`tT!-Ej1g%=JUv%9j}Z z3ASEs`Fr)xarF7r0x+J|(^?Y=yNw)Tmp@nk>HR}AJO~I_ZxBOpf)x`|@e40|~^_VQrBUCjZemzPSaIe@h%wKL>Nw7BwrX zLqnY!620vG;>HLpv|>p|7Ar-TEh*A|WrIL8L75!d`^!AKUHln?WK91Rm_KE3@UhLn?tB-<7^0Sd}|m%B*x<_D@IOm)%jd%2-1*r z91>V%bz(Ph#FHJIx%NER=#n5O4$RLKY9!D5*LZtDWb&x=GJ0+rV;lNo!3ZpJeOXDIl$^D|R5YZL1fBh8INDqF?NG zIDpqMsV~gy-Xol`1n>(cCICFfdz2q5wxDzakjxnWx2XI7=dSa)eE0i)CG{LEW+AW% z2GaTFr&D=*Q9JCQmLUXa9K&&Ll@)e`aQLPc}7$l9kE ziAV59TAYvKMHd8u8W11V>Q3S*v`DDxG|t;~nxB(6|I2OriI~EHBjzu|!zfFRpnA(B zM#UqxW`E}ie173%k_74FD~%Py8ybwdyk6TGFFwVxnLe+iq4A_SM7=mqS*mX!&pUzx z{M{W8FDCPW<4Tl_tTYT4&;>kAf;srC9B{cUNH*&EDke-MaYjukIjrp~N%Pv@cK_K& zE4-RwC^G`11ps2vs+<&~YEn|1xD>>9AXx9rb#Hgl%JS0Gm=>#t6Rr3_YVH*S<`-iE z*AC&yqiV~w&X#0b;?PRlG4>{fhmdsBFmLPhkX!VVfViNhCOFp?xRlrR#9mSq1`GI;rlBMmsu?3s%+3gSYs^bkW z(l&~P23o(Ay z=9wg#Ad&zMd_I*1Xs+%%nR<|M>0*>js^0 zz&P1qCFzoDD+l1d2NaMgtM4Ne19!=&yrQr8^^WbC>;b<-^$Njcp(x|dYTnqi28OI2 z$D+_-EHesi9Ri`U?1dU$+77)jq5R6xi}$AF_Y})sAdM;ReE+k}^?_+mAvQw-ylptz zjsviVSCI8^3qlhze2<59rey8#UEFZz#qW=bU#C7`w>dOsrj0P4UBP_VMdlGP(NE^z z2rzC^uTD^;i&Vz6JgOuQL@fCOKiHRMfAK7;|DZUf9oPB<3QP%1Esn*%x2=x^u0w-0 zBa33#G`n*@)v{;^eBM*i>04R_ntFqzK!X_^ugj^}b)9LdiBaZ zG@NbgvCH(Utsv97dZK6!YwY&0V!B$>nkU^ag%viRI3rg-K2vr=);3~pkojYJP<;kb z=`{)7$$aJa&cb8JEzOc`{?G*W5|OHJBL|-83UG$iI2m|D>$Rb@(I z-{W9;ymF<^@(E$!*wQKL;Y~g)REwtnubrTDFW=4xRcZ&ptZZ7QOMqX8V!FBaF=aNH z==i_Dvk)S?n&>pA>S-@clU5NS7m_x1X&!J&hR^(r*D$KMJYrmR*DZ8B{RJ4#}6-7CGi^6_IVo|Dmo!w2Fjg1=a5X8>JQ z)nfT_y};b{e86;zq40P!khgF0-w@V zl}FJPQrDpb6;*jj-&$5omS^?V^c8TLrAejzLlK{8jIvzU0Y6y}G(x$v#C*YGyWkno z2|eKna%;7kUl{O_%M_~7dZAU|7HMSYtvkpjm(YwAr8;Y>LWll<)@sf@0=sFr1X}`+P)#`4T$OL+y+_?Jr}Y+5uf#RkS^>kJ+1Z#dRyTP3wF7=kI%ooA#->U8;%zF;jXIXSu1+ysqb!ES9`p< zL3^C*IIPeeQZ_*L{uk=fa5f%s0Po8UTS{WEAzOD~nfDGMbWle?UvqyjjZ=M`ZHmF9 z5cbFZ5NCyNC>I2=Jyn~$5fW@ZB*0D)ab5z&EJzUFA?^h0^H?<2VTNs!drf#xHq{;+ z4l`0r$*)4blUaa<9J^Ek$267qa$=h+n09vivoJNJS-SS~$nHBQ@$YK}k8D&kRt&E~ zYYFqi@u81hPZRcIDi`MFw?A)Hviq!i+`c0ccqxfs_2r-;jngC|HNEHlvHy#Qy^szfbRJ$l=H&_Y>;!tJT3{p$n zSLJYN6;84yC)3xr*y^a)<4r45=tZK~)$KfS?sKy`d{kK^0XKYk zd}Ebz+!$>6hhCMt(IRAZvpl<4iJzXD^3@K$A*^r+-Mil{o3>Dr0=?DI%181?md)(g z$&FK_AkM|}pxNQj3)V4$Hbsq;fxlRq;NvR$aHgy6Y!l>*@F>q6?ktLS-K0mQz{^H` zc?)~RMK&DMhG)k`U1daGl|_ny?s}tK?H_IZ0~Rpbc-HCcnXRN%ua4@7gpKe5C?qn> zXyIdoUCG&V*ZM58f~8X5#T($DsvMtX1_q?snLW-!!u@Ms%|*xO#vF8XTpP&1;z<5z z*;+7144Pqp?>>Sa03ta03Rq$UT3DPnA5p+F|pPjTAuWYm+o!+f9`*Cdd zdxk-6xEN#l{!2z#E_%SoRCA6L@7HsWoC>RK< z$HaX7kEU{N?$HY0Gnrq|9RNdlMODIvnTOmz-Q~|O0lq2$k*M4Jp3?uv<&DLZgLw3> zI8P(Xi*oSni^y#OZi-0YveF>E`;lRt4NPU6a<|G}Kl~5``_cc|WZ`UX@*L zSvvKDoF^183$j!Bw(Ksnj!HI5R~VK<;kZT zA0}P-ZHFfZbXYwP0@@Hav>GyRX5uaVVR@N_$`_x~FlPB$2dVAMKloN8=RC43{@uE% z39S8D>u#ptLeOo;gvBWEbf_!<3H2eO_Hy|!89m<6@NS)z#2w@Dyf{7VrG#MyMtM7f z@zf^TVsOvW!I+l7HnkREmTZG2O zeAI&!1bPVk3QPN4OEhu6S=!etUtZw{=AO{cVUEPU)sZ|rV73OVw>@w?Rk*gknL8@2 zurSNu8-}4# zZA0Z^<3}vDG&JoHJFvDHIh6<}$RhIGus1eHQ)vVhF@4M!gyUHvy*rw7Z-t&W4`}DD zeo716b-Wd}(()Sr>Z9F-BXyEzO#$=t?yi{-2o74}&n4gx{;&WnQqu+Qb z`T4(AUscXFjh~*sH(UXIurf;}ahMU|QK5ICZ}6S^b=3_~f4jJw6&-EQ^DkwbI7BhY z`S#s*?^6LICMY~!#EMsAl-F`GR6b~RUS|F8t;Pwfa0D=I!yFqNhT(nhC5@N4o11k;fK1D@=^t>c@+ zp&#aV7X0yo{a0k&8MY-C)S>GO2F|R=402s@@BMR!=m8;O$ioRjB736oE;0ktd zbmDQa|D>(-yOTqrm|9WlEp}K%Lb>5OVCg^;(oQe4PDw zh3T8imY^0rxs{i)s~0^Ui#hq}tqz6j(^6T1VAstjN3pZrddp?G65+0U{F^ohHXi4T z>7!JhmYpBxB?K~Lc{rY51aNPLTxlr*n{lsXj^go3^8S%MGh?h~FIkxAm1F-(_`z-# zLZcQSG;Uc7Ma469#`}|%D`-T}x0*d|KZ;K-xP-h#z(!$j0vH;SK>M_vV1)VfzUF#C z@izPCLiS2i(~qx(KlV0#S-IDW{`SQ8VEuW*ViNusO+Z|w2u>^ORoJ>^B@BODb@kXU ze5`lp%&(2har%RpXN!ZZfeW0qapXWS6WyVH~J0Ao^+;v zmYZ2kmK}r~2RsPlaZ7s;YOKgzO&G?5asCSrC;XD4j*To_ri1Jd`9{A7WcbajtwN@;1dW814 zUi<#+IA)Uev$W7hlgqOr)v0!Tc3q^Mm9ON6e6r}vSp34>k-Ereqx(g()>oSU3X8vS zi2_blAEf&8!jydaNO<4Li5AOg-O#uj{QY=`JA}MYGf)t8dffkB86fecyy+HBeB)BJN3W4`L@9f#x z+1WW~zwUkrGmsBtCeL#}*L`2t|MxR8_5Xei8BKovg?f|Sm=Sm1q}4uFp8hZx|45c_ zQ)`mkv9&3(f$;EHVpqE^bcf-S#}7O`{BLwPwzp$#rx(bEAOcv*#(G$Nd{8jW;XQw( zAzC0jFmH@HyKq^Xi2V24Xyt}}yQse+MWi++QZnKzyI(&P(hgkSnJDxqgx=#|5)lsG zxpbIVi)j~c5ETSwIN*hOpDcGxc4AU|{#dDk=)I2mFmppb=xC|{Q#g>rIE}@vIk={G zDWop)etB{LD1(e3F@8HN`LJR>7hOF4$UTg z6SZeErpixLAK_I+OFr(NcyhwC8+k7e3wV|LdKq9-E~!aI@q1S|D)sE{-^6nDi`XV{ z>1KVyA&)!Ha5psl502*8Wpl=l-1gJE;BS!_z|ZQ`I0ff*MBb6ZbrHi1hGA;0kwesf zjB!4UBy6*T(?;=xocp#1J=83eTtgOWAz&|gek|wLS8?}d79x#~mN?nv9e+1J>0%Ns zFp&H{J9pHw9y+zD0^%gR`Uwevy8av>i2MHAX@Y}ogThS+JT|WFd8>#SL{31tW!X{x z^c;TM6`-;U)I%RH!q9$yUJyDmz2g zA?ejcZ3>;LIBuhxIWlwL#mN47LlCpqDgOQvNn+Wa1?xN!pLe{3%t-hT5!$IYc}K%r z8}@Ps^YNRY#c74lR~C4|6j7VaRti%LN@%waMmC1^EhXj_DVFTdpEXQ}d-vblmTjYk-7+IQ6;pQHzpkY<=BBNA z-V>ln$`FZBdMbG}5iA)8+f^?zE$c!dyjHfiM<4W(Lh#ps{CE9*bm@|V1>WrC6ReCU z5*Rf+3)I&~{pMx<=KCb+HNa%AARKOS+W|n;%yUC#fX_ot3wgU&Egl>t3JXG#p)Azi zLy3=Q-fU;T;ZRjeq{TVK^E3?N#GE4Tu0LT#QGJNwh-R-u(p3w~@gGoCVNYLoH!|U; zbli+qs?BeQI8$bxu{v@~k5qMS1m{Gi{$MSuDR%(|UMxd>)>t$9a#6Xkp^3=;FBnts z1q6)w*BtvhLIn`TYE1Re$r(|d4NZ53&IT!9m^X)-a(E!b@0~f(_vy$_txv-45}q__ zem_^3&qbJRBoyiYSgpTruq^Ja_;EaJjnUc&4rf7;ZKF^{Pdfb9vn%M9Rh~A}F>iJPGX?_3fQLmUVeNYts(T0WML5&^8 z70rqm3dbsXZ+<}6^yd;EOhZL2X)gphM>-lDCE4^Ie|7%og8zI0|C5CsnzA1STOSHy zL3`(9GG{wiI*G3JIfPh0^5LUlO}Go8Hn$j?T(nG! zjEDYy)y-UVqX!c-XqahuIe%c+1Zi{RPe#lNMXy+ia&IJVV-~`rt73|I5>lrcvrq3; z#o5_vC_2j!%i|nNyqlg-Z&%i+_GL%O@{v_AE@(ct3}&(WCc&^})(aS7P?FSgNI6Lt z{X;bvssocN4#A3>-u)@CHMAsB+Cd;%AX^d$&4>oFW_A>jKz{ zR>uO5c4^yw=dzsqe{i02gOB!%(DF%Fu4GIvdRb`Io*4f@cYL*{`W9g4UA!`G=ig=@ za{PV|>N#L(*Matfn(Kj{r{d8lc7mH9V{d&gkhPZ~lzOcXF_y>Q6J@>9avIhnzn0Q+ zpUii5p>)diJWRiaQ8Og!n>S%u{W+I{5e&EDyH{!7fZTSc>&Vr&*xzT3|G~LDH`BPW zS)T4-Z@7K`BbPk5SA+Bl%H4WPlX-jEC<~GJ7YMuDk3ozVTaYjhbaq@%FT;Pk>u2$g zy1QXK_1fy|+Unv3sL$-1m?8F~ZZOijX~oAidpD1=$esKn%5z;p2fgZ}`9BbRNRP&; zutEthSDB(ZON%l7{lx=QnYSO5e))L-LDC`x-|_$81b0er9_l-I43zYh!-Cc!5%VX! zUt4~)pD&DjDQ{GCQB}7NY6$`qYl^x8lW^hf~K@^^J3Qy=2C5XFlvE#&Yz%+lCt1QV98=`*MT3bn~vR zYhzBve{d|_y8I>IqIM#xDu9EVG(z9v{slrm*87vAezo+o3))>;F=Gd5#z?K{nxhlb zQ@v=D>P*TWDdXfX70xTvnP zG0V}k4wkSqKmBBbKD;RT{c0ZDk9P-)-Ge-G)iNo^K{CrXGtkJ>xHbh+V@Ff3>(+s| zo3HC1lgC-2fr94tq~Ckc-yjhIAvD)J@e$D~U2;UPM@6ou{Z?pubDJ_>wRGrO@pq-i zt_I?m_S16Qm+}o#=+8>v2^cR?GYP9vy5eNiY9@*9qjan_?t=yGr&6A6-B2?@Rg6IP zh3B{PUB2(vYQX8aoZ%O@`}L4`k*Af2*%ON;;}2u?BGrUemkQjcZ`%LV%N*0+oU7Eg z)a8POf1YQxt|_t`fUg+(xloU72MmXZ{E6aQ{XYa;uy@zkvuKVQlWt!wFI zxtJkPZ0}uTdSLlc{g^L5yszGH^xlg9ZYoE}uk7k?mv1mXU=mngSUo)epF00@U3rJf z`P`%ndtY_ZRnO6MF2A?~&viJv{udDZW>>uT4mxd(CT^Lf>F+{)8Mw<~y?vb#oPN9B z@Q>MIT@Zx1eq%&a;2>Dp7wjgGj5l-q`^4Cu^(R&bf72r2okhc=mq>=jw2o_+i6ueS zlGtQSQYJ9FGsX1&e=`d7zZ(qpO_|O)wn%ybWyvye2K2)nZ*>;u{m$B3Jp@Z~w2wbuj)zx3~Xuz)w7C{W@ z_i2*+HBdHz8Z}%SCwpz=KwyjWb4+7W(Od{)dAV|vj$}V-*=#urzt46rv|QPzc@qI; zh;}aG($E`T)_IDv$@<4g1J&0p0)K<{!07QC%E>K_o?TBQv$7Dn;{H~Bz~tjR0bAjg z=XF5uDUidJOVq0NTYqY6A}#Cpr|W-`ol!5*ebYf?ktY!QcGaH0k(Y6FhJH+w#MPdA zBP1PRVtJ(hUR_R63KVZ37Jke5kI9>LOgl&H>U_Cf4{5B$gcc%__O0+wEkz2FtvB4a zlpUCMY!ZnN%ubuV>H8_LReRu{aXog*1e+NEQh=B13HK~cBCD|2K4!^Zjeo@H3JfwH z3Du1gP~l!XjW9fza_^?AE*vELi&0ZSCmstIg}H+I%_#_(XvSWi)tsi$=gj=A`S#-0 zuUuw!uwYvc3Z5TbUq}i+b|%e!4@~6CZ{1V2)%6<-UvLp zthPD2=F{wD|H!NqM{cRI7ff@{YjQub0OW`g+m-RIm<8)=n_#Z#ri3;Qn!VrmX=RW1 zGa3NG(o{PkisNwT&>IO84+QroFCJv%MzS|Snm5mshZvK`8NRC7JpbpO{8E;Hv4*1! zf^Pcv?dhWAAqFIYsBOx+*sY>~m@5TOdyTdD;2ZlSz_8DC%$Nk3sV93Kru%J+B& zZ!eGh81fovy0s8L>$kAKHO9#vdRYy*3XkoVu1-UM=ngEO3BuhA55JfTqdmw#%i7l{k#;#<{>lN(oDcBWh6VeGjc4R7F$*8s`w)#?>U@|9xe|_C-zQ~udJC@zUOQ{b74)G-%(7kd_Q;u@5Y_* zaPJhyl=gRWqg-RffJK9HSsBsw6#9qrYenM?lKs3|rX2{ISt^HT5umU98_TVMezf&zJjt8g2) zR1YWrtNbftQ$!ChfwSQU>q1$g2O}>^K#$ukX%bxFxK$RIV5QcdjgS%724_PVC?)PA z?iX)7*bEJc+HfhJY*tHPA0ba4It3F71W~}%6Z=!=g${AeW==17)vJrnm#Cxjleq~{ zeU64iZbtdl%U6*oVEybTjawr_(R`}cIKCvbeC$YlZJ6fqzWz_t`2E+-sKpl!*C|6F zMct0B>NMeSHQffz+O1%6c?wJl;>eD&Hju}fWZ?<38ho4 z=B)Bh90b!iO41ue22sESY&;Q@_|ZkuDTBHV4Es^%JMTDo$(QmUyzCy?$~ZsP>RY;x zcYGgQvFCdw(OSuY?1jYo#tT^EKPvoyBZVJVt-@+>(Xy<8;_WBHinL-XKrwJZ&yq#{ z!j(nS1B<|m7l_6|3pPE&K7f?Xa)7*cR^GB2m68P*O=Tp8dx63`IWfzA1+XAutzLh-h_6i+lr3vE8Cv8r{Qw=!XkhCEF;JG z^6IS(a0_tcWBG4_KAh$9eKmsItKx?OqZkaZ&Au!8tL$=EfVTAPujRdY>FPZG>IOen z8FOcSJb8xgF{($+H8SnLT7I^qPF$_23$S=H)~XkqFfx*<*;pE?NTdYVpg7o3+Vi!Z zWeyoWnR%9w)q38NgSSquSEaVN6ih|7j7G6on}&8Y5JF@$#Yk$j&EBsf33qE7YnvR4 zzQN}?ty$PET*C6o!rEH`gK?#)Y){7*87;$Q#T8;3hQ)ZDAsREL&c9t6AOj5z(XU4A zlbUsh7M2==Sg-*m4S9U zuCV&uw}>ZlJye(Y33hceT^}tYuKkP3n7$|W78rVuKDcraPFZUG@Kx`Tyop4c!k@EA zy8B7ymng%qGU$STX{5Kp5gj$Z$C&19`1o4Fcu2vHP@(HfJ%gV_LSW6H%v)(ppUJfL zP71Q-k;Or-wR8yTuCqC@{JYYrLJMJ&8~A(4-Cx$2E8a)bN6zvRORF&YUDehZKyz#Y zT{t&yk1cniwB&^dslH{EWwgq9LMojQo*)saF2 zNq!Zy$&%!9+~a=dpF?V)ik4uLFOyl+^y^Bjoth21LFWsdCc{Bf=Z z|K>04Ik9%Sk=dM#?_`6^p}jz)z<|Fby?yBB6rXD5dwZuUXyCK?y=l!&$8(d#T!F=x zwD>Wn3rNX0S#i{`V=G4;f2Fiq&mHT#T77y^hy~&BtWHr4#iOJUm%=#flA?~~<*Kg7 z|G@$G4;DMB3a2Fe2WKY<-7ZOAv&b=hd{U#5-B+<<+Q;hWA+ie0xE%WP+-x zq9s}oU@-?@vR5ty>F`@x0n)K79ClU0*^7Zd-_7v=;HI6Dmp=?9jWs?Xhs7#32UIwa zjk$b?0gD))3B{RoqHwT7kK4gl-$W0GNf6ce7@DEiU4Q<;NG`1VGNc6{?YtYpYMd3*z{YT&z{_NC2HQdA}3?KL2;cp=)`C&FxsiXn?brj2X4PCb} zHHTYPXtOF_?`3>RPYsn>}FRcCjP)!QPUz6_{rwushO?gCk?miXSP6h1C4T zcxq>i^2YA+{90ROZzm6V%6Im1sa$w=Va`HfMr;+j85vWI27og3E9RZ)1$6_+Q?jwb zyBvMnw)y2TuhmKX8K(qM;H1vA$8e^vU>^Xzx%R1x^Xpt6{TbS*d8S=yVJ1U_m&9!J zw;dIb!?Sc^)UdPX0bts>k%Esgc5oY-XgD|0oaWfN7rIXBRB$E(4Sj07&G0VmxduM1 z)W)3a6F3N+8{bZd1V^I@=scF>Bdm|K<7*kwJty&d*e1xNRScIx zDPi$v8iXZk%GQ?;PJ1Vyr3us&J%CSD%r)ZW&k#YbpdjezB`1Q^5lLmCj)>gMq*P>ObUo7an^p*_?+B;l7l8nQ$@ECqF+^UZA@R?OngE~-@Yo>+le(#s$XZVMfymq z76z9u?+ZiJ(`fj}UnjNZeh(ob*k!z|3Z{YKzD1G7*T89zx8IrqMXry0n^J$a{}B+< zQdQSy+5Hrv{Snwd1aXg`@wL$Azy|bkXl^mT)>)8dI~OD#n}PzQRF@M9?tl~gYu?y@aNG$q zfbKd*viq7|JAPm?w9DS;>r!9KN#~1IMH!}$o!68hiIY4yJ;UV*WnGrt(emcxNK4XA zazN9~6o21BQkuU)_n|Xs4y#-OQ@esC1e3x@ds`a@(h^*&c4P3J z!CJqJqBZ>jGAX%r!+jqri0NXP)U&id)M;S z?&7~>$r8`;hJPsji?J_(8dubDy;}z#T*>kx(|UjmIF@NvtV(oXvNSW^Qb${ry{=j0 zpQg#T!jx?WTSGlsqK)fnp3cvA<+5@TnTW1%qlF+b)P0>a%!HrG>$Zg>*;;_Z(?e1< zuYb9={fkKxyh9s)t*dD^k}Zt!OGtYMcn{(^du|jGIBEq;p(!iNO9gB>9%2l>>6EU+ zrLdlnNpVP@KXZ0P573NlJ-srZ56Tr^p%5=h;s3oK57az@D<5`=R^TNaT?1Wo@;oEA zG8lmixH_7$mI|T#c^CqnZ{>O)pqw07TP0Y73<-ymi8i9;tRMqQ0@QT*8iO6#Ye^Yg@VBk}m=4QPpHrEo0V1-j;wHpr?ov)*ePDgj zy-1kOP9oW*F+ytet4HSdf^RHjQY_ZFlG`_CLx(gzO_x4h%A2guuGZ10I%Oa(@JOtQ zp)Sc%9oB^W?-)FX17JQ8^s1r2MBJ?R`y?y>z^q-Z$w1VjUTB$chD!Z*>l*t4X3$yj zG&Sw|45xe??duK#E>tOYh@Z$GxMG{%*CQ#e2sjN>2xAVf)a=gwo$1chk2K}cc1{$& zIl4OC#vLGdF=6PRi^HZ!lL2`mKU{C7Kg%Nok1v z>Q=PNh%DFNzV9GnH=UTjuUy}QpCc_vCJ{AclZb{nu__A(b6o+iids)+H{*w$1|h_& z>RS&<9vqr;4hV+j2w@ZiVqG^M|D=SbKD>W&Y%e0ln_b1&@|Ie~%HRhlo_CFBZg(#6 z1t?|``NLa*5F@C|OfrpCE^?%wo2@lgtXzFQDZ^$}Yco#%#gB`W$t~E)l|;*KgY@&w z=X*)iRSTayD0NA#wk_Gd#nY3ues7#UA}V!oGptS4TTBz)c2Ti$i? zk)nH#RZB6FtGROt^){Dr?D_}7BQ!Y?zJfXy4`I(@110g?6!Eb-Kjj-R-5f(6jN(y0fScP#&<;Qg;& z(nOx7omA6;4bcMpM=vO(GTG>;bR-foqeiZ%(y{=HOt|G(M}q53AaKR!E&Nt3DpT@| z*at}_-V@sX8vHYimg-}rV*L~2>vLjwL*Q)`%`LUv?p|3T#L(<@Cce$2fq zD^FT1>@yuu2Vz6BxHy-Ev;{GI)TF2Z-~%S(TopFT=&DdF+CH;Qh=}3(g>$I#OxKqB z4Hpo+d^oK1;6PMM<(UZ%5KH>Mei%P2i8mlnkcy`{U7U-eoUQGp8If7b)O6t55F&F>AX{@+& zD*W&HZ{HjPIot`;V83ySF}muMpL>D&7qxsBZT^ZZr}in%IiCG(X5;yEiyUdc^g_6J zn;-?u=U$5mtn=N>bY<{?LlG10!zYuo^f$gkxw!wqLEulFU_prBuzPy+<9x^bs_mWp zAJ9zkCZ9-n6Lo5+iu`M}@ZZjD?+?w?j)qW^y{*84bfF!LR818pW-yw!2D(4u*@ZL< zdN#j(?0U|Ssw-}46E<`Qq-Gy7d`)vL>G~GWjnPl5PXH8E?CoL(G&aqzB&oHnmi_zY zY>KHo1>o?KC9Uj3Dm(>Z`59T{W6ykr$e5^Kb8S#ajn*=^qt#+Q9@n2fJIn~odchlD zu5>_Ki1#Jxm(aVB-H=EApre{RQ%vqAdqiY_tBshX^oMeZwZSR^g}=p%tkihKRS^?X zg8_P)2lri{icLHQcR|S07-l(4ig=v7JlgK6;!((5@9+=2fS+_%Ki*EfqWkbK;jy-t z0<~-aBG9rIc$_<<4t62IgqRQ9JLlrw#?=Bf_JPyG)~a^#N}u1hC9ZPD&1L&;tPuz&DadxFW{1kzmlzWAYVUC$E8 z0lW6fL#dq@>y{-GHbb+x9QPc@dkRb;nhw*< zeuiEKNMWRXf}v@^LwR|^cw_b2^Twu3z5AXFqXt3e=0zON2cKTD56Tc3;cp~< zHk4I=?Bg(H5DYe<8 zg9_0oOd}XHu<85;76A(y7vJ~hXHz);U1r{bw$i>Rp<$l zLrL=_!)<5ToY~AX(T<-QTr`i*6$g+1(r#@o)m7|IJ~W04plG_A&;|0plA1o1jSlqKx%WxQcJ#+6Z0vR!A9zs()eO!)m!G@maVS zMv|sq;Rz;Xh(0YI=+l$zGt;)$hn^wNsJ(?JKqUD(JMVwugi!Qf;!v8>yY_%cphlf6 zv%umIOYe()&P>YVe2(20$WMPI#4o! zpB7{R(&SZ8Jqi@*dbtdWY7Oe(k1<|rsH%#qZMq6X?yMowQB>bi`dPN{A#vH5Yj(}2 zdy@>O7SS80iI%kTAO{dC4#qkpX)6C+67+%r@?pe-a63oB`YFyvvJ};BW@LViGe*OH zL*W^g9}`*Pq(JKRPdu0y<6>^T00~H6+Ik~k{jT?v{0AW`Y1_Yd$26>_Cp^L)Mr~TB z_QZ%upyfr~&RPzoUx;Y;3@BYYJb!xmk|lvffPKZdbGnGv#afYe5cUJhj}8EA2<=+I zKwU@z8`Eu^7gw?E=yf4j!}6jugJY$0LxFFQ)^ZgVE%xl! zY({XMN&oY6q5&)0557G(DIrGr6}gn{;v2bP3gpg$r5Io4!V+jF^RM|TFFDmSgP2js zx0R``Zw*@p&%4KPLti43X9qO1m$v6DYwT^J(F=p1XiG}|s!FUUvaG>@Z_Ck2gFi3P z24HZ|bIbbg{A@U-gVhYs`y>`(BF%Gd#`@+5$23D#-Zp-rY%bWqjbD`A>EJ}(f7)oo zcy72xwQ`16wR2Zsa*qo()#_7K8v`874S9cjF3zTkwqvQqD`Sm8x7#9B`9E-q}%+1u&K zO2d=eAxa4kw`|WLT?9d-`$<@~>7WmQg#lWi*xmV~tZo&t%$tT7YV}?L+xVg! zwc&SqZngI|s?fhx^G&kQ-Y&|{=P>1d-y9*-X`Hu=whLBrSx|P0)$H`8JC|Ee&r2zm z5U0dASy$lO1v&MZp>v}IE}hIA(IP_issHZ>1N0!6h&@Ha{@yNP(0}tVDe) zIyIaUDBoV3>=!mV$|81EEq@z+Rb!yG%w@yzP~6SED}+BsUQgqKMrF4?BlVt&+y(n5CL7)nVl~jStfF8h*4PG5j zXexSlEY2A>d}PqW@6fz3Xa>ZT(g#9Cs-P+Mz24)R#wygB(qaqpg>!j*e9&3D9e#lU zt}(}OVkGHe_4*JH#R@EOpaV^WZC!IJ{=F*ZXZ?NEY8fDz zRX9%NeEN@(=5vrRTo)w~?vAK=irF=oz8Fgr(SYJscW=GV)_9Z@HO#I)0R{Cji=fp) z;mXd)Ndl;-fF@9`N(w-xzM3Q*b0D#LA#@;sMte?*Oy_zoFh&c$wkH3r7>e;qgQ+PC#9J{JwiGl$mk%kMvIP2XwWu#)4(<2EW@QVDpY(GV$0$sV4P^W#~=dnqT{cfIrxT)e*^U6dH;W@QvXwbBE7=^@SU zG1n>~xM#sKhvlzWhTahWX`JOJ*<*J|epR|^Mx_u{-8K-+)X59GRddE4Wag9p7X4b0 za{ns2;q5k+$qS-$cs#f%(2e=f^L_D;jhs$x$daZDOLaGSBh|d+YcSAn+uUP zsN&Plh^9HQ4xQfJ-gz2o$y90GpN_|WM;dmOPE2jWT7c-4R7J{^|9v7hTJwDS?1O3w zx)7Nfi%7&Mdv!4#{BbyB=uOT46IJ-pjy&e{-DaMZTm|<%8|?VVc6uAl)YFinnW*H@ z$O2lN`U$HV0(qMl+yD?XXRVEdPY6J`w+sY!sKh#O`>#_qf~h0sDObcaUf_pwra%6h zJ@+-d&&HRdiBI7}AoXT%Y7W<&ypW@*qbsQitl(zeT*Nm(TOM)sc%_9x$^%C+)Pt1j zn@Vw1)<%|%Krx8merio{3eJGswxl?yh4op^U5sJ8rIO`Jq*$}rO+3%ODCZIAe%h59 zlEu4;C_@XH&&sN7T1FWPABf8OwalAtGz>Ag<-8WL0*D{aU(pWuGb+@5l+fNBOQeFU z&s@k!cAFL#$FFXs>W#Wsx2$fDJah7;mNED!w_+Rew3-kZ30yuA83W()=AFf}1r2}2 z>P>rVso2F_&A&Nzt2*Z%W+~eua~vFR5fmI@HU3fRr8_IDU{j=Wbt?#Tk=t4upCcj$ z1mk{p?{14MzaX;8fiys2U7M~0xy*Z2h*MhFuI_C8KD{K2&N|xG>$dXVRPehREq|5~ zgT1|czBh4p`Du=y9@oO$_l;obi<;;zf_8Qka#C+57rB~Hij1e5=N+dL3;6y_;~NE^ zFY&j)^bvL=DX9srWR7|G?_pHw5NRCq(?3HISYaTn|8wSBtQx_yV74s#F?&N{dr-Gw z8O8_+46fh?JLW#Y0G<9f_kW+)ytXs{;HGg-(9FZOh2403U48rNF4K1QV?9LMcc@WX zFj`j2k!j~!(MaivPF!;6dm0+yGIhFcJlwVipQOgE_CR>khl*WjP(LZdB1~j--8l1A z9W>L##p3Kn@N$@1{@vRR(X+<^)1|foPWl1QPx=I*)3^uyf+)v|m@t^xbm-QLm4efeI>fAL^SH_$G3HsK$IYgCx@y+EP5; z#t$W>I+SOxMd8N>JvsnUZeV;%Fr50+mu=2GgK|7OfS>hslKD1mdRDqkxAkcE`vk=a zXa^>3gNY1gaUTqvjXag7b7~FB%`-JK2Y~gz)9A-P!N|rv*_)f$mE@1GpGlSVIqm){ zF$v`w&75A!<$dt!lTkgtuL)|f7K$V#J@5srqIN8)Ydfn~?JlHvKCHF)p*#t%0L)R) zZ{~*>_suQ_4V%qz_dtYWQGdxjYv{YbWv(SRDU9|_ zY&YMmg4{v}ZYGycUqi{#j?hYOEkoqxapwzE`M~}%M*)43_Wr+Ix|D;)C0A@fV1X2N zp!b;W3zzRdH+gHQy*kRi_a8jat`HDnPUbj2)@8_61(T{XZLM@Q3;DH}_bJFDrDTm1 zZ}fUQPP{==4+NuqT9~AxMtw zjs1J3MNG*C%wkL8PDH&RnFaEwky>wRD=s^(<+9pk@L%CP>} z_9rb)4o(f-k79P-MepGBb~_liuaR^jG1deVk6?PZ+bhqE=1W}?KNMXt{?3}gX)UBq zQSp(@O><*DH>^_{ZiERek_g+#rRhv59cGk$`rc7sV^^SZ`t@(c?v1PfqdZtCBCUtP zH7tl4&6L}JrLD7bXaH^^CDHDBo2(#s$5@v8^ykk9EfXhURFiN9R0IHX1rO0@mquSq zRWHI^?ltPOl@+1I60zH<8Fbk0+TSD(vx^b}o$MHz_H2$>3W^0Px4RG@v9F=|L9#|3 zX}E^5X2guTss%S<1{%HPkBAF zT6qrglxU0WYURoy*a8ibjwQ6G@^5UHs2iUy&ajodmFi>lXUGij&7R&BfJxH=Chq9v zx!{U3`O~t_n+&Ul({%95PoZiwnQ^W>F_IFZ=MrGfj7^Q~<# zq+CNGcqxs)Q7mr1szFq2a*1;@CkZi(eO?NGJAYgs_2O2{5H(zG6IPm9)8ro)>>X|k z10f?5?-_w9Eg0v3QLFM=!H~z7BZu8r9=L@S+&z2lNfX5W!98@H^qA8DpdL3Vhd-O% zihV1uT3QVZUQW1Hp%N_i)m?eSUp;-PJX586m;JGm>A;enFNhgd({1V+CwAv+B=Kd{ zVnv%K>EXXpe>R<#4@;43Nkcl0v}0)|MSdh5NMhds>4y0>q_)Mb{%lR|@8;H)z-jgS zqB|j+BdH4Mg_s5)>b<|DnpSzBXSTFwIBsA1dNdExYt}f>jZeo}4fXAwHicl$v|k1> z?XL+ncQgkO@=M&96vmF*Gw4`*z1+m*Pc%L0ayWh9a8EMR{``m5#;pQAtimv2zaf`o zaV_(^vyGZAULoI6+(x58?S$x{kg|pjkCVi|93H8C^VeNeoi8TK#wy2=Z6zJK`jMuo zfk`I;n1~m--!^{iUshXm#x~z2iA9OqPpJ)7aaZThQ}&$=-KDtEi9fX=bq1MH!4a7X z9Ma;B+@>6!Utdh8UFL5G(P56!0X>aI63h(flYATE)v?vls3f8DtnDrBVISf9vyBBR zZ*R{)PyXZM{*;wB+gqus|A#?D+aEAc6F4^IP^Jxbkp(?p!YFIp&u6}F{IWk|DZAh< zu>ZgqUHgi4XZPj)fZs%gJ+#y*&4=1F%QU{D{!E2xg=;wNN#(1gcimRBo}mwR>U4IJ z;`Cd20Cr(n>;LMA{{Q^Dr2m5AMijtvrL;UCGhJSu_|@`NctvXC5kBYAPnuJLqab2T zfDSsiUz@PczwFybZ*2=+NEqeOm)CzM=6CT?HJOE4%iOBB6(I5WY}DDG!t%+hD#Mcl z5}W34#s9(Sj5pkrsse5tfk=EH2oEOS_jJ4o1T(TrCO15e8pKTH&1r-oOnA_$raaUZSlG)@remc^V*njGv4>@bUh5|$DZ1| zFq!?=#_2`!Gs;OmH?9?;R~{lxdDDp}Lh z1_5mF!J7&(Yb`2;$Vq|ZeQlcl{709ZDVDnE zt$+xSS3!BQ??Y0$Lv@l5pN5C4P6_vOs8h@X1f!%=^NUO%MG?Wjmi(GtsBDfISmRi) z!22JpwN5f(rBFe+8`&~QwGdgAa*MsRwCVjc>YL(LHBYEJeZ++RwwIOX7uW!fU)8gF z(X*_C!H!ZyVt3SK%>yS31++voa}7mDyT9{iX4YT7C?eN8HEex3ge0oTdxck%l3p=$ zs<=AI6Qij*(>;;KUfQj)rm>Elwnn<(ZE#9dCX{lfkc_JKi?)m7I_^EUQ%3PxygkAw zU|gOM5<9HD+S<`-W#`s!JB)mbvF`xg=JPrXy-5pAe2q)|lajB`WH6N@FSO7n*J z81<=>)D1GC*Sr*o!~Y<7p-(oZ022&=79cO-s6)CEjzg`_{yj#%L^Q{<4JygJKCVrC z#ekQgEOo=%I3-Vr1r)pyIjqyNobCd-LN<7be7a(YAapcS*yv+5t|~KC#NjJK6)480 z*Wv{_8{29Weo^MlFkDsK=J0}tVKmNectzDs20$d^WUS105@X+>t_q9yZn{2K7j({> z7t}Vz7~3a1;v4k43>e$mL5M^|xVRbZsAge9h{Oc=gGUWRvGppy5>C`&I1|nYzw+f# z6zBWk#P@}T8K=>6)Y+FVL23N8x1{~>mKpWWq;5?Jn)Ypxb=Krd%?f-t3 z|9ICUNwgBW^OHpwhlW5YLF3tj?Rq0WWNsv2zm4(c7wzpNg^JCHALHm0wueT~!T8Vq-am!wW%dmX zkYQB)!~nD`;l_wNuBP+V$vOPXj`{appB0-79K>FA3NX7tBI~-j-T)%lqp(o3Ra@fl zvk9gBwBKrC@>ck$W#oug(Cm-5E3wmpGu%m4LmYMJVr7V9Q!FF zFLO#mG;^(Eo&%6Ex(UAsGJ;h&@N6k1XL$m-qP#i^%IRt2itr10DRT8~xkYgQG zudNVd==x_^MY7V5liyspNd+Ce=9s{Z4z>>tS7_2w*lSP7n@4=8|iaVW_@1JuNp@c}By#`vfxb*Y_bUiXoZX zQpY9Z9_Io($xzVo8;*~1y&6yJdn#*E$=T0)Cn58A3`&k-Zs{~CbgnQ zTdlRR9*$*4u9G_fOc$TD0Jcq?&X&gJrcNOSKj-7E6ry4`M7mN^(!iS!-{tuH#-9NJ zmGeIT;MjH(`@DmuUrVMFY>xKvid^O4(YyO5*0lyGFZQSk)BrF=)kpIrK%*S$-G4^3vUvgqu_FH1-F%&l5t;Vw+S!ly-Z zvr~Lf?SV*~YEzR=l3(o~Qp=zvRh%~&U}D3}c}^LCQJM9j4g3VaG~GmoHz+*AWSkl% z@G>+zQ^?@O$EH)Yo?Z-fI42D0jqL}RHq8|mbZ+St^ZeRQyj-}DnrNT0NOwiG_30ia zH2F`IEWP|g((x>sp!mPEVrhw2vPN=9*Qbt#OZ<^BaP~5@1L=3$eT&j7N)!PB;SU;x z^nm6Xp1%=0<2Am8Qx?SNU`DH=?zl4!+kEmu~yuE4i&vTG_`+Am(OFg1mj5L1~ zx!yBSS^bjOdFv;mxM64kIF$qXlg(7}_LD6}pfuS$s;b*>K4no(vUy!r=19bueJx7k zuU<9}t}f;g{gwequQ$w%k`=9nneNDF2Y{g_9EyBu{;lefg}l$7NB)^(P;_$1ry1vJ z66kb{dUz{3v4YMrOsdf-R)A`h3GAgSz4b{V!@I}}m0l?63YRBOBrJ_pXuuk$*1)~) zLb-@?tjzyi3_ihn8edtGruF-ONC`w}l~ptE99OzdZ#vwvrL&uDVhD(~E?D9g(!g?k zIwy_;9CJ%Pt$VJQrWvM_Q(5n*ijh(m70ymOGfJ(gJH3z3kQwg?ra#T0qLDPln16W%>b&};iz32+$&Do3bUDYT88y_tp z!uY;Zn`Lnr=9MW%Mt@e2|AV&mkZJ6jv$?Q5VoC}jdGv&k;<2&8xX_n0!FuhrCGk>i zEo1*gh=Yg0yKz9*BPO#jD&<^s?Bb_CEK5dg)D#N%s;l@59XYb+ zrKVI(zGvY)@@}o&x{9vgH%a7u?Pa7^iG7J~{AA-Ly3mACCTncyg}F$SXC+fg{%Iy7a+gS^=vq%`!e%8Q%>T$&n= z53TRM-il@yv@&rbuH(G_7UydH*-n+R6A~3I^84zovZlIoMJJvO^fc~kugsNDf7My1 zvspV zc10R|OT}%gCIzjOVw$vdB(TBxjSY17zD6}hwEv#Isi|c_EUhu-5O8%?7bWC<<0KLt z%_84_$LK=;E-tS zNE|Cw_k?DzG~zJVpAy3!0wbz=3u2I4pX8e%_2jcPUXy9_>62Gs9@K`nLoD>?y!N!x z6xovErMD!p;FEc^Z z(R&#tA$kjfgdln+${4+k5JS6&9U3v4T0OBczx%GCLa)aoVwJ%y11y|)*s!C?tJjd zopnTlh_b+p3wXyq6+ah|2fUDnH*-Db<=gmB409+CyhVa6xvt38 z@!e1dmm+JL|JmCKZrjEr$4_7El%1-9F{bu+^x~$YqMCjcUCi|yFtR8Gqt{z)^u9J5`W3+wg#vU5F;5AOEe%I6Joa3{n%Ub;D{cA<^fd~p+c&hf<0O2~%o&>=g*;lGL-Z*} zORqkdJ$x6L;ikx~iAt~e;cQkP5gxM?&7F{bCvMohhTJ%jM`Ae*G7b#E70Jwk7KW;m zq~eaCeprMxptc3=14r`gSJNw+Sz5F?wokk_Jo~*h$Cth4>KbbwSS{lnjd3iC?=z*E zen!yXKra=dPiS?tNIgU|$&(^dDO{Yl{#Z1oJB?_(hg)`}&1AlG(Y3V8E@(Oxxrfi| z`>E?wu^@-rphDDVe>e61IX$VFzyFS4G4GH zmG8E#VQrCHo_=<|HM4Uuw2Y0#!+%FD1Rz#eaBRCYg!T|oobBZB1<4X_c>?r4u0j#mf2{Ep1y3p1EI%5w5f1qeF*N$knuOPO?%Ghc}5XdQuV>!J<1Q zt)K0~u*p`T2^!mU%i;PWR|HVN!JzDb;yy`oZ&6W&fFk@l?))vm#An7-tq@48B*#dc z&73CEZX-UYJPxWW{}K^Sr9OTIU!?6`s(+Il8*6tHtP{S&UrvPWdw()r z%??6dtzKRP%sTI}4a0^05pl8>Y7S->j}rECy4glwkiflWJDF)U==B#3Qp1wY&rTkEDb-qZ z0*J;P@&7#w`oDfRzUoZ{@W3DUlU2_ov#Vf(r8kWhQ;L-T2NV3?sqjmoP5~%yCqC2e zN(UTB@Ty#N3Makhy2%mEg={uDNeSYQUp>@2_W4f7tW_hujyRwP$YToNqdXxUBNE+7 zE9VheGs!i#8TWU^{MAWN>g&qK=^|<0|G8=!v!I*FmDj=j)%rb0m=V}LYjW~;A;qn! zPIf0xR5DX#R{D~DIPw@Jk=q1vb>|A;lWUjoZ@VuATWe~4$y7ac7q}Na%e4}fs12vt zI8gn#JXdF0*H{2Ti>b-99w+a$qk=(6Rs6J6z0g4{8$KZg`Nq+-5=D|DMCz+ z02tt04u+Ipi==}w!NJjpOaGe7+`LK)r03i`_SZ-5Bpad=Q#K1RDazG%FZMyAz`3TR zi#Kv@pt3ZrpdTtR*Lw2!_C^Ud+o$dD8i65Ne-RQ^3Rgh~q@hRU zro*PPhH?jWMf=f9IZ9~PiDdl6@jsZr9z{sC2G9+K>{;cD?mfR$LKFc;ExAy5_q^YY zh#Fxm{AOjgVA$>D-wI}71E021Nlal0U!z&4QL(R5+x`TNlL+cEd6{xNkj?hq5%2qL|90Q zbXxGk?QF9RY{+BK-4}}L&Ms~@&_A;@tuY@(V0x(al&`>lMD$K? zol?y8OF^UOgA<8!rcMR+{I488`24E9TeGBt7r{xJVTI=VM%W+d5$Nj-Pxgc)G%NCZ zsj0rozx(b%lUH6NF13h2C;OYLg$@>MG9H$Ht*7Rm=tJ0CP!rbh`x{yr2DI&+G6 zZf?T)X#jXF1mBhp-f9JYHsdt|9*L_EvP0`92v@c9pgJhl^rqb<<%2ddO*59MJ7HgH zhIvwo9TvqA3E{dy^dG|>Lsjs(nQ!sD$a*l+)>zn`EwFk$aS0%61S;GWP(Eo%w~D92 zD6X~V+MR>2m&jiF2{gHa_{yJ=Cc2New?i5esEjsj5bwh8^GfoW!>p5n)Q{7Sd@Auc zpmShxI4Q5#*jmEe}*XEC~ZKWJ8M(Hu_*A)YwSg& zhXOmgGV!fR*Ysj&@VFTxagI+H8I!0Q#e4!hC5E-068sU;XV-95ZF?~@ za5D+G6+2VaN0Vg#4zga(ORmQ3K<*#+n_ZHjiZgo!z~KabY#0JJp*DF2V`P=u=n+mS z`{kS_eYVfW0L2dW3e_EvMJ)*yUt^W{&mp5r|N3^BswzMER8=E

IHd*lX2Q%lXbWh2sgKlr07@MI{U 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 v*Y(6u6RDu5_){vc&H&pD}-sxoxW8-h*|20R* zB=o9_T66jvogWpf1?!Bsvm0a;Sj5UArRmLe-*qLEwLjX?V0STNCJ$Xg*PLIu*3|<^ zse{I7f#1~HLJ?iEWsa~9zk+Ah3hif%^O)HaZ)W*4P$HPRf)-*9?5+UNN8MtE*}}Fd6=pAIyU~}$1t@> zvAdm}k(e~sR`+DxDq!~e{3z;Ev{4(iMWz$(Wq3VZpTDh5Co9;l5j@8oJ{X0sJO5AT zD7)r=YZOL~{?=2^CyRJ7jfadG>jiYl@fm1_!ROMHhBa8C<-Hs7V} zoy({0VwLb`i0%z{=4)Jj-~}6E+N4CSab@$Sc{hl#FO9ZIUHqT{xA_+_HEDno{Ueh%q-Bbj zN-c8y@IQ>N!Th=l)R>v{kx6nS|I{x^#Ln}8jnA;7Z&LX%s;m z3N?@To3%Wrm$8;VBs0QZpD`!?$o63-8JZ@>D$WHt2)GWIT<*ZB!f1`|E?~1yOyE56 zHu#2M8{0m%7b6Z9XX=J>v45v!9~sd_;T|z1?z}j%b8`l=cj?^+FW(xBBq4Lu`YiZ5 zVR94|3^u`g5x>y+YHm-Y3?CdSIpO))_#0MWKcgk;%dtR&PU+CZbuW5-a$#veV^(Fo zCzaz9SH@SONY@O#v&XCj)$Yw@i4O;^m<@U(usq11#~TVc2umtQzwTd8lYZ7U`>x?1 zmXTJuo(~6*p;m`CNauR(dg;~WsNGFiKw1%Zg|HKW_#@oE)Yj6QuAnbpw4X&(0|oSl zfK35}2&Dy7SSU-S_ZLH6DqeXug3t@6x&CU?RflVTD|5`?{A}S8d7!isXnXCHy@X%eEdt2)Z zT0=JXRpmG4=LbzblUl$=I`bZQ8BOFJ_DQtKkA$PZD>}i1HAv=$3du7dgm(asqu-#O z5nmmxLudCebT@$p#YcWJ80`@A$Em7+%Avxu%U)1?7)bty6m@Qn4opQ5qk2ja6R~=^ z%aDR4!&^;FoG}fTi^*ey)dh0w#*eB+gOlnzBR4}_*UA<<`)z%o*E~?6DBujZ*0ztktfQ7wnks^D zlu`9jB%Jcyc8JkMEysouB7qnA?}xaxnu#Ulnpq_3F1OefFaC8v!vVKD_GDyi=H5YY z%+7KAqi(;%rWl&XtFj+IYMr3oXu zeHvxKfPy9FJN7)9uW_-Ucy)4QOnX;MkP{|~$6@!ea*bd+_xbngJKq>=?^3c{8S1R3e6Xqd=&RmUEaH%P262(BV#KI z@F@F@#3I2Hy)^6EGKVlB`sL~ZwnJkk`}NK}+_O=Z7cY2bq_bYS@e~^YU{W=GAlL4q zQ@W;BFk6lM;ZGm;fJg2@_@*0d^IcE;w>BR-W}Zd~I*Z?G1N#tbBNb}l4Hx%k8>{N? zDu2AQIm=BuAdg6VoMz%2Qu-)&4(M~q^)n7Q+_K|C`}N4z0=E%(--Us zb8R()ZwgqZPt~iZ+~RnEaPnpS;QqtHg6N?u_?SjrKyFj|R#HNE**JQ_xSC zes#E_$8HSdv6hb9ks68zNa32}E=Mmeut7XF%+4G*BGg$iP1D_qELx&Cukc}r8985((#_XCc=sww?n;0=&S(2}5FYqoqU_T^|J zAt88RUwT^l0@=Aq4gvLTFk;*u4{b(e%I|e$mX!a`;_iL@Y}V&ZR#C17v4(~BSK(hR z9>c!1QG#ON9QTv$L4#KBOuZ`B4fvscno$Gsu3$d#lPOA)NB?R>V68%-7VA`zVt0=4 zEy>@muAeeAZ;7JsD@IpgE^>6t1)UuQWOkb{&p^H|Y0reYsz~+hMhG7k1?Mg!fR{{2HD56;49pz;QAJabLdNZ0Udb+7ix(yMjA z{Pou;<%QA}tl{C@F}~`{73opA_sJhGulBw?(*WqY>Lp7tJ=T+pn3le z-zQ=rOKzxXJxp<_N{!^e`m-CPWFucicUpF<5M8?=QBxMf$b$^g5E0QOx{J+l0jMt0 zl?7u5ni0I%MrrHJ{%lB||J^Dv)ZD@<5qoC`hu)2r9HO*W&Nv;=fw$T%ZAs3$8cUq) z%r5$kc@7F&1c6OL@uLl7tXk*vYihs!sFbyhYGf9UQ(9~_(Z5c6*c&-&0P zcWUm}nl9^j(R8Q&b_690{Frb$^m*|#(hZi9Dm}iQ(0x>iX03L2wgapm_pVQUo6m~1 zJO0Dlust^MQIi395_zA2bwlxmCGb%bG_b^m5<2xe5#QIFwAIY!)nub>`44X|J-6lM z)kN#%HESTCWb)=$dz+0+X_FuJk$_W$PoZFYBU*L9@+NwJ|x-xx`ETVGPwiYWNHfR+GGb` zAg*T3^CsY<)YQ^8Blw5ZEgP2Q{Y1aP#r+wzctOGgO<0Ic)M!McP2MIDH`%HGVanD( z*DUXWt6D%#^Q;MAIx_LP`<3Ni^2ab!e;pTTC*Qfeu11u#v&Q!O6A8m{BCOLo^A$~uo414P zs!HHp5T2K_GbJQDsco;i$j?&OuEGx2Avk?U&{6xBjijW~9}(&xnP(0BwFefiH=}yE zQ;gT@se#yPWu*|o0XvZKLo2c#H1Wdn_rImvp1fOeEB@QGjbBVn%@?l4o3Ev(u37_g z<8hH>-f2}qv$IRMPL|CX{f|EmXH)*F*Oa~jghIrP70+tR#@&d#b=LyQm0wM@!AIrh zFFM(%4zDfS=LM^rs;Vsxk32W5$71g`PnO=Cdlyeg$B!EXKME| zzZgt!9Q~2583fH@rE9fHHsLE~t1Hvxqqc5@(|c4IJ})TZ&ZitFr-BjXg~Zd^oe8F^ zrnN7M^7xpPjA-LLve|wCjc{xEv0Wfg{z9Y;KbUa2bBk5k{k{37csVN!( zIzH%1{BzGw)&cI+^Bi?KyObXh#5-~K_*eRM9tWVV$MzzOWv(L(^}A#C2T{}lJFTC$ zWU)Oq?hvi)}PKM(AIyQ-)POMgSLo9 z+>%}#i(vk=?hRZ82*QkQ)6AopntC^IY-x>HeK)WpToTJA>qV9)bSG#Zs4YQ%TWpD` zKZh=Sm=5UnxnC`QR1#TV$N3=Q&*Y<&Wl3piensBS60`eU?l*dpw_lmK`GEyx zkGeRh*jwRVE^YM6u1O^JQx? zY#Q~lSm)8l@(~%%oN>Zh;+Vs|8P5apTH1qCCSN}r+U_=rkS7wsmphBGS|o$KHv(~K zhC13+rsc_23UX#EZVr7{Une4lMY;t~aMwC$ zG^(c1{ruqieUU4f^#g_J&gXvR+hsKh6Dl(&PIG(P!@{P&Mt^91|80mSi;0tPEH2@8 z@8l`oE$q4BlP8C9SUO7l`|;fX7aO1$i3JgvNR&O(ut|`}u;P6JB9D_AE0Z$=LGtuJ zOFui&(ME!M()i$?$q6!v?wmMA6WP=MKyL)2kBQl4BC2oA@ zJ}#JQ1vfMNtuq{Z%nhB)0tuOY{c;~D^MTxpSURMD>~Jn?u9Ua{FG8@I3zE9X^-eEU zjyl`T@)e+2QH|cBBHA!D!!n^Gqs95;#Ra{D9u51=tSnn-kYu#TdcEFLq2FEGFCQDL zk-hqYd5xH6gJcfA$3)C0l4+H#9*{l~6%*OqoYE{3ZezZ`BqB>0tTZ}lItm)P**Q3q z=JKWg=?etv_?Wc(6xDF^Jjhu{=bf;(NzLXwX}{m=E)7KE1TM`4;o#b(x~baU2jo>XB0o55+ATCVj8Uw z=3up0-PX>!1!IAolnGrC6HTvJ9**FzwrxQS=fP z%u6Rt6Y?BoT&>O_0iw?MY8R>}dqYefvTwY;HJ0#q2-}A?wd0k$bu=n@Hop z`UIS{+IiKSw(-KHBt@1}Ln$CC8`=l`JxWmTwHchtiMmTj`X8ARgorm(Tl5m2-PD_F zru6_@LA-H?yCD&;@TXoid?O`Vc#ma72ct`)p`;#HAkZnPsfGDrikWP6m)mMwGV=?b zK;6$uQAf&1hc` zMw@CVkHiEVv^oR7tJuRGjkN~npDRpoRM+3(PWcSPuzXs@iBY$)3AM#}ROmM~(Sz(LEs)heqX0`Lz_IUE1WhNrLA*8RcYx7lybM zT;=k0JPv}5nlJ1^agQ;NUts|K8fjW-EmQ~w_?h)?`E`yY4RI=&zN~gDi`G$qgu%n3 zpU`n@{(Hd4K+$*S;`M3rCbV~ZDF&pAGA|wi;zuP7meT8gq-N~2QgS>^OqPz`*@M*u9r)`+Md;qleAs1Mx8_CQIT0vYKeSFu|rA zVi@0~+K}5}``kwj4Wot0MmO(|h96(892*xLJO5%#9Mz6ytZDbDwRF5X7TAd8i5wzz zVD?5C^+>9aYZ8n|P3aNldKll74kt1mNl11C{tR$IJIXjGB)QYC77>C!g1z)?N@FQcp)#{Ms!E>8cP8e z{U4vqtJ+%3=PN$8Mh!6;?*h9YrfYGlYvFK3cMLntYa93choE}NlA>(m-R1)Y!n=Hc zkhnRx#L9%)$U+#lbjSbfO1@PNIRyV$2^DjfjccICJ61^DTRhIAzzU(#hd0T(?8T`_ z4=1*%$p5$ljUT)hUVWvF!Nk+4bcC-DMaupN4jv|ME-mO-FLDc{1B6a__YX5$%1az9 zo@8A5|McOP?4YRrO|41FKFl07Am<`SDGUeD58J21fXeO22$N)6WoiM5v~2Y^y^Vi0 zB^w6Ge$HE82M>Ml$eO`I#-KJ>P5`y7gaIiI*G6?vmLi2@EpYvHbH`q44eI~lx$TQJ zsJFY?T3$yV7($)562mvi0y#J_DT6rnMPTamN1_84{js$Y{N>)9YdlZZlL3~#Zz9Xb zdhiDI{dC?G;8Fvuz-hzKrNi>Bqjm6A#~YFe{(Ij?-oq8*DN;0&;-upU)~CfuVsM<8 z(h;~&WD`Z;$rGn8zRxhmPoo4QvjdZDkyh7J{+Y*tFEKp*4>4507L~4xq{Oj0DLrLig$`2~%|S zY3L@Qn6n|`e{1F1h#oHV@m{lshqHCj!Q+Tos~B?M#)+3Y8lOLwGZ1o{a$U_n(JXq3 z_Oi>g2VgG>&;H=Jm!NuH7%!%2S~g7f>vH2^4`-;P3xScj_czKlYX-Qmv4t!qW1A}K zIxSt{MQ>{|iv(4}!IgK~xHH&}(p~3yA-*2%xYW43zx6DR5&IIA7%iu6mZ&BUZ*p z=Jp`g-pAKb=}>ih+VAM`RT3r^$au8E_`4Qh#Kp`!9%DJvh9~}67^i!${mW9@II=Jb zSy8p;wKeLiuUXHsTuE|8;xi)j8|s=`_!~v zJ4p%cn#eHQ>*7RT0}8YYQ*A2y%!q7Qrc?5CDNtW1e&2Y`QDqz#C4p2@LPrDrU?d^9 zrqwzO#@(idw~gkn8VhAF`D=65wf8M1w|c{RABc66^tJRpgo-06>|`dv8g=J|hS6N2~|Pfxr${UOvp#&l7GI1qGI~cf{V^w@S%8!UmzY zNAqa88#>Vfy^)Bn`Mns3kk}*#pKQ3W;PU8?JYoH9vTTaNxpr-mHHgWQdCdMuht^eT z!BE__gtrgJh6Xy0=SN>SNv>PoTf<7MK1K!c9dlQJ6~KV)BvMKt5dzF6qh7LR;$7+9 z=uY}i?tPDWS~X+o1T}hnowELUNn0{N{V_S)-2~@@9aohfTi_|9f(2K8w%t|mxG48{ zFD}jM7bHt`)gHHRu`0cD7k5m0vt86>fJ9o@0mgAUaMdVAkB;MwuvJW~_Gab7Xrl^; zSXt0rY!q0@T6=ij*Zg#y7C;Z$lF9ZEuKYaWP$|qgI^y;7S8CRo(-_yEfzzARmr@GM zs}Rv)dYSd(urr`xQ4?&aNm^H9$-Rc|hJ7UuW{2IU3cYEcf^*$<#B{P@Ly+HmzlKE5VhZ{nNe|3EXV`S4{dE1f)B^kD zOTZMHJn*~``&N$z@ANCU9W%Xkig|TOMR9E$trH(2{R32tdIXK8q6Sp| z^gXvAehyTKr4$a=4Rw%ZA{+J787Hi~zeH@=a4DRL2~@exxPd&|ARJ9g8LQgDd$*R- zDra46X9hJxPfleBCUM{WY|T;zc)W@Aqn)=hB!=<$Bd<@C(+|P6fdr6}&DSje;(!*S zrrg!tz!+bXSbjLb^2*;`B%$8#0JadA0T*WPJp&lpJHnZryZ399!yqup| zId~LTT`hTkQxh38Hh!y#FC6#9KOuELbEH(us$G|Fq;TFt+VBLc70Bs?vHT$rMt zGngAPZ%=rV&*dc#&TqWSf_id>bX-vPDI%+rbNJs1Dj8v0SGdAHLZEzPpK5p{0qn_98I*k*FLPD zXKWjqsqU8;60%g90t;{AwTYXks;JtF0BXwTer$mPjofVOu zYcl$54*)#0WsE2IKfH%#j+y@(zbjx51QdFMuEa92cqVYp=2mslsV^k%qOV^yaosJs zYr|2}(p3~-#C~Fz%tyx4kmgC* zV;x_K|M0+dX|!TFNs?u20pYJ_R-~4}kX1{dy=J9~1PQdw95+8(+z#s_3nZ0>JjQNbS%||4AN%VGQ;KeMX_# zAvNUJwH`Q@r!812esNomK#lw{*oA02^Bu2YIylhsXPAkD=6aAJPHif(*cl@S8~us2%a}M({^A|0mMVpel;BB}(${GY{5|I9hcb)Z zqKKMwopS1-%|uIbtRgB4FcW|g zd9;uqo%*^(xiU|i&bHjc^~Yf6_O+<$gTa(@oycw=Aag@jf}40C1$l_HD-XCyiQj45Ap~B0!5cjq#)+JIG}2;hJH+ zxnYrKdFpF$Vn2BAvlC01>=K_VDh1iS_QJy%wKUW)$Qz-iCFs3Idta&qQOna zCYK1)xNR8;cixLE)G9SQFg)iT9?2^|Ky8>dmAz~rN`1FP7#C;r#hs65Bv1%VGDP)o z4O2PXy6Lh|a}~`kT!B~d^8=mUJ$-$=dw6*F{1peNzPdSD(6*ynV*;5mdBf>*jj{m@*0;krulW)=jh~8|o2V!kXIsTm~CbYfwctUC#3pyrH$hU+Lot*iM(r z?5j1q`-ue>&i#yuVcZxn-RafmPQ+OAjN{p{hGuqd<;4dIqKK0u zq7iqZgU%FdqNe5(zJ-Hs@dRWNcmAke%NEI24RD6J`9XiB4TCkhDknIdDK9rRFuwknD zle8<`I?>8@NAQ}kMn$}ODTzLDP$0&j_X*UxWSTtl)q$Vq!=pM(l5@4w2M$xr2OSBr z6@r3Y8QR>8iZsQ?3hW2zCh{lgHL*ba;d2eZRREFmUZoiRP#3vE);37^k~7zV$-8c!Nz<@r*?ZU zqfr}b_sBfzl)*)vMUyYm>K8)YWeD~bXrOy|l?lzJ0W}5nNR0VTXH*+W(_>tty5IxcUK!hrJD00m=SahHz$7 zg3UJ{Q=D?QwYNDa%=5mReWI^@Q@1L4j|HF3pZf?Gw4*Owj!oL(+`aAYF!YriWsCA* z9Kx}u=rrF#Z~iK9+0na&-$L2%Z@LKA;s5d!@BgY^>y~w;h>4~wxG}}__C-K0@rz|j znue;6*df(pXTj%Oy5G$LG-v4SK<_-FQTwBcOc-NZQ!*inwG7vy1(@j_CH&A{u!y}W zNrQu?E8%e}Yw9@IWUn0Q{ONi~=5pKo(B%86m(Q0zLjD@}-)@Im-hj1|6G=sFPm8Gl zj{QI;6fA}A_8@8fsC93hr(HQM;bp~Nb>?Ix!gjhrM-ynKL9tH>NWSaTL3ChlRkLAe zSA!{ODMB|cL?(t>Z&B9beyz$Q-S&Jy0r(5Zm-_mDJu(BtGKQuokjcE9EE+Tj->df& z`0FGdB`C6gR`y2`T zlG=8xEBP`c^IRJh*O8s+T=|2#nU*>uN5eomUf=DT(sr}QU%thX?!k$a`UAMnuxn%w z&76=T!W=8wF((HqTh=JmuH1Ay_N-ImIR3smTn}xr9ouQa7Z-{I>eCB3NyITN;G&}{ zUfM%_Pk_r0*OXng#pnG#D^@CXC)xQ}zH@FU?z*zepHPapRb)GWN5*-ACA+(4IGVoG z=_#FYybDxI(%Y#ac|A=Rj;IZn5uXmL!a4vG-ZTZ;`o*02w+WypHTxZUvNq{9LGJAT z;R&AkN{?y|_i{)+U%@KPi(AHsK$S4@h#y!ru)n{+-p>->J$wl)_Gcfr(v<3p9s*1( z&)_NJI5J*TGhy!+LQufP^PuP{Pty-Ni1F9$UAdP}hzR(SGM{ti8s)c{Mr|i0ONXS= zJeQ&f9UI$cy_|s%_QCUpglJbWu5a5y7GyN!C)fKq`0P1#&eVqpXO*Q6-G=qJZGY+5 zX&@D2JD?^C(TLt~1=e;{ZbqS_KxL?4zN_HpZr(@gbKlzT7v9I)=8>)zz14!g?ZE^f zh3*|~@WC#nFUy^~D%(%-Z{;3XPQ^00zX|@%8WhnWr;Q4n2hCqtQbW{63^uq?xaP8! z?;TM zC>DfT(oT855Xgm9T!4NE^73|lnzpKc%Sh~E%=JFfJtgIv@;)Fy72Vno#k$hvrXv2s zi(@p!w0?+V3ZQou*%Nc9)A+iU)FjEw>@0m`3_#U+2JalFC(xF|-l&r*L6h|=!&jN- zM9e`|YWi5-Z~-+nG}}Pnb96*K(lREHG~HB7gA?QrX3?k$9At!+CDj=a5#fJPzO1IA zgF0tSM*?kEM5b8}4V+0bWP^WPHPYPYVG9RiPPZhZpgh|_6c{p%#cHp~*F$OEtq;Y!Y&kW! zrCzip-`8Q<-(vPG^>@7!`P6EmviT4<1GN5KhNuqhNcdiF#fF)gV~|tF!n3Y5L4hzU zgY^`bEkcTa6iY{=soTtq$5`JjHWG-$*y|#!P9vJK5%8heb=Eb{+a*Y?avLZ6{NqyU zqZoX$CTu4Embw7RfJp?D&bYAO57SdQYv( zg)l@?Qf^5aj3%D2#1!)ku|?t7VuvJJmyUhQ9jc|nUG!sZ3BPJ?y(C;ws@^1pKBhw~nKPLm5o)mY5GiSYO3k#y&~$GbZ(`?Jnc)YQW$)bZ;l?V$$f(ZO2gNr^_pDLEuz!o0hKc4PC<@0a<%qtMi44l|EdX4F0R+()RZ z)LJmess>UqF$m+3OatGJ?RY8m4wN(SpZGsNGM7x~p?!_Wqj11UqFQ7Bc4;GyUm^bD zmL5?#bJ6CLkMnCMiPrq7H__GwrT&l*$#6?fwrTvamHrB!A=a(%P)Lsq;<#X=Uii!T?CSn~Tge;9VO zgj&q*#R4%DD;p2ewS(v+vgF|^ZXJ)>TT+YCjFu)CjE(MoV~cXN3A+Bn)0_1Ikervg z#fJ|M9DD_`Mc4Wa1ta(3v+#Ulv6>`w?h}ZmA=%Y&0e&oPt@+z;gR;ZKd3+6O&wsn< zksrHwXM2Q#Zm_yECTka^cZD|1OZ#ZnEJU85LzlHs%o~rj6H8w8H>zu??^j0Nq2t{8 ztKzkhOJ9$nRLELF3@2iEB!w!55oRoS;$`h2=1RVLb9DPg{`keXi15!MTuP9q>H|(b>a2^Qh(Sc~yaLByuyIi;r6>oUJk$+9Idpp7Q^q z6sc;*GfdAi-rd_6d5t@iu6E|>+YnjuT=AM@8}OSv$#PN8yxJHs{ur0=`|v&qx||(- zno#8ogD;KXo+jK^9s8+efS?ikTT!uvugUwnkGuj0(-?8WissoIfV8aeR_4BuN6PbieT9DshLVl(4HfF+$|&6!9zzJ@+^8DT*j z>*e&}&1sk`l)B)LiBDK~NvEy&pd(`|oK&*DMXp(Ro3!p^ht*^9bq$7Z829iY6$3=u za8_mPH=AoyGSy3R&kbU~#Hj|>Wmap&a5aGtn4~9*|A4o`qa{ysaQ{TP;}VkXZ}rk{nVxEJJ=%8- zHAvnJD081WXoS`3!k7Y` zd0`{HWIB(b;^!{{*d+eYd4h{-cca;p-4-$<2YcwylF=v6T2Q@_&GMg+4A{{~Z9s}HG^;G`C+|Ok zqc;`?kBTp^L0Oi>rX#NvIF`h6 ztlup?HgA}7ZW^yzv9Qg3Vf6BqWJ>!Za!PlH|L`nh7$uud6AjWNVUP1ru^K$fUfp&^ z)LDI!v6!$x3vahN@{|7cdBSQ*Wz5P}C#f7uH6#o1yQ4G;OZP|uCJCzO@}4FVc^vDB zPc`-P2l4tsi1G+uNEgNy{jA@{e;OYWA|6BB?(HmMOG=adgdr87Oip?nQ(cN78|~=% zc7sdZ%5nJ*4-_XVe_S2c(D&0X`LI}3M0C<5R9QglNGOVxp%YW@yS@1Y^76<;9EjFi zgDZCk$Ne@c+aQr8v&)~7H3%Baiz3?+qA@?zbs(*9@XINJ2jFtgeqe7M9utue^7di- zyVW#zW4>uNY$e>6uPgzsX{24gmF6bn6@$eNaNEMoLzA%Cbdaf6pFp$kXMX)x2}SeG zWA$_3AUZiZpAsM)ZKms<7qIKrTG-U@oiANv^siR+){68*7=9!h*kbmX)TqH4p~OqW zF7qBh1u#C6k^kW#IP9$#YEGLCdcLo!B4Cexl_{y4eG_q8_|$+kMm4^hp;caL+GJjF z%%xvCv6vG0XfjUSk|FUgT8u=nh?rdBWQ>G`7uJo61wi{UwXsk#w8-G_w?7nASJhp9 zPB8brWQBeX-`*obZ~WUL)&--=hSM;G=TLc+N}LQ3XU=3+8=v3gZx3L=%!`|2R1>Y= zrR^%<$gH`-c@i$Bzxw3{|Ae&aq77r9A4(6BRvk>@UP%v?n3f9)e2#yk{4nJ8%PC~H zs^}C;K9(Le9dq2<2o*y6s4YPCVDVn8u`J;ORpFfHtlaIK6C(JX36ac<{IqBgu9&G+)R>t6zfLkJvN_~xhO+nO`a6* z|J+PQRQ2{qcoc0IZpY+L`P{cU$~B#<`QzuiaA_nqefi8+u2v$wA>63L{m|BYL~K{O zle=v0>fHRKV>*>9tC;OC>=&lT*ekZPoqs6PYwg+c$|+o~CI-KMMAl7T+??-KlbH!7 z$)yM|T$sLui-vflWa~{LK(XM}F}u!q{;K2n%~gUu$yl}*tbZW{jpga8)!!G7>vyY0 z?=K!oG|j8cw`Bcuk;ZB{!5U4KKUz;^(PvBJwold#1fiBChl)-ui(=ATjUDZ#^)%+r zHeZ4jt~OLj-afD$4J~f%nLVcYXJc-TPvZ-rmYC5@%KdWxqHGHlJAss~Cb?Cn&Tb$sm?^`CZ4-XoioRmi9&puu=Ol8+Q{s}`{TE; zKgV|K7yq&hH~3~lHnofF-TVCm6KN;pM>Fdyz2Kc{Vn+PkPoJ7jDau@q$tY3j!t9RH8Bw~A}}kK_GOP((_R zZb@nBW-8JGA_CGXASGQRCnDWLKyn~4YD{u;H;xV&xzW9mBY$WA|IK-vi*qi{x!BDv zx9`6De%|lb>-o(6WV=|)dTH$K$xN4}1u$>Ew7LR}W$RjqQo^0~gAv=?V*{--_YNcX zP7;Ma)ipFg{*GRsmV@z$T2s>Rr+t(q{P{m%L|qO@K5LC7E_t@xCDorP(fBQO`Q9uX zHlb(bRx|uC!RG5VRC(%5I)M6tX@(DV?QWGC)vqf5i3hQid+zTk&Xnw1eT2c87G)wq za}s}_yXzfW3$>McA3Nl_(K?i4G^OWRn9v!E#hH@cP>)SU#lVdk^o)(JqTwW$hDe~4 zBw3=0={v9Fgn+4bY-Dip*$9iJHj@Z^!t`mwgXQOMHn|bnrOt&~KE>8fT>EeTNj)5= zUZt_mAN~5a+CfJL(0Y>-7`=*hv4$WtmWH8TtqJQ03w*lKcu2o8vhcfkdmVRS!r*^I z!n0TXJ08d0mF$~lIyvNeG^RRUUcoFLa@}lS65uFB$AL&j@NUB$1N3eQ>ALBa&$MsNn^dQoa0rNbtcWo z6{?jInSGHH&loMVQ^!MEF zVZ!hEk81K4HMx%GDvulM+opwW8!0X=%NIWK)0C~oZ6?%>-b8A+>gU{m{;qsjAg+UK z-uN(iV7ya$%&ZV}8FR(3CVwmY%h6{l4eRPe6Iqam0x)3M647v+d1UZ7Y#3$8^UW*QpFLG#5PLfHF&|s* z>WwK*FclSP<>_FoC1i56T{iRn0wx9=#+o;mHU0=}0Kh-ndlc1X{ zaR<%xVLtBBG(BpT$Tg2%(=2`@ox=~rQs zL1OoT>2BC~HBf1zLTBZw+|v4O&6HTd_eotv`(~k3Ds`h*o8Ul-vwS-PC}UrWN1Wwe zE62%I?3D%?eVxV(<<_z;D1F1u#7w==6P?HsD8XovqoG8hFj80egS zmyhU=(OfR*>!xU%J1s}Fan>-uXVyzB*0Oh_{g22gCFs^hi<3lv-(khR$hdvX)upl! zfpDwq;WYoX9_`h}CE3jO?eKlusO((Z+&Ul`B8Hql`chpUT4Y?7y$Y{{_%8cP?S9%7 z>e)WHwlB1xRL9J}0>Y)u4e#}o%MMk%80K0HP0qH*RZQXd(?R)5H$OuQgf&h6{zqi- zc=)t`ID55KlrYD$l+~@767l)vX4904zFGL%Y#OlXV=>;s;p6{F1{!EnBK2&DTgQQ1 z>D%T86Jtlebex2OS%2#G#t5kL6Socuda#@hJKP$#qNZ^yUUF8p)=WEEtjs;2)v9Nl z8~MQT^MI7dciX5%1J;Y7d~MbF`K+qK;B{5ih)fA1n;Zs;Jq(9S?=}`(Destadhz_} zZn$pmQZ~>1*_mgd4--u;+(vz~PD;&rKERk{PR#0IHa_HNP*(Qp+{Dssw!VpU)_uGZ zjkKo&i3DBJo>=O72RqHaf1aUM8#;8q@360~vPGvr)h^g2f2(?axa3=nj&griIAue) zWdYM6(g>#)eLvQdGe!tIpYbOBak!RvpJ77A2gkH(x!Ac6sl##2vBv_NNPH|MMRQhDiY&Lj^cdSkW%? z&&;$esIJpF)r4NP9?$TTd7_Vj#`2pmM!M&nli_y83erWX`)<15pBwn8ZvBoql>OgL ztN)+hH@fNqr-oqj?zb-j3~z)u2nCWv{VRvMKmQSV-{_xI1Uug<1NMJUcdI+A;;T+o zN>t>q#V{O1|E5SxMZkd)y@o~g`sK5|&M_#udKzD$Rw*%L_cihgBBMoO!hq#o)#%!F zu}8xrmCRR&?FkE?zM_qIx2pghbB7JEF49X1tM=pOCYcQ(+3|C_J}$qF*L^`{G>56L&P5C`gVkK=%ZwA6$^t}Fx$v0Y*atfU_Vi= zkXZ=EVW;fkcfd@u+wUM*Jay@{bV@%nb(D|(V|>Ky`%rJ4PT#cpdTe!MYR6r9w!$k& z;QGb4N+<>tf1-K?a9mQZp=5}?<()%0phnagRg15ywW)8UZK(7?BH=-!kFO;AeG*UK z37KfDjz%r<;bWC6mN>zKBDM2&)qiTMYZGRcW{7%Y3LWmofwh$N14f)PWsa?rk<3a* z@7k6V4cqa4E`>nES7$J3ysA_ZeaaEaLn=p_3m$-^VAw^XuJ^KR0*G4#9mm zqND%goS>PLTy-PGd-Y@*H_PNfdy-If#RYCWpBUAYdt8_$Xy3_OcImZ?1f9~y!}xyQ z@+4|Cy9f&OrdUOp510<6JK5$Y?zmAV0KfK-wVu$=M4rq;Eg&69t*j+U!&_K$eMTjF-DHej) zj12$c=)G`g%~@^>{bdq@W5J;fD4pF>nC1=pw_$WOk-eYN=c_hOAD)cR8M6` zs+xZean-}+XY#8>@9V-614HcOu5ju0mu&nIN-sY-Un$|wyGzTu?-!H;!_?A*0J2}J z)o2#ne5@);wCOoN+S$_Ab@F4u+tT?Ni zr(mMQX1c*X?%;kG5?H7Jd7sJmkOOB3!H`TyLM{W{dX1BSiMmaho1IoP_4V=;d_9e?12peCH=jfkRi0sk7>!^!?Qw|sY znI-W(LYoWB^M44Y%+o}w1w29*>s#mb+ce+tlu@`4yu>>~ z=1+tw$SoW@ja8p!P;NEY0g!Isa+8A8R$5swfNpX{p};BGv|E9u@8*g#7G(>lmZ;&U zR+_e))zvG^drBH&p>2TckosDJ0w!K`zB`u@yI&XHR~qckp4~K+HsIG!tQD>=qs1y# z=nQeX*zCG}h_jA6)be;h&{#8iyf z`;TZ9N#hnxpj=f*b1>uxI+N=VR@-LtZ@!b6W@{&O7RdG8VVw?UKH!MvxN|LyCFr9e zd3eM3SgTe)A7O=&(la?x&YuUSn3RKof%%A~20I}uF|lt--nmRF5}^CLs@<#3ASda# za;KL{At#j!1Oa6+(7KCV1PAM>w|b1Nhr{$AMu3$ShIG=@l*u1-a(cY3OZ}T8&3g=r zsXBoj_7L#A68K2myYtRl*-0e;VbeRSi@+X6oiZVRdJ?)9MXS7h0ME&buSN-EthVC4 z{)m0*iY-JF!#8i!7E>s&yiY+7;=>VDSI!i4lC9La|Jyn4j-m$pighj{Wk20I%J2KN zX@xTw@1~1wgZY(r9mGTDO95--+zNk(~a5wAmAJ04o316nMdV3Gx-d5X#X{mWOLiWo(dbMp*mmLX5iG?GhM>-bh833<{OT1nHT+R9=+nLzO-Z*Y7ye%?(Hg+ov4<1;e+XoeJs6cXzC9IWo);4xCWNaq*WgsKu##~q}f zm!pmFcxU3p)>hH`8(kY(l%`!L>mv=$iH-BdR?rg#_qAi`S~04&X3Vn|c{iBDnTv-| zPkLtR4hj|2sF-Pd@t)OPH<$$%Kp4wo{rce~*S3=_K4>;S?Frklyaobx>{CmldOafk ziw&!7QrK{icn3dm-0a^b*VIzy%s9f6GSt%AYJukIcN8~ILhw%l*TDE%1D!|%R5c8y z?q$Arn^K}4bLbN2iq$Ub7Sxs_x%+^7SJlLrwE)R4bO1Zr36>-wH?rHWbl8ASSBM`qiHl#MUG(WQXZ?+{UJYy+sjDzRbfl_k;Vz0?t^ zJt{(zgR`fKqqOF#%0*dmgiT*ZAvj|-(7orng<`YA2Ywen+jZ$u48s5(?3!67_@GWvcp%QMKIxCv z^TPd+nG16Y@c}xw_vw(W*&S7@|A=H$G!Q|b$6Lg3UOhBHme|c+>IY_~R@-OAJKcs; zZy3(rb>DqGCek)B5eFR@JW2{kfPN;xL)n>CNT!7MuQj0lNWxHeq9DrhsSQw!%l;e1 z+CYJ?VdWj3hZo>nGU70UXUmsIw%U;j+E<`V`+v8B)nh6=cVGI}d|0=O zU8$vY*}pft6j{koi?U7UkC1eCCaxCMv+uJ?0MA=8tV$T%%()%=1Mfu1er#Bi@)yz2 zKr_f4wTz$E*6F@`E74F6d!e?yA_rTYU(7;3Lx12> zQ={2UDRUNxC)(oivHk@+OLuWHH>-N0 zBxt`Em;lA-lUn{8bSb)4sGqE~C^ZkC7A>9&%cao}43RcZw?g9TT6<1X zDS)~JnHR`Lc%SND&Y!9fZgZ>4_k*PIbC?IG`Ky$An zFdUsQw=+){es%1xN$IRay8n;-4~S+z+CY7Y)>&47wDUwH0|k zNW#AV+X@ymo*-Bmhi~s>3y2!A>@8#dj-QuL5^(j0j~v=q@Xf{6z(_;HWJRTj*q+$| zy1tiu)KS==CU>}XmjBO2OAqUj;6_Tp4r+aC@}b31>8+5$gECJAX)r{MmZ_{v)c* zP5lJIu@Fu-d3Z#3=1452RSPZ*syWpxZj5K<`Kn8#V*6qH_|5>FTm0fbA|A3$CSaVp zJeaUro2jyaK`55zbnMG`yHHw=zDkiX?_NB_y!pBom)60Ci*Y|^4T`SXX;Y}4Y;nVS zb(k&1W0t9eZy$}jN+C_O8>hf*ueC)Be=>dyVoFUj>aWqlJ~mASj9f|>H2fT-?9PQ(MV(}B35e4#oQ1f>l;sb+PdJS)=+#3 zB(xv|35G_C0YS1}GKU-M4FMsDni;S_4_}83*+my;tU7a0ENS$ubOl(Qe(uT)keOx$<KLT6dU^X>)oL<=n-4fu{Q>FkQE96&~y`svo0F{B{C)(`029Ts?=r zQfm&Lvst*mCMWkP$-~rTrg%m=9`NO-o7cbJ6DOl=x1((=<0o(?pwqAswh~%~^#4@0 zlWeNPBPlGLNS*m=_?*$@o!*O6nAqHMxA6>tz9kJ)?YpI#{4}e?U$T|v?i9jRQd#`Fmz9uAVi{`N7uDLA1y4+m(#r->mA9T`NT+K6zI^5Ip(Wl)XPIt$ z2DgO7+=%LIka;#awj_}UMsr^v)U_kl9=!*@gr|M$=r0nnEpR4W*kJ2Cb#UtGZRKcQ zi`}Xi8!%9#TAx116l>M^TeQZ__fkK!xE|4*&9`6UfA&W#NqzPE^7F-W;5sUOpAtKA zsM}H(rjGC2)c=a@v&UyhB}WJ)W|*lM$_<_O2A%I4us1;6cGn_G`|V3l>^lgnVl6cJ zB6TkVpjIrbqJ@RK-w(bmR;f5DOcHWCeS&FkO@a?5Cm*{9yk`!9jbbuL090Y-M1#sG{_%Molf{x_;qvMqNm&59JHX+P zEl30>_AP|Tap~?@+Q^~)_fMkX*?*7sIY{JTipqR`Kg$TE3=Dcr5dJ((-1LE(~iyWQ6Y4Hhd zk3j6s$Q9ye*v+#a%X6P~=ji7X(^TBDh`dEc#B-g0v8ib)yq`Bue{i%Xg(IX$)$`S8 zjcET*fC8U*OK1njg)lqKe?)(7;3t)mT(#F^7{$W}c<^XX)5o~Q?ey`*4qnRv+ZTnd zY`p}xH>T=Xo)87JPqyjlX6K*AjaiTWM^wOErYiJONs>=ijccHh)z%wE=v&9Q(K(+h ztfA-U#!m6v*1{W@mI|F+BtVCv1B=FFc4FtM;bL`M= zMmqLlQR?XEg^i&b)5{FI&v}&l(@}I;g}X}t4$9ldpe`6TPN9POt2gq?%cA}OtJjUZ zu;MQokNfN9F36j6gH;@+S`0A3@A@bu-{IKH{p{U(WG0t}uw4atuSM!Y^4>YN@!|Ml zX_K~=o=%;dWdBE0$Xm3KRJe-xRnj55YG7b(w2mip@LiI^3Wq$!&sG9i&_>Bq+z{i& zSgpDUHPjZBZ-F>5(fzgD>6$BrySY+0*&lOYWfNoUQhAYS-#$V%kZOwb3i&5==flGz za~H#RlVVQ{CdD>un&QCe^hdRxeV{oz&X|gnQMQie7)Kf(&~t6Z!^P}_QH#x1ay7e6 ze8kbl$u;%w78Cxcuea2Wz6`ndV!PS7W|dqhXOe%v}2{qauQL!_j~em)J5HzgE~lbf$bno|%ram)N~c>bjzB@W|Qm8mICKvsSVOW?4^H zu1@(_e!09ndh+W)w9?9vRK9P6XT|rVv`&lxSYR3P%;)g2Q$S{y7h_8c*ge~3ovKHS z{a0aH8UcZuwc%$yQ*jQNBOKa`egnhwv1pVysdLIsQzSmn+wEvfW)`@=OL9tzeJJoy5R#xJHQB|Yjwr_>w zI{*s8lO3gwCC)ys#bcEzNEsvYPM+n>6pMafryFlKBIlVJ_vV9+r=$03rXHS^L-49f zvN+I6(G{?I*>En;%#3UJBUXsJmosLiSscZbbx(6Z=^UOAN}4=bWZF7)DxyPx_fLts zwhcd=@7x?3U4Px~!@$38@Q>;qjm4no%ys6C&bzwh;y;n^XCv55%CdX}p`%wqObSzD2Z%r!k7tXD;S zMqEYSPnc|dLEC#8XG(4%Wp!t-EwKo68D78R5in1=XgSh8BM)pmA=Pxw&_9NX-^V;P}RMrbv?pNXhJ=xDqKZsc&I*2kJ)V`U5|oH zW63|>vAec)&0xA;R3neSecE#>%FQp?PIoMEYzjx_EZBQm%xiznpe}1#fumzenzf!Q zz{opp#qn9Vhi>E_=AO=q9ztcE+>>7n>AcxffA#xFPqJfG$;1#Bc4eqKc5)aQpb>c* zDE80+JTO?gLr-JgRvv&qb|w4xyy3zi@EOUGu755B!|`bAJh9XPbzAf)kZ+ZDx^5x) z%~ji(r6#}f#RhEWRtA?I<-+|3#}Mn&YK-DrN?LZ3)MrMNn0&r-P6x8%d>%EQ^xWgn zsvWJk_=;n0wy&YRG5&sIVL$y~j^@yC0~L^zI5g%+iRxE*J))_kCQGNjspX32pjy|3 zHhz6zH>YBNij3(ijoe@_ADq4&*6Zf4vb;9F52=aogA{HvFO}oWbZoGf7|aq`K^p~O zhd)hx5c=C9Zf{uXhewUOQnER7@Mw?7qigd9+?RYoXYlftq>lgBbsAvqex~e?u+Yc4 z`R9%V3)CPncH2lxyzL^c=d*|3mK+*(JdrXsv@68^!6?vz1(r7Jy+!sq}K_ z2eZ|5M`PgVxSJ`SR5k|wyXj)5$L*(UMyHLZesSLO?dc>dM2I?%znFFGv1uBcdZnZz{4Y*2- z?_f>UsXS0^o=wwr<@LT0^QppPSCv`Ot3mT1Jn>(7xAt!;ZmoyL($r7zP!p|!86|^? zW!^3x30Pl&ROqVePj!lZPSzx9qLZS*-zK-U{}FMy+ysp};Nc8C&dm^#rE4)lZ7>RN zsU&V&PvfuzN@gW+1B_bQ)Bhe5yN=53_6A)Ze7G`(L8Fw^ink}Wn@pm8%oVjdpS+|y zDW~{xx)9SLYjv7$Q+TbM%cy%8?_=)2^n8NL!I`UoJx>;OAAJ~VFY3vE9ohkd8gAel_dYr4FRy-u{i_o73oze_=w%=K5D87oiaVYcHz zhMsl;T)%5{zplTpUimg>vB^3siBZU9j!_zDo>s5g-aZ;dV6l&%K-F(X068EWf4O zEkaolKKJ|VfI3RnUDZoa{g!=(N3il0xIRJEX6mDtmG%9CvN_X*?K3mjbZgF*fu9@e zNR`FC_195UvqS`Mp8(443~DEkrPb1eNGg9C_|B+4If98apY^e>5b-p7DE2wN`WT}d zQ4X~Zj~HU&nQ;}%D5_7?d8pO_;s0s7@LZp#QXXzw>u{hV7o><|2(#nw5P@1jBWzx2 z%&`ftInEf#s{n&)X#9tg#Any1+h!_0Hq{{7r$jiXo(wl8$XoH|XmH9IcYo7!?}(e+ z^Fey)ML(^X^-;8qvCn+Af3PGSzvx>-L``ABnOo@b+Nd4Q8355!t8g}J?=}|!7*C73 z?nBUNP$8a)nh2k%0UEZR^=}5>157lq=UrY7fG6G{3bUI3>@uHrXT%`&X?Q`|Vxq z8De~j5OwZjuMm?D&^DdVvf5q+n_MAxO^^KlBO)w3GqefHW{t`%EHJ`)Zc*fxS))V; z#1K=QVil_`c9u__avL|o-Te#U)+fhbO1HMer~g>3e3Ux&Q(LUbtKaKlVbN3jjMVKL zq>Q^pQmq8BnyA_F>lygTZsa{?bH!3lPiR@1I_Z7*ziu?r4cUSN#QF#c4cgiJS}N{; zdF3xUV)yO?*ee-7T{3@p#7l@p`Y(nc`FQc=Os^BKv})tEmD$!f?@ciiuMxn*`Qq%p7CNgW5wze zL-{`F6vNc{Wu8U$t!Qk zgPmkhtx|R8R;b_(m!Q>zwzkyp*T<#(EuB6f2fd!^68 z)BB~{m$bL?;{ZoWPmUib<=V{sN?4iNC+8|=N#+b<_}>ouVal)u?@BK;LzIuDWKUBr zIG1KhN#7nrDTC=ZoTwm%{jH*bwaKV`u1F~BUgzdcwyVkFzAtKV|<{l#lcTd--j)tYZ`m!dCjj z+E}*O?@xOxn;76I7jGfIt%o;vBwl?ok{Ik-X=WAtVex|zDWBdy8AOcBOGrt9QBcP0o8U)aA^$&=?a5=!9(?fyN3kl{)DI38T{b0p?rBT7qv z!#&1NL}T6VLxiigecy2lS^n+x*i@k5etE9y`gQH$M!S8%`t{rX^Ak2(`^lU8pDeyw zKe^>qrS&CDE*@W-(28>=$W59x8L6?}BT^yi|76laiX%0^vh_hr9F~|T4iDk`MfD8n zX@^qvrCbv7ub9*|Z3gd$a!pPqd1Nhmv}e&F8wPFe(CWYap6o!yt+Uf;?kv| zAvG3MX*&phycZg3U0_1X^wZUt^D@jJ!tV1C(tn|{(nvpXQ=k@17{vzx5_}Lg&6Pz}R&M2jQhOc%@R}{imYNOF{;uk8u zI~wj`IJh!6Fxt|}Jbo|lo(P{NaUSiqD4f@_dQoq;fq*ZKyS#B$i>=>UFYP`Ef+lG*F+9Ocw)u`C9(Tjl&4;sN#7Q=qI#!BL1}ti^;SS?rb{* zNo##jMJaYojA#{&v@dfoz=Nr)gX@7aOkBsGmp8t=Ih@DOvY$&X(uj#}HuO8%@)h0A zP7_N~zFMItk|dkw@g0?J@fe~!cBrfBRst7k>AlUgm}yrlXhIbC`xT|T=yac7<{;)o zE1xz-%|H7WQ>-NP!%Pon#+?XmWrN7~nv%jtZ_LdZpo1z|%p z2_+oUEm9#;l>xt$lv(S4I+kB-Ew$&v(Ywy&5K`yg6H`c8l#5*K&1N|}Wlc*BjwI$R zrJS54@4G`|(mtTuV$Vdgxt$ta;b;^uBCBlS%FYz`iAQ+x8g$!m*Z@=+Co;j3}gEe{D?B0T*IuY5)!XNh%ytpA9VW!aV?X7qf5qB0Bm zPhkRO;1W1X-Fg|qD(zxn#~Z1mYbime0F6wI8~c|DeG>K2QIjHf$k3C%xRC3{i5v@Iw4u;jl0e{J9P?Poz$p z>YfVM)cLTyiS+%Chym)BG37+hAh3dz%3=wh^Y`5Ug{ytkz(3|w=1Vi6#8PsH`_4@P zDbasK)zb;6wurIA6{z6YHXg{W^Nbq0Ky(M4Fdj29MhJwtcFtwHHvCYeDs*KkGd`Cz*Bs$Xre3(v zXH-^`XLiUbVo7)Vn-x&H=gjdnU0?<)FjC-qUhw%aYRS`KhEr`Pqc|(-XsIB?^-bmjXWxJ|+JCMz&(`ol0?v;DuqUAy}2a zBsvj2%fq?{jOWVw7PvorMyxC#A)Y;Ucl>|4V=V5*Jj?sPei5>Eg+^r)yZ3w$XCGsn!hvn{~gN^^C11PmZzA+m{=^oCJqvJ0@DUi_1pcv`M&qEP&F1H z?owE9(4;T9A;uRl7~in*2Vf@_c`J?e*$ln5EWk^CXl}t3!)Qj!%}uqW>(dK(IWEPClVp#z=6(vztN_4AYxDyhW$$viR&9b(;o#uAS7hH;ISytjYHT9SydN-oC- z_AhZx_$-QU>a``lZ%Ue9d_AtvcT}ZX_f1alB`NEy*A@58&mfOUP_0H5>6tNXLycqE z`-*nHp7608!5;&^pZ8qO`FVUlhp%zmt3*z~?L6$Jtz zljW+Mv65R;>}{>fM$dTm%GqMJx3bU&_~P=^Ug@>$ld;2(cs~LczvkMCU@G2LxYr*Z zq-zJY_=nPwoZm6_Drk7(=6UPzuQS8clhIv)a^DZl|3itt@>&&lbCyUs`+a5nX&bh; zS#zGRVVdZQ$DdSeEliB*KccjtO}dRXL42bJ=J28UwbED+LmtpX8)C?uGiBzV&R`P$ zTKnLrhEDCBdqMax^PY+faKc3hE%vRAtRJ(;VwIx$WJjTBB+*^eWqR)4ODBHoG|Six z84Y42tH%gUECLx~jiqvs^?8yvb{&}iA!G=VE^++~o4R9$Z<;}Z_X&3)xtfJp%KA@N z6LJF`yS=v8f>?&{Ptb*sCqa5k55b%7XKW{>uH2q1SZmn1u?kkM(@dK#CY+Erej{Q% z7-bJ6w5?;J(Qwiol{+}nu-bN-Ewj`;g=9nv*ddp^WIRd9?TM&F#Y>XM6Dge#dTwlA zWGPs(^fS#fuW9r@V+LQY!Md{!RKwE_~=q-q4yNLjPB7lFkL)gYzWLbBi+h_OHA5Kct zR*rH@HjAt6xQloE7WUXkNN?;n8Mnvih1*|=o9Y9)IPP2i!AbrL&lO5y>Mi@SUL_MW z@$#Z3(dpu^mnCI?92|=GW9SZZepH)ik19Z{B%k@=Zx+n61YRPAV!zEp|k0C@{V=5a88-+7b%x4Kax z4Lgdorv01L>&cquVA*<8y1U;z!ZyHOxnYbjj!3|h%IiD!uq{6;b^?teADHkUf9eGJ zT9f-{&xc;F+OHJ}%~>0ya&p^m5zn&;)c^V|Lc~mkX_+qz_cDaz-%Xe~R;CUHh+Hy_ zht(I^D0$qEIy@$^t@lk=0xKH35eT#QuVrGp*tt@=t*I+cgpfk&V@hgt$hyCk^YA49 z`gO~g3)O&B#s%^7cz3hJ{+p1q9iv+8SqgO91L(Bdg>p@IV*B`~HYZH6RM>QFiQLuG zQ(rqDe(gAOdZTDE37z=|p_@$@&If@_~ecXR(-K}JzU89(Zwvtc>rwt=R?A5Smmu!J6diexd zZa3hOlr<^-_i~R;*`i88Ih(lK*R;iCI{TF;EJ^cPgCa>(l+GdJO_j}sLAuz5+9p(tImB6X#&_&>_1>jJrVf3^5!YN!3JDPCKo3|4HJAh&%yE7LUX007- z>soTR_;O{RtgF9!Vfy^V(`r3Z=}DmZS=?+bASNKOH^;gQC0bXyWGWqA_(Z7peg^$m z^x4}Vj6<9#C_mBVu_66pf$Q@3s-Hg#{51#(1qLs})#WJr$(F|}kRQ3cGqy7=Mrc(( zuZkrt`RuvYPKq>gf%TTEW7)Ti`y9S4QbCJkn={Q~Q7V#Hc$`h*6)8G51sw~KFK%e8 zB~Y<+vJ(>Qd=E#|1>0k<-56p{02Eu}KtqiQ^X@be+gao1KhxjxBK&J0n4nEafZxof zu7Fv#YvLQMN!0br@{r+nI`4f$t0}mM6m;xU6{4XDtO@(gz(W_$m=u;WMpZYxrGN-k zBD<0RO04!M%*Kjv<3tgq!yq?w>~u|ILmI<-Q@)H;M4`@5Z=Y%OK(GJoj}X}{c3Lcb z{tDAA6%b@tJ7c_j^)4`WxE6e*`JqXkjFbGBJ%*^Foh<0HE^#;c3W1FYAbg=Q-iqdD zQ+pyl0J-28Um`%-xI#``-d!l?%KJKh8IPTfm0OnfUiRVNK+6yYeC(=T7{>a^&T4mj zvFQ!8H|?0X?6oLQOrT}V!qCyd!&T1&#?3f(u`_Un=(Q|%;!u66(g#9GNfYQIWIDx2 z@Df|r`cZE7dqRJTE8a62reJ40+Z1VcNTgrD$M#;f$DCL)^eBK5Xn3Z*V;k0WtQV3# ziu=35IMVB09RV3DIHHzm#ezUov4y>Pj!pE@=wW}6lU9iiK>FQ*tF_M~I+Btf!McV) zNR^pu$VKLO*pJCJ2$n_;>@I1wT}goI;$-*Yc@Luq7>)O7Vr;z>o=XcYf1}SNd_^(7 zB`1tFIRAoGoHP^#=6sJ+?m>G`oe1o)9>1%nr^0`7759;eE&s*&W08yKdL-&E5 za8-N;ZY}85Of87+ACOcNyBsCuD8kI9uix0<&#d^N>%|Z4zXh(-^2GeCGl%cxU%hi( z%)Ds+aHc+8fJ=wD`oH2(1JFht1!;bl8iiS)Y|LLFf2pDZ>&sdxsxv1lHZg$H_NBEf;sMs z=6=h1N2d6cCZsFSgxB)1Y?z3npTR%td23xemq6Ah!eDa?0r9^DWik)sRQg&K&!Q)l zr0qc~F!OG9mg=$9+3Tm!omt~FUH&9d-sP6O}|^KTZf&?z>gmilcgZ%4m~ga>^OpT25)5uRq8*4Fr8wBF1B(K?V1DAvA1!ZZQ989b593bAYH57B8T0R z%PzhVz1?Kc=B(Fxj^ek{^AN^Yhb|^Ot*n*Y$iDnu7sI_8N>eagZ3^ zr})}(=0Ju0pR-s#K1?G~t+u5heK6FuAURh?aA=hM&Z<0*nDjb?rG*2X!$i>NEt74N zYW%@LBhfSSIyAM(b9xh6tjT@Iw4-B2A+RPXlk}DhfUOqkhF>iH%l2p&?OQ1_&w&H%!Dtd#KU6}^7H@q!w4V|yPbg-?+8hx}J2 zVJ7Y|Dzf5(3p(sjz9K8_CFg_cF>WS6^p3ksw8L!uHb}D9>6lAz$0KT(a=9p&3ZS2>oSQKA# zC7#%_wx%wbSxd~jaW(9aim^I$L>xbBUUqF)o!-rS9uf2u+ueOjo7j8z*{ONFigP;H z!6r$Q>!om9RjGU7itTFUvz)?{vBj?eH;`_z?;#H&yO!v1;hW!VBi~I_)G0e1nzm0B z%!r^cyvgxp-Q+J~=&HXRj)pX_gG3{-v*^Tbxoa=69~D9Fx7;`xXrUv*Ec>WX<}x_{ z1y8(+9?s117otSs|##85cN2z7h5HB;0!~)qXlP z3^tgN1p)XkZ#9bWqiE8ua>Acy=2@0aKJd0*t#W|VIVYq>se!%w zwatqAGS2=Y-o162`Dt&v*OwM*9!VUWjytqdwmk*PqWgRFf1p6!ZZugRTDXN4t?JX| zbZtz*A1Go;vh;r?|E|_R$fNoj;lJDn-G^aAygf&^4)WEGv+eY{m<1b58nX@!%wo@bMDFim_>y6r*!C{tnd%GUO`S&;S#~L&iHi4w z=Py?d`s;i->iT1(kcO+&vEEsrds~<9&zKj=`3k<&h)i$LDEU`RNMy4@QjJ>>JxvFR z^CjJCl6t8q~MhCmKYC^#i}eX15Z=Q@5C=R_YavZk-#x(RU5C zc+Am42e<|WWESA${mHSI@?5wDyVytRkL>T&Javn_^b2j#SV0QCgS?6`59TnD(50I$ ze5YwE=vzNX^yRZe0ZY7HFU|y0C|>dWTAx5-%6nMWv_JNc^aW`~?>l4t4S}w0jj9v% z%v@r;`IX{muulmPGcs$6gRRH+D141Bc0xpxIImUgbzc8e`~1d6UbrRhiN&?`T{qD- zQ76=TCNQj@nLaX~VAYh1^IUW?`_&k2HZk)ee{`AGGV*nF6RSxkK?ki;RP10zfa92> zo5h&6P^s8yGfF3a)Cc(8eM#W~^ilWWUA;rNbjGTF%zUxhvme<6=5doH05GW(^DB79aW+=5aIh9-nV9{s54P34{nr=?ktG|oDqmQ8!; z;J9ns&4(8mmn67I9{sEPnBx8i6d-7@29%@`iWcwwg-RrDShAjg%Z=eC|dv(EcEZ+4$UGdViIi zZP5G`37|Fce9pv`j3+sjk(l+!nuNgg!2l?NUW^g+Aom>3(=*+|rA3@ZO+HC1sY!i) zUY_b6em{U#**SYcMPR-Yd&UtN!jHAe!jS8*l*b}fb}){=b0@Ttjn01kyU)Ru#Q5kz zc{`Pg*fs~%$-ery3X5ZA-AoO1!O<*yar&l9X(ol1_kS?<-tTPx@B2TcRkeyzqc)`o zwQE$Wo;8cAz1x}*5nB+n)LylVqH5Ky8GDbS_TCbECuU0M=kfXjzVGAvgZwB*4sw6o zuj@R|+j%SJUY--vI|&V;v^Hh%yCo?O`5PG^A2fH>xk7ygCxgtig7AUX81mX|c2k7y zW|m3v!GT?{NPp$z9nwS6;1Tm!A-FpJ6}GD{ix0b5b{$L>T2ta@bzl9bnXlogXs4#2pTBZsq^*Ej)Wog;Ew!h=USAGL7@HaBre}9 zpZsw}!1MZIQWB)2HnIO9Rk?Yx-0L@s+F=4dTaH;j>rLqUz294r=HFIkG(~|ci%(*7 z;!RU^9RT zR>57Y79y9s(+g{XnUp%hmhey3YBvTqk55_+&elE+`K&~=@@-!x=jkHA=+=nUtNJ6l zlPhJ-`bHV0R9?Y;-n<_hK>7O@A=y?Tq`rquU}S)V?K@=>3`N!5=StTb_mUekR5C1d zw6DJ@7_Irsbf*BCqJrkylHExA4N;8gjjU+i(?8Lr+!Hxb@@yLlu)fy`FPMvgCG$m; z=T9(1hiY!yH0C@xA8NX$uJ!yu)v3qLMqPY!M+Fw!L5AUVB z4%%AJuKwyGKG`a4F%ptUUSk&7yy!Rzxv;w5498G8TG8yvZKk5Z68bF@r+7XfcrZo)o_+g=1{cS)A)iPH7@FWOAQ^xC_HS&o0WE8i1ftScL&l+ic&bp zt&wnqNS+jS@n`b14BObB*ev#mp1^+Z|424IajTv9-;CGeuD|(g_e$n1R<(l(3HL^y z;bn45-{5ygI`8dydjyV^mjz4Co*+E!2C{-jXL=3xWH@Sc!ufH}`+z(lnqwltERN9x zUzuZ+B#^#ziGfsv@EKo88&93f{z|LrF1{#ss^8t7+jI=Z`T9?8b=v7Nht z;~}EZ2-gyk-k^g)?gOpE?k~B&nA_Q>UcK=eO9LFbcV`8DME(f+KUVhC z`Rc#R_s#h!kb~GyX>4(&Qh$2KH|a`)RsCRn|D$5Cm+8P?+e{o76bhe zlMi3>VX#R$jZr6YCG7pchkdu&W|x?9I%5`|o5!f@mWf~*V#dBEbE~DLDx40h`fjNa zo9HN8hUxt&V$HYS``b+7UV*skUb(l4H_6{=C8-@dw9=eXoRbPohmiZ}FBFLx$t3Z~Dksvk$hZdzlei z7lV`2p)=iOrxK;gg#efOmH9M31AsLs;#yby)22~io1Yt z-taCD#;-7Fob+1K;zoKkN0 zC6gW!)3Y300@$eJVj7>Cqz`)v{Gk?WQDO3noMtKc`&9K8zOWq?03K_W1p}GeoE>(4 zz&=+u@uD$NDN=y5zNYCDe|S>VlNSA7zqb;zrbK%JX{-Wm5Y=7mlu_uJStRfJ;*J-K zd!Q5K{MS&NRJn=jw-xW9`E{y4V&;q}oM;CC$-D?_7Q^P8`IW%KJ_X!7R&oI0pRbh7AwFfT{4= z-8Uk^`Z&+bmX_I?an1yJ9l`gUvj>7H{7kYW?5Dujon0Kyx4J^T*SltkbI!(SX+TO3 zYW+ZunTJ}xA#OcA5lxjSB3`ul__Qzql1IgSBQZsg$6l?)HX@nPEhKNMBG(2Ymv2Tb z=9OKpUhlC~cHMUAFy4yYEFyUMiBci`X9_1y{pI&lW^jvC3JZes97)n6AQ$QN*yneI z$aJF6`j*e{FX6+wUGZ<(%BU87onXG>E&&eB;~(u^hHoWt?7#In$PDU zUbOSQ$KlK=76-4B&hJ_=^&<77Y6}fQwjd3APES-NWcXVIf9+iNL z99C67nKyg%=!;_V8gQlG{73Y1qw!RA42VPJ0lVCad%JQpK?I>P#iIA_7!LZ8(uG$% zQsIr#7$%C${%HDR_mp8@lC2wQJOsT!Gf!cFw^2}nqYv0TCGI)OtTQ>GJ?Nfz4MY>o zGV|nH0EcCq;zMWvCAKaY_mYNI)pZrJE-jkR`<7zQ=qu?i3Sl6e_}|{_CCVaWx77p}4l-Y>iob z7ltNmS#-)4ry53hj{%t(HpKHJuwHYs3ORtL)YQ<%>#*YQf+4*`>@D6<%}<*OYlVu* zr}mv7`*uq@{1Z&v15?LV(eX3oz(P|it6BflkvQ+K%ledq$K8QZ3JE2{LR&B0)XOI{ zaG{CsvBBM~ck3ULy_uHMWc?}4DfZV@=xAA;)YJUkqxI7sek}M4>oN@fR-RMB?W&78i}pFilONoDSUyd zd-~syx2xmc$D+x<45Fk)i!|S2vJXa`ROd#gZT}c*~7K9e&W^YmC`GDnL ztIUl2w@U49D#Nabe_t?#Kc{_!hlE+Y zj%BeDN3)6wR$w|GhbRh79+e$5Yl?TRd-nLt%r*TBP ziVE1X!dRzwW96KhKask5pzjTlhJx&KNmM;%&Kt7NJONNN_<(Hn3QVhoVFgLNZHhtq zh(^363(>o#IvD4Z?e8u&2xsEFFI(CI1)vr8N(0#OE1j(zGtJgzj#+|!vI!T)p9ORt zTl1*s%O|&U9`!HPyNGG%B+mt!^8c+0tmyfA83*(x1W=6CWaxz72+vG(o7QD8{sf&i zF{RJfqSZ@)sa_ z9r=J@f!evXAN%%L`U9T}6FQ7x-FYMKHFei`cz*QBxQ5YG>jrON1I8|CEy~=TXMlqM~c|28T@j!ks0*;?AQOqCZwVX2Skm z`(y#s@kQLQ<2XK@!bx`&`EcOKjS0AI)6EQyj$7wmvtzb>v{1uBd;?01xtFM1R8J>t zXf>0$22rbzQ`U5A>_|(RXF660rl$6GyQ;dQXxXqzQSdkn?JsJA^$sVf`(T==q%U6G zm&!^7UmmGa@E(0v9fsqerDV5hD7U69mHx|!Jo}W$NDX{ zH7k(!@cuNxRu)*`zB5;0J8FYt&xU&2KY*;ia$O&;5=UJQt%$Ehiz-Ysvt5C=6~Ut* zAeW2}zAkcqckv|9Jx6f+#;wL4YZn7QwC)Zbk1IV>$U+f+KP5&vza2F<6Nda z7V%_?GMhZU_>_bQRTnGFDEk?^K&gL@Ou*2CD&OW0^IEK71Yfpdt;6qBUDE|7F zl~`IGkQL`{6F5paEw@J-b$}`H|A^G9l;F7AM@L&%k~4KIcT-N-rOLMkgX@2?hS+6;`pwvZ{d-tSV`2A`01kXuPn{yi zmBNhS1xv{4b+--?GDwqhBT2+x%OjB3L(`;L8Dmy|Dg>u>oL7nP^!@=!ed;s1Y{4VY zACA4!Z@SH&&_ZQhfWoeHTRrp)q{Gj7dSQF>e_C1o`t>H&&(_W%D3p_=zCgtfVC}qG z2|FNY0V#gZGI#Y#6>qZxX8mQrAl&=nPj1oHw@a3Bx=ol51N6qe0;w}0b+`2)LURoq zPN2x!VdrI;oR*jiw5Ko@EgP3Y*rd;?nLAS#61&}bzNgvC{XB5hvKG_E2FW71GIyJ2 zyQf2L+ARXJ@w%OYZEvzxX5w)Q^HOEoi^27ESCAm$6~L>S&=H2+0M-fk`sCSjBfWU) z!k>$Bg3enm7C)PGHDv-Ib~t#)5gDGQd!DSzFZ;pnWp*SwbYK(aI8A{#c&|>HACxPW z@Y3J~KauqcgV-!wdrDhfaLzQHd zz9H*@T<<@k>XeGo?G-vtQA*R@RNC34?aOZOx*aZBb00(Z93$ zrWm~&S|aJ#{nV#!3H^eQTLWF{&o*n~jMn+fQqwjBrK8igo5>$|Tk?KXf8e$sYe+6g zrqW6On5D=E$iekK^~Jl5wtTk=ra*IdpTf4n@&)fn|Vwwv=9 z)3ANe-aCR}-(ncqF`Z6+oZ<6*FGlWVLPVLRKv#{EHWOubqtV$tM;y*J(Q?t)qKj9K zw{CMlKy8yjDH&;h6xzk^myWgm+yl`#QuS_W@E8lU80I^8YuWg%iE2IbJELOq9A3On zw;tPDq8L$Vy0oI|>KvKa@hFY?g9}IGny<|0bZtRCgeFu9#%2<$-6K2>ZHM&#&ZEYZJK{>~4aW`?kl*P?4aa-KKn2*G^`Kt9F(G^&9XeM&2 zcJM2K&xbuC-|XeFN$cN3`_F35ygWxlzMRZN-g{?%us0R9xynU-E>)o|zY@l21Z*N$ znQm&XHH}bHlJdsz=H5wRS5k>p4sq&%t#YHvKIMf+e`{PAc0RDlp)#MB&9$~hRSv2o z<$BCDl;R`bqp#Wj8?Q-9)vL=a=QU&MN_g~9e{F*x6#m8jSDD#?>GZ|xamj7Q7ndG% zeLUi;Vs^i|s}?V$c5LfyLxZd8Do*Te`5lg1!%Xt7%lkT~eM2L1JAN)&t*Db8p5MSz z9_e&X`xnE+;D~bTE>t=%L~z%% zt^_8~(UZfe*0u!CAUK}jQLm@qwXP-eI#jJik6cMuj~xE9apHAh-C*Kwj<)1rEYWz= zCe)^7v7WtJ?mklp5*Cd`M}TVjE$7GO8-7^J&wV%ZxwBvN+BHk<*^&8DOvU`+yIRX? z4Q@;=@>S9Hg`8zt?zR=g&yPkj&6&tBez9D8EHiiGOe!^B+sTu{FZ=I?_%njsOm*;i z64p_|^1LlWCU3rQ;6mP5(&8~@!zrb+SDyJX6k(TT_>lgKhJ`8lmVtG5$ zv|WY9D`o?sN4g37w~m!JzxEC-28m6%xohc1&dx7u{#bD|^rGRNPL+1U@P`9u`?7tG z`SG*o#h-zk5g=?rrDm*KVqpLr(e6G?Hnab3T-Im%3Lb_NLVaWTWvzcj!1>5j@unA; zt@ij(n8QXb?ve>S5xH%zEQUANp=wAyPGgX^~y{EJL$SI+ex%G_ur6@{zoa7)e+ zq`VCq$Ayvn1#>$_=`oF#9RdOgDs*HJ+2}C&HB9T(HUGQB5c#JAu)F1HuphrL)*!G5YKbf8Q{P zX*xlp(v9noVNkSDT%)Tlo)N|cKP*4AZ}qGH-k53f5*h4_8SGnTDve@$JB5_+&u#pj zXukC7DMbXmRVNA4YGw5*pWL%E8hRiy5grl&3P#K=EWc2*JomCRhBo$3#C@(>TA*p~UH=iiSG0ib z{njsB^h$M}myRgVd7o#z+%GA8byP58tzlPD}5Xdd<{TVF3lGYW^1pg z^i0}jq+l;^IyDPvn5*BnjVTSw5gVMnw$0ByJYBJn+xy;19AOn4~-(WkLoD86x*=(#| zD1uzRpYT{PO5bVi4#_n7BE!D%ONhoCXx#CL5mm3GtTgE!dK?&QC~Mv4{88`2Uv98_OCd48({Kp#tN~= z4wt8?@u1ql#qRUsLM~i`niKJbDmdT43Sa#?(X`2~w*F?I?N@BCZVmYdp&%yzy98)t zt+35rlRG3((^9&&ti))vzsQ0GCHFwCJSQhcWng}8y(OV{Z-1A8&6>5pebt`R_06uS z`);FRk?YT$X|EOgNTCH;(3pOy7ME5`aN;C8^l{(=$n-$r{r_a-fSZF!M{E9$y(y z`^7>p@FM8tk}637*>ru@V&s{dqvwuX*8H{1QQ68yg=L=#ixl_9Fa)B=Fqx8pF9GLk z+p4b!+K|*Nz8e4&oUK(8dChEWxezC)d^r18XNG99u*c1jR;snBaDJDx$RP8hdg7v* zEEvn_2u3R}oy|ZAHMMQ`ueWr*E`aZ8bH!emwgkVfNrFfJ_8t=SvJH1rx@a#jML5D< zZ8p}_t9f+8Dwmcpd`_OcHreeY+EqB)v$94L%RcHtDAW9}Wzn=>zC8XLgk5- z;7)P8>F`X2z(e59EGu1!Qut347TUkL zBEfFv*UQb{`~~GlXp|rPnuRnb?51aA!49nX9(Z?UHwOf>pUOno1f3i-N`mJ~yXbi7 z=vlAzad?lsW*m>jUn4Q&h1r#E^v#qFC12%kZuC{Ofr{~&5U7#Ag>YKi0@^g)^5gbF zO4+|l2y}9cI{)RRYVSl^YBu)OMe$=gedo(1IxE`Ur#tuk(X{L~`_FSNv-Kh);q-$) zRe602lA+!*%dto7=G*?>A|wF{V>D4Be{{P_O?pa{E?iyIJ=w=am;>pxn)@pJVQ@m~ z&E4!8tDmz-F?ZKnyY$+1a*(AB^QDG2dp85rW>`!$fIh{1eU-md`{$9JiS=FJ>Rd$z zuaL^I$7BpL#U$zamyZF3lnrlipSH=@{Z)kI-=+`v^b93`snLrWt>2E(j zIFEb|E<3_Mbj%F+k4bi3R_ggs$VNg=Q(XebQ(JElV)m&!-j6P)q6V+7_Wc_#H@DzA za`uTF7gJsZgGNH^ue@4mW)911Dg1<@sD$>tTxzPtennTtKGgw5AG=5#nOndhzGWd{ zVNwG3)z7c4E6Em)ONrK3FDpr~cj~Jjfu|jg^SwO;qte{c1MW^CR~U;jbSfL-bes8| zM#=8x$1Smye48R-NuD*hr||dbgLgDvu|lft7wPP2B@2!$Q$y=^hwq*Xf2y2|Hf&S`Ra*N+%jf zTgOAfdzY4|g*eUw@e=kp_ui+oh%n=9wb8XKA!rmn_wBK{_5x=RXyu8Q?1!f=;p^uW zg`NO1daxV(;NV6;6GNJ~nt2hbJ|$z-+Xzv%(LKly^B(ftLbiD{$h$y?!S--Q!o$Yt z07eAQfui`YX~`i^QA>e5)uS9U?|0;z<#a8gNo_oJf#iMo#MO%KwmU=lN`SFL)8TTd zz2=v-yE9JR#hB(vzWTlYR^yYs-nJMMo20bSG(&#kqx7-<1;Ij47H;#wpYsagp-Z3+ z2?+_ddW#nKVHJt^Y@=xpkT+U&|DYJJiJ?ihE!%4qshRYMQu2N@&Wd>VMHA7)T*>`v!0s_8`3_Jza5Yhi0rw#dC!(a(zz z%_Uus3ncC<_C`;ZSkFJ;6q~T02`_6BdTN=ajA3O5R^}7AA*Jh4-d~4!2dlJZS(!q6 z-EtrOJqo&cfIq@eZCuBNNP_EHQ_!MT2X9mOJIL*oF3V=@SsORrH;b0 ztK-5II98WLuZ+xUasK6nBH0g$wxaZ$tWOLABfLHLBDY)Vs8o&d{Qj#}cL5mmONODQ ze0W*;>$lwDEH5@FoO$m32T!>wL2dGI*L=V;<1FC}76?JnJLdhRCi78q7^LH2Wn&ExtQ zbgj2#^v}5`08W)*2&wCYSMKTlmW@{;FlS$F2xtsuTrMS&%MPaW{qfvmzlDE~9qG-c zLK>83)8T`_4-s%x|M&ac)e*vTP(6`y37HyLDKq>+af7f1iB*%l= z1GldKU>1_k71}_0DG}uLb6-jtG8MZJ{4{)m*~cmQjK(HYeczPJnpjgr{di%Xls8 zQMG#h%HwtMF!fwm+Y_p|BxD0mnU|#k)?nm->*@t0>-MN8o3t8|3vDI@a^BOhmEHy^ zos5sEcYQqeQ6DBZPuvr8?vpR#lTNk&o7>kafE2|7fh`_%MfeIeL8;hrnF%M1k{*jO zoO|C=+Ys!GUnf6)%GGh(zhkp`Ci^8#i(lXY^ADuPSfc6#L3x$p=q^H8#RQOns38;m zkFuZq%}ZA?l;8S77+#VijyB3f^r3s(R%2+o6uGDEEB&k(XX|ncqkN+3KTEGHQ&Pnw zPNYN3w{sDFmW9^#*b#I=30Bk!AO8aZ#HsONU18c+o-sY&Tuu1r^Pbf5QVwx3JtE|0 zB>~zPfgM|W5oC=I!Ofl}_#(wOu=CdA?j7yiB$&&cu#}@asaC zXw|!}3~q3lKAs4OCF~C^n}G9^gIYL%+!Sc!6^&F-Dz*xQ_2oIz?n>*Lbv|mhy&j&n zG3}UZDc{~ zSX;~@pp6-@w^pWV4Hf!--dshlc#xz|S6#s8Du#O|+Z141e;>hvTkjGS>PHDo0o;fY zqj3dg9JLCJnpOVUItmpvbo?RZ;OikdPq{wlZ)Sd}^;QAwc_CVqZnu?j8umwKUTd+= zy#j`7FzmPtwlc;m0qvRDgMc%aeVUbTVdO zmUjglURrW{zzX|-0nlBzp^~XRrb7oq!InB3Dmy7I3m>2TW=~F>R=>QSnK)YPqLI~V zIwcI#ccv)PO?v(YHMB*68Ze5emK8L6sl@#Wy8Aimi#n@Uof|l` zY#$<9ufFit>~D$jiPZxnH6GysXQByRel~TM>D_+UcKUrwb*vDy-cUzGz05@Y1H7)nD7j}&pjS1ses9mly^OEb#Hjcg&kqQrRhpiWkQFKb z97}`cn+E>X9zvWAFJ-x&I;7B*lSd!1mS=j~Hry?XYesB&Yv}xXq5;UM(}RSObjcwS zvl(aSG7mPEE8Uac3}?>*KBIjf+2EI5p}vg?hzCepZ0_rAo~-aUwFCmDbu5hqcfvnDpR)%tJk4 zG2w*Lchw$)V)t^F!bzqZfmFcqtqk1NFUH&Vw{$VbQ|5F+% ztoNa7uiT7K&bDb^#2GY@2JiPeTX(X8+|19I-XkIVh0qefQUHuc%iyt7jEuJ z4}y+03)iO`e??KB@*fd>gMO^wVe*wM5;`N90rJ+s^3E`_#5BvKRD(9^S4eZk2HJn$ z<6<+xJ_sUbs!+$DtkN(NjBsy}diX$6xx&wOV@@|iez(`fdJ6h(|1tq>P_8t1*uqnp zxjwCbGu+H~&kno%s#&^^7s~C60T{HYvkn>Kz#rvL&&)Rl^tEFg^;jz@(sahTHi}KO zLx#@6;i0B&i7H<=t$(bck>I1IPr;R~P6uZS(Z|amtbGKMqHF{`^3Wlb#WL6VK{Gw^ zz^{R-Uwjs7oS|=(_M!XW@GROH0{jQTeH6(|o2}b0qws9dMubkfWr3vnzos;0)a}kK zep`gq0dLz}+$zoO-G{p7&>JRfW@~1dkN2&>$9qF5I$W6uQ5L!2AQ!~Xx5s<%PE3fZ ztV#Jk&ZYjqG8h97vU)&Z{jQFI(;-YsPq&e6vNv;Z-YV zui0@*wWICs4Hw}>?WCvz2RbSPFWF~Y&I{oVVN@P}KK_h~-zwg1dFqVnJZH`7KL1Ao z92^}tt3di=)*77p_zk6t&w&7tQjE_ z$yRL1K}_gR5>)LW-jq;|d5yo`$X|CJ2<|m~zkJJDJe_h=`;UmHquFBhhz?I?nbE~A zGQKh4Y332DUS=csCvuxjT>lICTWX2;(T`9L{^`~>+)TFx7@gfG%4miN#(0M#pM!cc za&Om<7HUt`vYC~rQg!O&^i%0O69k!GhS{)U zUEF&-!E|s%y1l?^t=^nGsHIqA^P~dI-J9OAb5dhI`b&lmKMQsdXo_*wXmV_q-EfWGs!n= z*;$%q>;||Mw0-CeqZQt9p^N=-ss(D=L+y;YJk3CF_{27%+jzb2Ys6b0&Y2YU+Ee}m zWsa^XY++~h>fVx@Vk13H@msNB;v1k zL+fvCl&T*&V|OL#fVQ=>YN;v4E-4z819CTP7>idQNci2@JZSL~7xq3Pws0Je^g$({(2qMh=JXRZh-B zX+yjzyNiCe%XX0PN7^-D12INno=Y5Ez7?NE^>k;Z%(Uvl`y^4E33<0;GANmiAADe< z{mpO~c-uqEuj7(Gtob^!`qnE7(0*c96Ri8bmOFQU$obuXH(F4rRgFxD^Qrs-6L2vUjc`vZ%Ma@h zBy|{w9uCoeNR>?bjZT(LRYNvSu(UCEDBuxN63@1_5JS@eT045su?F(H|B8Eu7C3tr zEPf4iafxo$4?k#JOPpZPSxxO630s!I$F5KTJG3~HanC1bBn8F=i4KXvi2{L7`*~x+ zCmGsTx1@FgDHX|)dRUIG1&+vNa;(;L8|H}Kw0<-Xl^`AUU!AAmj0jWAj0lC3WhAu@ z@9m918_}@VXVZZW7BsqHT^D*i=om9+l!RK_cC>PlXkvnQ=uWz~t*i{$u>z`#MW?G4 z$9E9dZ2<-9xDT2~_)epY3+mHfxiPrx-&()rKBBP`AdD2pRsdddbRau|%xp@uSss() zQHOIpkGj22*FI3r#5KF?9W9RQ0A?dH#b*%kgTqou8j9M~D355z=@g(HPk4dFh_;_a z1I@do;D*?-P(6^9iF77#tol_%d5;Em_UrlG$+c(blu&+Gnr#osiHLsl+jy|^Q$3N3 znEGIP5C5R9NZ*KcR`PD#6^Ia^dJIHDSDsdLfAV%xvVC&}w0O(vB`crEV1q49$n?^$ z%#2&$c+<uY|L6k2dp_uoom zRw4TT`q?!)y0?J$Ll*%t0gz}XBXG7pt}g1asct+pjyhlsU*j}n^R}v<0W&xAWnVIZ z0#A7>NbX#pY*uqzcppnNTm9I+VZ$+O0^5L(P_Ofmjf@G$>5WB~!OFB)hF}r*VIdwb zsAf;wvH0#kqO)u8O{`-+#0@55-Jnl7CinaM>A;u3k?E#vLmyc6n5Wg;MPH+3s*exf zbnx?=3lc_@VUBPCVNCr&X=_T+4|POv2(PW!Whj&wCpQMdDUVAOO8 zI-fq5KN)o~EgWR#tfKu?T(-g&ABeFEWe4Fa$_RS3RXFio??#b)pW->o&M50bpOomT zhBO~f{*70h?wsof<=$@q(0m^9IbH@v1{6nh|JpURsgr2Uc@R_EGr=3*Njv$9{|`Ao zKk;+f$7S$T3}4ZS|3+nXbJpk(WiRtnR8ya<$;+3cEj$a@b##5wrL z{`-N2>i!N%96)z}t!D0sQly_+t&BVD-wLnN_on*z>X}gQP1`4WFRxRAwq6eh89^Ow za++P|c*D^=A=Qw+JY)Q8G6e=6dhHW_y!t@s+qVksSFq)Pk;a4UFsV2O6U0BF_Qu+* z5C>5>z1CC~BtiTI2_ziZ{f#(F$9G#n&o#$9U?|2l7ZI?gTG@t+l+J)C+_*bOcJmFE z9GDk`nE>(WhGMwaWjMg z;NhQ5AOWl3iE$*|zEmmIHkZ8ayLCrC`8!5(AEVw(zBAuc;}SX{LB)NwM``@Q!?5rk z@Pl&xs!`A9cX~I;#J);dyn^M&2QdmgLWr#2-mkx?_`V7gw~A`Jw^oZiSidkz_YM>B z$kdkq$W>SSiEM!(x9<;gu+2JgEOh9Gas)1VpoWrjr?(aJ*o2@ z?NP_ew$A#rBZs}RKrDGJAovHzJv~Rk|v+x_3~}Nfj`F|nS1(f^~U{u zyh7#%@Hrn$wq5Fd&^k%a~Qml zTrcR25+p~vwcFKa>S?u(i~CLi+iqHUEd38C8Q8y4ePe`+Cjq*dbqo57iJ{e!2F1 z6N8dHLEI`|`421k=j+|4TP`{RA1Lxb0;^Xd+o}k*#Y)$yr4X+=c7qB2xmDY2H_;3I z>s^k{jcaF}Gdw%3=Yuy(L${kZ9dhH7d1v)}fu1sj=vdeJMBZ2}zqb90TZr)yvoS@! z#{N~0hOjpYmxpTuS0X=wl6cv>&m?T}qGF9U*PNl>u@|weTssfj7^Q~nRL2O))&S*A zm{#zh>Z7Ij-CWZ9^zYiAPgSo3fA`O_-i*{L_Pcj^(p$%})g4Hu{9|EdeqJY^pqZgF zen=JlS{QWmCsFPPj5z73@cn2;#m$Bz=Dka!ZkkWa&s#F|an7ws`M?h_DJowIPqjRs zNVQ*o)tbaVCo=O=Qwu)$o7n=^IUy~txON`-`p41F=oMKUK^Fp}D$Bck%PST>6A)(2 z^Uk;WrAb@^@6}8x@-1#AI)FL*9>NG+_ha5Z%$Xonxa{NdidFUJps-Jtdejqfw!hi| zhvRpI+K*N5R45H76ogik?#i8wHcx3aC*JM$2^y#HY#&xsi#Jj@>kUZ$_8Z(}*>ELh z^*C+q0IHYLE7VgB*zjG~2R`L~$s-DJ@h$4Tdnniz)+!6VEbInT0mHKUA|h35)S2Dk zfU4OzRQ#!zfH|_Pwlf~Je(omo#)?mN zEkUCT`soADq%34%&ijRPO{C?@MQ>CGyn+Be;qCH07QiD$Ns!GQS%tDG4h-*Zs@1Mn zi4FafVE6+QA^O5OLh_9No6Q@cN98Jj$l$Xo^p7Zl{Yh|_w2|06kFwNI&tkX<2puWG z(_pZUEhO@M#^Bz?QkM@|6=mG%S{w^ZIucYdBdUB7*Jh|{{aK*Pkzw%!Gv)Ac@uG0(X`Ga!rVSv1D8z;l^Y2^ zi;A(gO415+uNe`Dus&fDU=4qBN9|GkEcdPBOdq$l)&?@lL{34El)QJz)t_fAu$% zjMaUsi2&P&zinIwW5cdK!B<%tJ4evd&(+KUVCc+UiRr^oNf5^Sqe*8fYRc&KaQ;J+>2-Bc zaD}=OXNCVq*IE8G{l0w|MMV?^rCVCMdw_tnfOLnFV~mc`C0zmn(jXlJ=^EW2T~dRM zlHSOX-{0;(;C{N-o@}4EuIoIH^LX!t@_Yn-FDvC9X1>vHPB>Q^cixam@MJ*#hl8gE zCTlL~h@-MCnUx3IIFrPZ)&(8~2DC^j!6g$MG97r#y8e_MFt@Lxx~}O2ol(?-qBlkz zGZ&Ni>+8^rE-LQoW}3{TV4l;DAFLRpzqU8~>D%>LL}mu{$7_C`rXDC%o#Q^V>u3l+ zr1Na$>pxWvpN~xQDt!``eQThDg2SD|+9dAG7Mb}EB*Lqvu%WL(X)2$hraQK}Pm|u; zzL045W3P~@k8AH_t>1jGTppRcZ`HuYX)XM#WE#Th#7N7@ITC<+)4ZVO9wmebNsm9giyvQ1A zo%D(1iJN(D0h}+%@+AR$twXg?wS(Iu`Tm>ESg)V6ont~Fd^p3A*h~mlNsR!`3Nf#f z^0P$oeu)?!d?S&InXz;`uz6`7DHBe%(?5rWl#(3%gF{%b%yzYefvTPpVQ4olUf6#) zL3&vnpsKRlNi83655AL}+Edy$OZX=4f50TdO;(Dbrw#mt{AoKBRxDuxy zEi`9I%_L8nRP4+jn)>G5DIsme`26<#(I12KPfm6$DgqZWYsTqjX`pxw;IYTt?3RB} zVcFpP5FXt{*mMpCIDsnEF%nw4=h7OX3Bf8erVH~v1UMisES(#u?F-m*4oO7elQi_I zqqGX(iFNeH6)bk}$L2qr^K8A@NXbCs2!W!1Zf;UrDJu{7IvS|8@%zGkc5GM{dDhn8 zBJqhEAzj)Gby$M`Tf9-PPOKR4g4%G^ zJPMWSNu5w_W}-J*1UM7hZ!EIQRd8KMjH!szSo%t?ur#b;a-selR z>=>s}P{9~bu0*Ge+rmH(-?H|i@kZ`>PHhbtR4VG;$C@0@pC8QqnQkysGFB_E`el}u zL}_CO&Uev}E_&8|S@Ba$?8YFVaK(f%AbC=MmJH4T1-k$)RLEzp!$j_wYu_zOi8{iMzo9mJ*a9(C$=i_N#Sh`2``Cl^9cn z!$`$X`_JmC%syGO92vvR+ATi*hokj#h}2nm%5ISd7Ujgv@UCplmG6VfKRoxT^NO%1 zD0gEztlu~(MH9OV|iX;?6W`5TGRX(yk@thzLJ zLwowdV1Z4AmRr)*-|Yb4dw!J;BSUMN>FQwNgS()~!}S|ty*w;sJ#-&=9|r9@e-SvD zB-%PaqpmU$1~vxi?R+H?aFLjGZ3$LaXMs)qQek$gWc5lj#XY~^l_{P0Szvo1@u(~tV!lAU}gbu%{B5Z?Bjtv<3hjnLcriimry zSGl%n;f#`G5PQaYBup6pOW?q3-zS3$ykK>}I-%~VS5o+Ly?G1bAxMRFObP}bcX0+~ zgv@+znr@%X)PMSVVPl3)(tughakgO^S}hbYe|#a_nCPeU_WIL~A`bCv{&l1fFpz9c zSlM_-{OH}c)&#O#aD+E~Zx~JH-z?*Wq0qxFq>boZfF_;) zRwPdEp8@;E1gr6VBe3fLnPD_8muEo3$Bxx+ACrA?c44;fqj}uzHG<2;ScfA%dT~)5 z%Z>`r*q`MU=jcE|fVPWSXG9FWmdvnkIjvd0gw zaW7ll#|MEOw2a~$w=<$~aHLZ|RZo@f6%gf9}PdJ@{^icYZrksP0IjK@!}Eeh)pF+d#-Bd%#+=JoyX!L*XsRspw?_x zF-S2p*&9GGCY(!8s+!7t6ySfH8;PgjebdxhI^bUZFL`-iM^1Erv^kv~i~kx0V!<+R zJaHzdi)$ZO#Uzb@ME=tl9=Wj98(od8l0z>W4EBka9Un@Ad*2iF45_vk0R=(i>R^JY zzB-ZAqKv2yc;z_Aa{Y+dN;CVbx2DF+eJ(SKU#&B|*>x5fe=v;K&PIDODQXuwIrDW) z7uB{k*PAJho~k9_)PEld^~LQznB3NJkfxaPSkZ4!GcihI#pi4ODIT6^cfOn0+ZMn> zD}mi)^=57Y(aYmLURKEzr*C>UL1p4ABmblviX5SZkl28fK4$$f+A%?=x275C6z2f? zi8iZ9b#wEe(AG1paWs$XMg8^YvJ9uij%$PNuVs1qtg!7+l1}N*njbVB=k$gH+~(Sg zf#huiJ-?3caybd?A8c#=I#uX*{hB z)2(U69$=P<4M((`Mla2qy4JefWsIh)&#@rlv2A14WQY@!pO1IB;)Hnjxv*mMoxV3~ zRDcIWbN?dd^0bKEeY-1#p|{oaalq408yknd8G($3nSis#<3*<;y~1L9gSIMSGp@9S zsSiDLCeb2gZ222+-48j%tfW9?(`U{bx9zc6K}Ro*YTnA!-;XEcHyw@bi{W;N$!CuV z_v`i|?u*G!%vjDY>i9g>MNX7EKriPWE9kk5x>Z*RP-?pBfeO%7EZAA6fp;uxam37) zLFJ~Fic%3W#yx55mf#cxW=BPrgRmH#MCMkd^aV&9fJ1$M5h>8jfbyz!eXL1s9FZo- zrGF&CmV~EGOuz8^U)(P&8c`R+ckT>qOQHCWd)lU zSUXFUQ)=nC1Nut>t$ZrwGE-+vR!Yn=CP<_H6$=-26+F>b0}{`W^pr`Lc=uC;wZsD__}KitWk z=lSOL&_&fdHv!K2`pzb_b^7o98wKnzw)Q7phPcAsf2{;Q;&`;22?^}h`Vd4?Z(Ob3 zu-3_*g0TZxJumm5Zn_k}V2G?km|DMKO=4FMmM}QQo+cmCvhi9#MJgC4*#YW7_lj## z;s-zL-u5gH8IeRt7@ptZ@QPW z)|vP`Ottk-K>lt;k}Hg{R7geb4GL8#0A^7IPW1In;a@IGwOB zgLDahjkEn_gP&1#6i<5u5D`jaO||?nhYd$s{$_wn{WB zp;8<191wGIM@p2R549!gU;O*8pP zsofL*$vQGnA2UdDa_J*QGO9q;j&wMW_z@nlI*(!-D}e*hpq*LDbDiL9IFC;QQI3E zi7=*nE$3%*RPsv&Gov_867lK8(64>}!vUFH4&605oYyR2ojf)1X=KasV|`$rl*+0} zim@WwLGYi#Il|qKU&iU2b%2v;fT4t5bQL$64TS+XfAN$TtI+gV zcitM}YEYviM*N#;qJA#;Pph1HVWrb4FR;7Yo1=T*ToP-%6PX=g$wyjj|L#We!-6#R zO5kWpygcH#+#LsC(eR@`wv=pjQ6&>j|I|Z<7{m4%lUfcGrGgf< zwFBF{(&?kQh2le=?J*~Vk^#L1-#PL}#MvlG6jeqBS4_|bEc%JuThPio9*7IiY86l4 z+zS^4nbS!c%dN$!-w)WtXF?myG<~R!LE4E*JL)|1s3eE1(tc6Xm;6E2!r@cDHTQIf z2s-o3BIbc&7Qk6)0=&$7b!V`#Qb?VZW#$_sHikX&zC1Lo#>YExhgT0bN;WzT;Tu2D zqr78bCYN~_1%K`;kj&Q)S29bN0;@hR+(U0kj_~kO{(;`1K>wg@19Z64UA8}^#}M>y zOne?#iPi$r`xEh&ZB1k@sz&p?@58d44z$l>)1A-QM`z;v)=yi{5~x^HfL_#d8*w_L zm2tmm&SEZt!gHTvfaU&t==6R~`o1bvfM7tWEZDAejal35K3|o9B|{nK+Gi~+Bq=PH z7vkP4gC`6o9#|(~#f~?`K>gVf1y-PE5p6}b$2*tX*-u0{$pF90{wSNfCJ1V0wV0xV zc=+6K?#lm~H=f(3pyUxE#oncm9f~_y`fy>3r~7c`h@P4xXYLb68(lsjxH-yCow0|Z~b*qTsJYIkqzrFRQ#Uihw}Ae8{1|@ID&aULy|un znYjBUn8ojFCc`Vwq=Vk5_Hhy?9kM@BNaRnlQ1mB~#%?pi87k<5*H1rVg^Nsyw>pPi zcDK%#7D>0zPP@Chv!Dg5#P;@>yiIs}%^e-4{R^_@={^-Wq65kMd~Y!`XFe4sFqmHF zME+Jb^?iG$fc*PiFC{HaeLJf1@j)ypiMvytFRqhqRp9bWI-``MM6ko;pMqb_5qb5x z1%|U5TWbO`!Z0bM4T}^oNvYb84$KMOe0XyCi@giBETA3Z z#j;%bVAgY2{$WC{TZU_KDv%fxhO8kD9L;;{&s(+6DUw`t$2`4Wmd{T9fODzi z^g^>Xu?d7sG+3-Vm;M~XH}j2;Q{sO9BTizsPSLmi9TmMoFL9Q_Cn+Zux)7#5I3r4? zUnW+cjtycx_6HVFYl zFGSdPL9=|iRx_Q^Xqg0mqs_@BGJJqZIm_aub3%S&z2VVjxaBv)+UEr%-uoIV#~>xv zU)w8p2YRg$mrBk%ydEe1#~U>0*Kqnh_E**yect9+moKyj{(~ zjm?e14kn|9*OwyiH-0+B&3#`$v>zPY63G=ViXYu&x6sv-?~ z6rw}fs;)Hh@VHoPuB~?9>F#lYPeCwFSOQ3=uah559^9A}L;}&jcD#_}+reLczJ2<) zoI2P=QBgyz=2s_=p)8vCvKwWdf|S4fhJF%AZL%;3nD@h);pyRf#tkzXo4W=ME36L( z^t?yRC>d1|(JS?Uu$r}lptt^PXi+t^S1aPkF?t4ze>TjJ?OmYg6Gx? zzPZw@@G|Xe#Q6+}v`t1X%LHG<>likpn`3x9)Se5CwL_)tk!Qvxjg+C!C;*~zs{smb z9#rNC{hD*Aj@qrvB{m%Zcscxbe|<8%@GRf|C`t5D>+9DPF0NWe9Yx;Xu|i&{@n6FP zDCS%f)r@WVjU#5eZ2Dr~$fKu4*yZUzoY3k!8PfB~J|XliDX|aRw$JRP07^O;}K)@R8&I@qU{5^I4dU=cSoGvsz@X zUVOgZ>BRhXv(r|>4~b}xhpJvZD|IX&y=M-9%{jy0vN%+XeU0k?7? zryJMt*@>!?s56M7IIHyg#`&#yl7MXB>;UDf)z&0xrqWLa?I4S1*hl#75HhV9`O{yi zYz3ocFbnqZGvWNVk!;;&U|A-sNG z&nuZ1+H+=<&3?REG2)}fWg;mM>pFlY5&iI`Ij-gH6F+38QcsT|J5NkG^7fj17BK&n zAPCpAZLqLNEAdKErnkySo_7AeqiPlkb5|}q;~S16!>Og~PQe(o-1%WOd6N-s0q4HF zeTfuV6s#X-VJ55<>BdgK1O(ZRH~$v@;MZGk)ey{dPv`m9UTs4LQT}u;`w&Fs65rLr zxi3Y0X@k533?CNVLabgc$1G&e7A1@g`uewm3wR|Ru7jPDGWL6bEe9jyRJEM7}f zLx{VbH+iGjD?nY#=1iSa9yNzx1qS)2rDH4$$eMP^bO&_OguQl4N3rBmkeQ|=CUFxx znHB}dGDP3fZbqpExWHT=iRuNOHiv)JNtsaotl8l%C)ILU>v2&gh7P_ zNk)5DX}vQn+T((a{q`ck_+kJYm69b6s7Q4p->=RLOlA3=1lC-Om86+d=|=Y5h$c7* z#S46SJ=FTm2AV0dBO zKU!*%YtfBoXD#makdUbJ!N{A7aEOs}P-+I6mZ4{3X0iQYXc0~viF95P?s~NbU+>JR z7z2C1`WMak4S0sn#D6Hcl>SacQ9whi@UVG_N<)7RRR+Zlg{1Mnc{nCKFW;kgs0it9mw5Z|6=Y-j1>3Wj7DE? z3OR!g^ROEKR?3L*MP#3pWMAHM=@+8$hn?&t;jFbtmGcaMdNEMOu_N<2 zshpBy^2QM5b;g+gI!6owri5zOYsGZbfP&uKDc!dg0DS|Rqc6Cfo~JRD8>v$Xm+9hw zGyN;TgR=0Uf}*r0M$%dJM(lp{8AN(o*>@54FrWLb1#Ny9gpvcyxpM@sS`ld34&gO6T8f{?LH4i%5LP(z?`N%>{s;kd~D*`e2or|Xv7j1M2H zs4}+M8^**1p{(&oJ2j%3A1F_{lS})cTB(h+Upe&xTyUM=6P~_y$HN$*h6c?^>*ns!+tB8>!LqxAz~@v+?16M;Sq%`>&=t;>YGoVqbVm5yj2JzKO+L)vfr(o99N z8<#REO)@d2yZdIU-pp!aNN)iqkUHmA|KZqTy04|V>rv@h_fYy1>3Q;>_5a~u!J;(q z;KXJ*Y0>T|mMBTznbygEHE9XjhpOuXd@P+AUtqGhDhz(|bp3H~K z^X!or4K~@sY%|+OMzruF6Mp3`~MH5{J+olmYV~qA%zb_HRQktPBIISgnl9E z-^f3wcjH+aYk$QiBsLakz4CCdT4TakIN5AX6}0-usc{Tq7lGtwc4N1GYNYvuZewx6 zlD=~?^H0#)`Y;rE@BMM{P8NIP2KA-bU#7cJc8;+uwQ(Gc(eqWLq%m*O*w#ues%(xs z9GIaKpg!*4FW|akL#BwqQBFf`vDIHlu~Y*wTsBJKFc3~FthovJadn>7zUDmPH2QmXAKdW3TjC!yfqVKBLHTq*D+w zl<>bs6A!>|9Lklv@{A`0T2Z*E%a8-}C0EosVi1ZY`??-ABu2!RuG;&0{z-dazO{yW zGf&BO{mC%dvfo<%@S>2CCi`mz<|TqI$7X}@HAoXRri^wfN46Ao#DOICW-UL$^*KS7 zv;_5am4fc45gO7?x@%Au0C}G@7n(dApn?~B&aDpu+b-(>ykUS)`T0&uS+-RwXz_vX zU~IK463nTcyQtUPa{I~KrB#LWELoOT=H6rcD?V5lv;vA3+M7{wWo#_86WQXwUmu?w zo6Ut(np4gRN%5LA%>fj!^N2_76owgGfeQ;rOIZq@{ND%5#r#sjJeWx&Tf8)*8i+R@ zAn#tD_5P0+B6Kh*R8~?8M>b`ZAa`q?+k1fS9U9Oyb8u7C47CA~##f{8Qk*Gz74va$ zXl}E&w$99R&{*b^q61TdNPG<;|JaPW?}~2dVEp1t4Xf?r?pHRe+p4|JTRg38lM8fR_8N}Xu3pEivq>p-^93mIYp&HD`#eiWTOnWFyD_~K>ZjX2=aSu7P59(hVv+$#?{oI zmlxk&nh{L{=Rs@aPGPi%J}2=0=8BVE6gmyu8L>X-rp}0D_xn734ZGB=baj9tp|i z%!-F@|D4d}u=*Mt^zFTc!_l2e1%?`tIM79He|jf0CteT*)l zF>p~r^Bc^f4A|uDyrV{9L;vc2OMue{JOuwl?>jn$1!=I}^qBR5Ox7ByYABGsCF1a@ z51Gz<9iq-f!~NjMd>R|Iii?ry3w(^4Xs+_-38GL#zTce}7Jrx)vQnAV-sr6kdH!hZ z8A}#ZZjO6DBywY?fPTp2Bot2B9-$UemZhe0!n{P+8>+yw@#ac-msHt~7g0kG8SABE zP9K2NESA*GmkMM!>5DDp{h7Fn(1vYYRz3(|8qtKvS4H}CeCX$M-1+H~@zQ-@{I{0z z1Ha-`5Q$GVGfNA`e_><>W_e@p_{pR4XW@Q8$H2c+3S43A$@?!!pV;A2USMS*iqkOS zvOA8cKvsx%FA+xR^V|jg`OXaZcsuuHl$C;ved3=n)?MB35!CQLQVJGb<~g;$xj61(Oq#%iz_&57(`e6vOd`I21dbkr+wDNn|E@Zz2)o zA&3GeIi z>6gNsnRhum9214b-0UsHNOAFST=+PyqB=$W_0hID0Y#YD!E}Q(IexBjlA z##Dr~d#QXGnZRdkf_bAjABcEs1L}v`6h=E6S;NbfYn7u*P2VZ9j8NJvG5+Rw?Sm^; z5y*;BFEPXx76#1;&`0UfhYjV(=D9RZcBpmmu(G1oyL)7Ek+o&moW45975eFj8f#CU zB>b0$O0b9XcQwyO$eF~))kX{=C_T9;#3=vj*K@N*^?>Of- z-{e%%iJ4;%o=3c-{Ok$98O^4O&AX{wy6+tOl(qm|D{#C9GbhC4u zm5Dz^E^TMb_B)@?v1Ik-vd>miU5=WFS99UEb-?-@lOAq_-wABdaim2eUpenCO=ZW8mpPsp)!jbpn&LYQ$en z$NzBr@=r`+QQj&G{yGH^rS7h`9yM>wLBs$-fLFcAfW4L)8TY%f>M7-S)|>eiZ^j^t zNP~ojcd>?dUyvs8iqy30HX%t&F|>bIdu|y_u9R%# z2!8S2knWT7yO5Ne%DGEuA`Qf#2TrR@-$(!E@J{tp&8t7IhU$)&+rDp^HzEK8G`D){ zR^7%sk=V(;A%hu1Yz`76!X%3Ot@uceF>QNXl&+)2Ps^>TClb!FcS^r7L3*gp^a=o# zM*B8TvBMkH)vAwm&zP;hBtRG||4B0}+zI>MM1+-%*tjykT@gwOXC7ia^1Ke&FN&F& z0^n9-5_oOT$UZCY0x4-7;7MrQ!DW_?aBQlXS{6j8bvJjEFR7&&Sa`a{-eE$W9xCBI z=u_RLjXKW5E`Hoc5z)vsGPT{=<~`()Je4DPeF>5~jpS=N0}i5fRZ#+&@$2qYSI0ay zPO!tGpART88LN#%IF>Zq`cXqRd6LJ&lDe!AuHVU7la*6aB9VA;8x88>!h4-Vba_0< zNj|P|Mk)+R8+cAGrpJ@;;RE9)dfHAtpm9K2dMV`c=4F61FC(cTl8fl^<1`MVxo2Vq zT!p_ktST3=7pJV@1b+=uD36IQ0lfpttmy048mp0{>qozj@sfN%D2WY82D8NsV{1J< z@x%jd9VY5AZ!)KNhG)W81yqfYZ|&%#tv_aCVAerGBS8E_X> z6IET6c|s2~Wls9zreQ3q-MWRn!5h`Mh%w71iElAkNY+HMVo(0Fu7=Fr$?w6^ZeK%E zXoH6b;lW=U{Yg=svu3EZ#dv=+%f zW0*M-ivN0`K6?=hHeSYi%(>!eli>M|wp)xqV)KdJyBRB?nT5?>QT^qUNSJHsq#uZ5 zjHFFB{iSew4aapd^N{qndrkvG%sg7A9|)qW+2m|*IjmurvR5$<{zynBY&5y zp-7SUla4=WgC6&Qp2%Mdgb1KhX7CF>RR>Hqi7_Z}oyy#DW%*D(Ely>d%wS?nF7-6@P6n|~y-y}Wmh6)@RWbOH zIj5hUvsJg5MRwVj2-MYi|8p~8zI68Cy?yhHt_uKImE$a+fT`uLJ&w}IX^UG8L8YU{}lQ<6< zl00gl@nO^Q!5G)Lf6|~eH#@6*gm#|D&g2+YIORN$%Y^c3rH}UKtW+y;a1{!7V$`*Y ze1cWNJR$NWQUGF<5T!BxHk*AY_yE_D=Z|EF^{wfS7!yPFV*;{=&T`-2eA#!l8q3O3X zz2M4HciI$Jn@x6HQhsmzICeY8hRExe5=9&rmmSnYfEVi8$cg=#i7)=(&89OXWG#~q zx^sW(qP%B5;E8fu?0p~$lbh*$yBGL=o^3k$VgCUOr=Rj#`+Pm4KGcutDN3MS_rqf` z*)tEi6*E&{OAsb}ZN4X=cacz7=)f!1F^g#Num`hR^8;c&S@TaAJV~`s)m^*JOH#8M znS@W)Lg`;Se79e&@{R9JGn@~`tyUheqr;b;uSHO7=9z@zGZ`3g_YowJcHPW-(8H3& zYLDazwZ;(jU8UzSS2qvk6@^EJQmC3U=0hXOB0?5u*A`4znx<7lCOB`+{XR(XQ*NnJtV;8sczUzILMUS8kSkypo>#WM zx}PopH?8$4`8p9)I#YGvYx3`EaG*pb{VPm|Z7qqy#xOw1Ay(}-Rc`sS^xY`He>m6n z3f7DK{|1Zc^rB*?IpJ#(N2lzMb?-TiU)j!kPzsr3J^PUr=1%gv^$>o(vkbvVeNJz8I6tRmo8lg2vG)3~qC%Q#tyv@{C5)$!fc@po%qkhk}zg}FW5 zh^AFUO~!^Rk%o4gx>Ql)vF@zDLe(BVmxRsTcO|~D;4%%r@5;=9q)*do%Jvp*FK3RG zw=LyG+*|1KcdG*qy!)2vV*QHubxijM*=KNr7y7T6CE_l2&#Q7|`mn%{ck;^8dO;|h zH4_y5{Z|FAe)srWnD!?Ii>KPfdc&zuuZ0`n^}h%QiQfxXY>BU3DB>JXvm?HAvTmJz340l3;uTOR(!70^Xgb>+o<`0A_6|( z`BKXK<$Qu>{im`x0!Mk8vmSbON>XmWgnQ)(L42z7`D}1gJc#wiWO$5%Lprsv6cT1I zqg$MMm&72>8U7_ttA&QX*k;qdTYSK2PvrYZp@Z=^a+iiAlfX-SOXl!1A{!F`&1T-G z1&^=C%K?=CHXUY43D*b2n!$P(PGcou9#Y=7f`(MkKbo$LRatKCXLU&_xv-4~(zv6u zyB62osAKk(4A=r%v+Jd!5?invWaHCm^fx?Xw`PRh$;Xvt(gV4hA3bj-TmCtgT zAEa$gI~(Wsmq~vtdlg>3BHA1Of;;BS=jM;ubg4F^gC)(<9@uOBC>*CW*;14tjV#c9 zQ$yW!?knj%eXwGc)Y#eno9z&~b^MGyXv4y{!Cwj*^!2`ON9<~K637DMQIr#WboK^b z6xlliXK7p|pO(%8up&*GD3n7Jb4>0FhUhMWG^WpQ&FE=Rh5n zg`hksUSRkK+}{dcKkUuX=&5r2D5ek3oOn}CXH;1usJdyfP<=wI)6&K3{vtd^ll_qf z>>lnr+elA*kU8GAvY+`ao#SV>r)(U->krTB1qazV?n^R%a?#PMs4WetH~gKRYt-xb zJ?rt0fxqv|OSbV5J6qE~-^-;8Yw*qv zjL!aC5yMVt8sf|n%UQEh4iwb2P&>?rC`e>Ozaac#c&^JnO8>m_OS!=hJ8kxs!{vEjuU2Mn@2|^#B;TL& zs8SNeg&Ig|B2tXa>E{>aT9S_#*ZD$~5mKpt3HWKzF(Wh{g=`BcDL+zdbo*v2@#WN1 zaE7-ogbKySSxD{Vj16mjJl*{~oQ_S$INzJzvoAC+HZjVA#p6EZnpQRt4f}?Dcfe8J z(po52>F+I}%j1lRY0fAp-XBtHoMQ@?3Ge}5>bVJ!s@b$$k*4VAaF`eNO~tdTg1nu3 za1QjhJKOw}599tYM8^*1e9tY?Qw_lXJLB<1XVaj02df|V+~XRLbx5I5K5fI!&s=#2 zob(B;&^jM>(sfLCjz@N+0!D)Nl7sM0t8U(Vr|k;#YQ@nCQ?t&_ws}(yIW(5;-y|e5 zsh6w?3Gnr75C!JX8?%>-`Lj&C$=uF_neU}}zB168uTHypxKUxXXj>XwFD=$AJGdDv zf4pMhK&(zh)MFTDO<;{4xf0G(U=g=)ReB{WG)I;NtSxDqOhruR4-gW%Ik6?RHPgSR z`zbD*p+K??jV+bSdF^Zq^;)^GzBIV<_3T|;G6iV!dP)T(mX$j-=IEd9ORi)sq#>DF zX}iJSJi29g@Jje~|CQH&yf;tDlZkoSdLLJ`v(^v5l zH_F;_1W}Lcnk20G7|W-1PI$w!G*TsMx67$(1lBK8#}|s}3Mx(2yx{e<1M9PyT z&v43xbJQC z0RDzDPw|DdIuUCc_WehLA1?70>dHn+TGM++p^>HL@M$)1gNP6

4T>QO?}2QmH8# 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 V6VK!=nGU7f$RB@8yx|6A{{tL^N3{R| literal 0 HcmV?d00001 From 4e6a89424ae37c8c9bd081fd6ac134ad90f41149 Mon Sep 17 00:00:00 2001 From: Luoyuan Xiao Date: Wed, 23 Jul 2025 19:34:58 +0800 Subject: [PATCH 022/132] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 537af2093..027166f30 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ fxmac ethernet driver in Rust on PhytiumPi board. ## Quick Start +For instance, [fxmac_rs ethernet driver in Rust on ArceOS]([https://github.com/elliott10/arceos/blob/net-e1000/crates/driver_net/src/e1000.rs](https://github.com/arceos-org/arceos/blob/7e52baa8bed7a9dbfc59acfb9e07d3f71551d651/modules/axdriver/src/drivers.rs#L133)) + +![fxmac_rs on arceos](doc/PhytiumPi-ethernet-arceos.jpg) * Initialize ethernet driver ``` From 674c4b2d2ff457239138e0efbc38b1a6512f672b Mon Sep 17 00:00:00 2001 From: Luoyuan Xiao Date: Wed, 23 Jul 2025 19:36:14 +0800 Subject: [PATCH 023/132] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 027166f30..17cc8d9f0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ fxmac ethernet driver in Rust on PhytiumPi board. ## Quick Start -For instance, [fxmac_rs ethernet driver in Rust on ArceOS]([https://github.com/elliott10/arceos/blob/net-e1000/crates/driver_net/src/e1000.rs](https://github.com/arceos-org/arceos/blob/7e52baa8bed7a9dbfc59acfb9e07d3f71551d651/modules/axdriver/src/drivers.rs#L133)) + +For instance, [fxmac_rs ethernet driver in Rust on ArceOS](https://github.com/arceos-org/arceos/blob/7e52baa8bed7a9dbfc59acfb9e07d3f71551d651/modules/axdriver/src/drivers.rs#L133) ![fxmac_rs on arceos](doc/PhytiumPi-ethernet-arceos.jpg) From 1bcf9feace828cdee555620a1c8ca5408129f1eb Mon Sep 17 00:00:00 2001 From: TQ <128586861+YanLien@users.noreply.github.com> Date: Mon, 28 Jul 2025 08:45:12 +0800 Subject: [PATCH 024/132] chore: fix formatting and linting, add README.md and test cases (#5) * chore: fix formatting and linting, add README.md and test cases * feat: improve project configuration and documentation - Add unit_test CI job with doc quality checks - Refactor README: highlight timer-only support status - Add open source license - Add Cargo.toml metadata --- .github/workflows/ci.yml | 27 +- Cargo.toml | 3 + LICENSE.Apache2 | 201 ++++++++++++ LICENSE.GPLv3 | 674 +++++++++++++++++++++++++++++++++++++++ LICENSE.MulanPSL2 | 127 ++++++++ LICENSE.MulanPubL2 | 183 +++++++++++ README.md | 76 +++++ src/lib.rs | 1 + src/timer.rs | 109 +++++++ 9 files changed, 1398 insertions(+), 3 deletions(-) create mode 100644 LICENSE.Apache2 create mode 100644 LICENSE.GPLv3 create mode 100644 LICENSE.MulanPSL2 create mode 100644 LICENSE.MulanPubL2 create mode 100644 README.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a2d898e7..c5aa8b9c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - rust-toolchain: [nightly] + rust-toolchain: [nightly-2025-05-20, nightly] targets: [x86_64-unknown-none] steps: - uses: actions/checkout@v4 @@ -25,9 +25,28 @@ jobs: run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default - name: Build run: cargo build --target ${{ matrix.targets }} --all-features + - name: Check documentation + run: cargo doc --target ${{ matrix.targets }} --all-features --no-deps + env: + RUSTDOCFLAGS: "-D rustdoc::broken_intra_doc_links -D missing-docs" + + unit_test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rust-toolchain: [nightly-2025-05-20, nightly] + targets: [x86_64-unknown-linux-gnu] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: ${{ matrix.rust-toolchain }} + components: rust-src + - name: Check rust version + run: rustc --version --verbose - name: Unit test - if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} - run: cargo test --target ${{ matrix.targets }} -- --nocapture + run: cargo test --target ${{ matrix.targets }} --all-features -- --nocapture doc: runs-on: ubuntu-latest @@ -41,6 +60,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 - name: Build docs continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} run: | diff --git a/Cargo.toml b/Cargo.toml index da4e2c2f4..7aeabb95b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,9 @@ name = "x86_vlapic" version = "0.1.0" edition = "2024" description = "x86 Virtual Local APIC" +authors = ["Keyang Hu ", "Mingxian Su "] +license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPubL-2.0 OR MulanPSL2" + [dependencies] log = "0.4.19" diff --git a/LICENSE.Apache2 b/LICENSE.Apache2 new file mode 100644 index 000000000..f49a4e16e --- /dev/null +++ b/LICENSE.Apache2 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/LICENSE.GPLv3 b/LICENSE.GPLv3 new file mode 100644 index 000000000..e72bfddab --- /dev/null +++ b/LICENSE.GPLv3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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. But first, please read +. \ No newline at end of file diff --git a/LICENSE.MulanPSL2 b/LICENSE.MulanPSL2 new file mode 100644 index 000000000..49c034c1d --- /dev/null +++ b/LICENSE.MulanPSL2 @@ -0,0 +1,127 @@ + 木兰宽松许可证, 第2版 + + 木兰宽松许可证, 第2版 + 2020年1月 http://license.coscl.org.cn/MulanPSL2 + + + 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: + + 0. 定义 + + “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 + + “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 + + “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 + + “法人实体”是指提交贡献的机构及其“关联实体”。 + + “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 + + 1. 授予版权许可 + + 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 + + 2. 授予专利许可 + + 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 + + 3. 无商标许可 + + “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 + + 4. 分发限制 + + 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 + + 5. 免责声明与责任限制 + + “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 + + 6. 语言 + “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 + + 条款结束 + + 如何将木兰宽松许可证,第2版,应用到您的软件 + + 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: + + 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; + + 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; + + 3, 请将如下声明文本放入每个源文件的头部注释中。 + + Copyright (c) [Year] [name of copyright holder] + [Software Name] is licensed under Mulan PSL v2. + You can use this software according to the terms and conditions of the Mulan PSL v2. + You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. + + + Mulan Permissive Software License,Version 2 + + Mulan Permissive Software License,Version 2 (Mulan PSL v2) + January 2020 http://license.coscl.org.cn/MulanPSL2 + + Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: + + 0. Definition + + Software means the program and related documents which are licensed under this License and comprise all Contribution(s). + + Contribution means the copyrightable work licensed by a particular Contributor under this License. + + Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. + + Legal Entity means the entity making a Contribution and all its Affiliates. + + Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. + + 1. Grant of Copyright License + + Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. + + 2. Grant of Patent License + + Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. + + 3. No Trademark License + + No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. + + 4. Distribution Restriction + + You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. + + 5. Disclaimer of Warranty and Limitation of Liability + + THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + 6. Language + + THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. + + END OF THE TERMS AND CONDITIONS + + How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software + + To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: + + i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; + + ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; + + iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. + + + Copyright (c) [Year] [name of copyright holder] + [Software Name] is licensed under Mulan PSL v2. + You can use this software according to the terms and conditions of the Mulan PSL v2. + You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. \ No newline at end of file diff --git a/LICENSE.MulanPubL2 b/LICENSE.MulanPubL2 new file mode 100644 index 000000000..1a09083be --- /dev/null +++ b/LICENSE.MulanPubL2 @@ -0,0 +1,183 @@ + 木兰公共许可证, 第2版 + +木兰公共许可证, 第2版 + +2021年5月 http://license.coscl.org.cn/MulanPubL-2.0 + +您对“贡献”的复制、使用、修改及分发受木兰公共许可证,第2版(以下简称“本许可证”)的如下条款的约束: + +0. 定义 + +“贡献” 是指由“贡献者”许可在“本许可证”下的受版权法保护的作品,包括最初“贡献者”许可在“本许可证”下的作品及后续“贡献者”许可在“本许可证”下的“衍生作品”。 + +“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 + +“法人实体” 是指提交贡献的机构及其“关联实体”。 + +“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的“控制”是指拥有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 + +“衍生作品” 是指基于“贡献”创作的作品,具体包括对全部或部分“贡献”进行修改、重写、翻译、注释、组合或与之链接(包括动态链接或静态链接)而形成的作品。仅与“贡献”进行进程间通信或系统调用的作品是独立作品,不属于“衍生作品”。 + +“对应源代码” 是指生成、安装和(对于可执行作品)运行目标代码所需的所有源文件和与之关联的接口定义文件,以及控制这些活动的脚本,但不包括编译环境、编译工具、云服务平台(如果有)。 + +“分发” 是指通过任何媒介向他人提供“贡献”或“衍生作品”的行为,以及利用“贡献”或“衍生作品”通过网络远程给用户提供服务的行为,例如:通过利用“贡献”或“衍生作品”搭建的云服务平台提供在线服务的行为。 + +1. 授予版权许可 + +每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、“分发”其“贡献”或“衍生作品”,不论修改与否。 + +2. 授予专利许可 + +每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销的情形除外)专利许可,供您使用、制造、委托制造、销售、许诺销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”中的专利权利要求,而不包括仅因您对“贡献”的修改而将必然会侵犯到的专利权利要求。如果您或您的“关联实体”直接或间接地,就“贡献”对任何人发起专利侵权诉讼(包括在诉讼中提出反诉请求或交叉请求)或发起其他专利维权行动,则“贡献者”根据“本许可证”授予您的专利许可自您发起专利诉讼或专利维权行动之日终止。 + +3. 无商标许可 + +“贡献者”在“本许可证”下不提供对其商品名称、商标、服务标识或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用的情形除外。 + +4. 分发限制 + +您可以将您接收到的“贡献”或您的“衍生作品”以源程序形式或可执行形式重新“分发”,但必须满足下列条件: + +(1)您必须向接收者提供“本许可证”的副本,并保留“贡献”中的版权、商标、专利及免责声明;并且, + +(2)如果您“分发”您接收到的“贡献”,您必须使用“本许可证”提供该“贡献”的源代码副本;如果您 “分发”您的“衍生作品”,您必须: + +(i)随“衍生作品”提供使用“本许可证”“分发”的您的“衍生作品”的“对应源代码”。如果您通过下载链接提供前述“对应源代码”,则您应将下载链接地址置于“衍生作品”或其随附文档中的明显位置,有效期自该“衍生作品”“分发”之日起不少于三年,并确保接收者可以获得“对应源代码”;或者, + +(ii)随“衍生作品”向接收者提供一个书面要约,表明您愿意提供根据“本许可证”“分发”的您“衍生作品”的“对应源代码”。该书面要约应置于“衍生作品”中的明显位置,并确保接收者根据书面要约可获取“对应源代码”的时间从您接到该请求之日起不得超过三个月,且有效期自该“衍生作品”“分发”之日起不少于三年。 + +5. 违约与终止 + +如果您违反“本许可证”,任何“贡献者”有权书面通知您终止其根据“本许可证”授予您的许可。该“贡献者”授予您的许可自您接到其终止通知之日起终止。仅在如下两种情形下,即使您收到“贡献者”的通知也并不终止其授予您的许可: + +(1)您在接到该终止通知之前已停止所有违反行为; + +(2)您是首次收到该“贡献者”根据“本许可证”发出的书面终止通知,并且您在收到该通知后30天内已停止所有违反行为。 + +只要您下游的接收者遵守“本许可证”的相关规定,即使您在“本许可证”下被授予的许可终止,不影响下游的接收者根据“本许可证”享有的权利。 + +6. 例外 + +如果您将“贡献”与采用GNU AFFERO GENERAL PUBLIC LICENSE Version 3(以下简称“AGPLv3”)或其后续版本的作品结合形成新的“衍生作品”,且根据“AGPLv3”或其后续版本的要求您有义务将新形成的“衍生作品”以“AGPLv3”或其后续版本进行许可的,您可以根据“AGPLv3”或其后续版本进行许可,只要您在“分发”该“衍生作品”的同时向接收者提供“本许可证”的副本,并保留“贡献”中的版权、商标、专利及免责声明。但任何“贡献者”不会因您选择“AGPLv3”或其后续版本而授予该“衍生作品”的接收者更多权利。 + +7. 免责声明与责任限制 + +“贡献”在提供时不带有任何明示或默示的担保。在任何情况下,“贡献者”或版权人不对任何人因使用“贡献”而引发的任何直接或间接损失承担任何责任,不论该等损失因何种原因导致或者基于何种法律理论,即使其曾被告知有该等损失的可能性。 + +8. 语言 + +“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何不一致,以中文版为准。 + +条款结束 + +如何将木兰公共许可证,第2版,应用到您的软件 + +如果您希望将木兰公共许可证,第2版,应用到您的软件,为了方便接收者查阅,建议您完成如下三步: + +1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; + +2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; + +3, 请将如下声明文本放入每个源文件的头部注释中。 + +Copyright (c) [Year] [name of copyright holder] +[Software Name] is licensed under Mulan PubL v2. +You can use this software according to the terms and conditions of the Mulan PubL v2. +You may obtain a copy of Mulan PubL v2 at: + http://license.coscl.org.cn/MulanPubL-2.0 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PubL v2 for more details. + + + Mulan Public License,Version 2 + +Mulan Public License,Version 2 (Mulan PubL v2) + +May 2021 http://license.coscl.org.cn/MulanPubL-2.0 + +Your reproduction, use, modification and Distribution of the Contribution shall be subject to Mulan Public License, Version 2 (this License) with following terms and conditions: + +0. Definition + +Contribution means the copyrightable work licensed by a particular Contributor under this License, including the work licensed by the initial Contributor under this License and its Derivative Work licensed by any subsequent Contributor under this License. + +Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. + +Legal Entity means the entity making a Contribution and all its Affiliates. + +Affiliates mmeans entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. + +Derivative Work means works created based on Contribution, specifically including works formed by modifying, rewriting, translating, annotating, combining or linking to all or part of Contribution (including dynamic linking or static linking). Works which only communicate with Contribution through inter-process communication or system call, are independent works, rather than Derivative Work. + +Corresponding Source Code means all the source code needed to generate, install, and (for an executable work) run the object code including the interface definition files associated with source files for the work, and scripts to control those activities, excluding of compilation environment and compilation tools, cloud services platform (if any). + +Distribute (or Distribution) means the act of making the Contribution or Derivative Work available to others through any medium, and using the Contribution or Derivative Work to provide online services to users, such as the act of providing online services through a cloud service platform built using Contributions or Derivative Works. + +1. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or Distribute its Contribution or Derivative Work, with modification or not. + +2. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to use, make, have made, sell, offer for sale, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, excluding of any patent claims solely be infringed by your modification. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that any Contribution infringes patents, then any patent license granted to you under this License for the Contribution shall terminate as of the date such litigation or activity is filed or taken. + +3. No Trademark License + +No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. + +4. Distribution Restriction + +You may Distribute the Contribution you received or your Derivative Work, whether in source or executable forms, provided that you meet the following conditions: + +1) You must provide recipients with a copy of this License and retain copyright, trademark, patent and disclaimer statements in the Contribution; and, + +2) If you Distribute the Contribution you received, you must provide copies of the Contribution’s source code under this License; + +If you Distribute your Derivative Work, you have to: + +(i) accompanying the Derivative work, provide recipients with Corresponding Source Code of your Derivative Work under this License. If you provide the Corresponding Source Code through a download link, you should place such link address prominently in the Derivative Work or its accompanying documents, and be valid no less than three years from your Distribution of the particular Derivative Work, and ensure that the recipients can acquire the Corresponding Source Code through the link; or, + +(ii) accompanying the Derivative Work, provide recipients with a written offer indicating your willingness to provide the Corresponding Source Code of the Derivative Work licensed under this License. Such written offer shall be placed prominently in the Derivative Work or its accompanying documents. Without reasonable excuse, the recipient shall be able to acquire the Corresponding Source code of the Derivative work for no more than three months from your receipt of a valid request, and be valid no less than three years from your Distribution of the particular Derivative Work. + +5. Breach and Termination + +If you breach this License, any Contributor has the right to notify you in writing to terminate its license granted to you under this License. The license granted to you by such Contributor terminates upon your receipt of such notice of termination. Notwithstanding the foregoing, your license will not be terminated even if you receive a notice of termination from Contributor, provided that: + +1) you have cured all the breaches prior to receiving such notice of termination; or, + +2) it’s your first time to receive a notice of termination from such Contributor pursuant to this License, and you have cured all the breaches within 30 days of receipt of such notice. + +Termination of your license under this License shall not affect the downstream recipient's rights under this License, provided that the downstream recipient complies with this License. + +6. Exceptions + +If you combine Contribution or your Derivative Work with a work licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (hereinafter referred to as “AGPLv3”) or its subsequent versions, and according to the AGPLv3 or its subsequent versions, you have an obligation to make the combined work to be licensed under the corresponding license, you can license such combined work under the license, provided that when you Distribute the combined work, you also provide a copy of this License to the recipients, and retain copyright, trademarks, patents, and disclaimer statements in the Contribution. No Contributor will grant additional rights to the recipients of the combined work for your license under AGPLv3 or its subsequent versions. + +7. Disclaimer of Warranty and Limitation of liability + +CONTRIBUTION ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE CONTRIBUTION, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +8. Language + +THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. + +END OF THE TERMS AND CONDITIONS + +How to apply the Mulan Public License,Version 2 (Mulan PubL v2), to your software + +To apply the Mulan Public License,Version 2 to your work, for easy identification by recipients, you are suggested to complete following three steps: + +Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; +Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; +Attach the statement to the appropriate annotated syntax at the beginning of each source file. +Copyright (c) [Year] [name of copyright holder] +[Software Name] is licensed under Mulan PubL v2. +You can use this software according to the terms and conditions of the Mulan PubL v2. +You may obtain a copy of Mulan PubL v2 at: + http://license.coscl.org.cn/MulanPubL-2.0 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PubL v2 for more details. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..74f9f6746 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# x86_vlapic + +[![CI](https://github.com/arceos-hypervisor/x86_vlapic/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/arceos-hypervisor/x86_vlapic/actions/workflows/ci.yml) + +A Rust library for virtualizing x86 Local Advanced Programmable Interrupt Controller (LAPIC) functionality. **[Work in Progress]**. + +## Overview + +This library provides a software implementation of the x86 Local APIC (Advanced Programmable Interrupt Controller) for hypervisor use cases. It virtualizes the LAPIC registers and functionality according to the Intel Software Developer's Manual (SDM) specifications. + +⚠️ **Important**: This is an early-stage library focused solely on timer virtualization. Do not use for full LAPIC emulation yet. + +## Current Status +- ✅ **Timer Virtualization**: Fully implemented with support for one-shot, periodic, and TSC-deadline modes +- 🚧 **Register Definitions**: Complete register layout and bitfield definitions for all LAPIC registers +- 🚧 **Interrupt Handling**: Framework implemented, core interrupt delivery logic in development +- 🚧 **IPI Support**: Partial implementation, some functions are placeholders + +## Architecture + +The library is structured into several key modules: + +### Core Modules + +- [`src/vlapic.rs`](src/vlapic.rs) - Main virtual LAPIC implementation +- [`src/timer.rs`](src/timer.rs) - LAPIC timer virtualization +- [`src/consts.rs`](src/consts.rs) - Constants and register offset definitions +- [`src/utils.rs`](src/utils.rs) - Utility functions + +### Register Definitions + +- [`src/regs/mod.rs`](src/regs/mod.rs) - Main register structure definitions +- [`src/regs/lvt/`](src/regs/lvt/) - Local Vector Table register implementations + - [`timer.rs`](src/regs/lvt/timer.rs) - LVT Timer Register + - [`lint0.rs`](src/regs/lvt/lint0.rs) - LVT LINT0 Register + - [`lint1.rs`](src/regs/lvt/lint1.rs) - LVT LINT1 Register + - [`error.rs`](src/regs/lvt/error.rs) - LVT Error Register + - [`thermal.rs`](src/regs/lvt/thermal.rs) - LVT Thermal Monitor Register + - [`perfmon.rs`](src/regs/lvt/perfmon.rs) - LVT Performance Counter Register + - [`cmci.rs`](src/regs/lvt/cmci.rs) - LVT CMCI Register +- [`src/regs/timer/`](src/regs/timer/) - Timer-related register definitions + - [`dcr.rs`](src/regs/timer/dcr.rs) - Divide Configuration Register +- Other register modules for ICR, ESR, SVR, etc. + +## Basic Example + +``` rust,ignore +use x86_vlapic::EmulatedLocalApic; +use axvisor_api::vmm::{VMId, VCpuId}; + +// Create a new emulated Local APIC for VM 1, VCPU 0 +let vm_id = VMId::from(1 as usize); +let vcpu_id = VCpuId::from(0 as usize); +let apic = EmulatedLocalApic::new(vm_id, vcpu_id); + +// Get the shared virtual APIC access page address (static for all instances) +let access_addr = EmulatedLocalApic::virtual_apic_access_addr(); +assert!(access_addr.is_aligned(PAGE_SIZE_4K)); + +// Get the per-VCPU virtual APIC page address +let page_addr = apic.virtual_apic_page_addr(); +assert!(page_addr.is_aligned(PAGE_SIZE_4K)); +``` + +## Target Platform + +This library is designed for x86_64 architecture and targets `x86_64-unknown-none` for no-std environments, making it suitable for hypervisor and kernel development. + +## Related Projects + +[ArceOS](https://github.com/arceos-org/arceos) - An experimental modular OS (or Unikernel) +[AxVisor](https://github.com/arceos-hypervisor/axvisor) - Hypervisor implementation + +--- + +**Note**: This is a virtualization library and does not interact with actual hardware LAPIC. It's designed for use in hypervisors and virtual machine monitors. \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 9c08909b7..3832302e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ //! Emulated Local APIC. #![no_std] +#![doc = include_str!("../README.md")] extern crate alloc; diff --git a/src/timer.rs b/src/timer.rs index 8a12f959d..6c993fb76 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -282,3 +282,112 @@ impl ApicTimer { // } // } } + +#[cfg(test)] +mod tests { + use crate::regs::lvt::LVT_TIMER::TimerMode::Value as TimerMode; + use crate::timer::ApicTimer; + use axvisor_api::vmm::{VCpuId, VMId}; + + #[test] + fn test_apic_timer_creation() { + let vm_id = VMId::from(1 as usize); + let vcpu_id = VCpuId::from(0 as usize); + let timer = ApicTimer::new(vm_id, vcpu_id); + // Initial state should be stopped + assert!(!timer.is_started()); + assert_eq!(timer.read_icr(), 0); + assert_eq!(timer.read_dcr(), 0); + // assert_eq!(timer.read_ccr(), 0); + assert!(timer.is_masked()); + assert_eq!(timer.timer_mode(), TimerMode::OneShot); + assert_eq!(timer.vector(), 0); + } + + #[test] + fn test_lvt_register_operations() { + let vm_id = VMId::from(1 as usize); + let vcpu_id = VCpuId::from(0 as usize); + let mut timer = ApicTimer::new(vm_id, vcpu_id); + + // Test LVT write with valid bits + assert!(timer.write_lvt(0x000710FF).is_ok()); + assert_eq!(timer.read_lvt() & 0x000710FF, 0x000710FF); + + // Test LVT write with invalid bits (should be masked) + assert!(timer.write_lvt(0xFFFFFFFF).is_ok()); + assert_eq!(timer.read_lvt() & !0x000710FF, 0); + + // Test vector number + assert!(timer.write_lvt(0x50).is_ok()); // vector 0x50 + assert_eq!(timer.vector(), 0x50); + } + + #[test] + fn test_divide_configuration_register() { + let vm_id = VMId::from(1 as usize); + let vcpu_id = VCpuId::from(0 as usize); + let mut timer = ApicTimer::new(vm_id, vcpu_id); + + // Test different divide values + timer.write_dcr(0b0000); // divide by 2 + assert_eq!(timer.read_dcr(), 0b0000); + + timer.write_dcr(0b0001); // divide by 4 + assert_eq!(timer.read_dcr(), 0b0001); + + timer.write_dcr(0b1011); // divide by 1 + assert_eq!(timer.read_dcr(), 0b1011); + + // Test invalid bits are masked + timer.write_dcr(0xFFFFFFFF); + assert_eq!(timer.read_dcr() & !0b1011, 0); + } + + #[test] + fn test_timer_mode() { + let vm_id = VMId::from(1 as usize); + let vcpu_id = VCpuId::from(0 as usize); + let mut timer = ApicTimer::new(vm_id, vcpu_id); + + // Default should be one-shot + assert_eq!(timer.timer_mode(), TimerMode::OneShot); + assert!(!timer.is_periodic()); + + // Set periodic mode (bit 17 = 1) + assert!(timer.write_lvt(0x20000).is_ok()); + assert_eq!(timer.timer_mode(), TimerMode::Periodic); + assert!(timer.is_periodic()); + } + + #[test] + fn test_timer_mask() { + let vm_id = VMId::from(1 as usize); + let vcpu_id = VCpuId::from(0 as usize); + let mut timer = ApicTimer::new(vm_id, vcpu_id); + + // Default should be masked + assert!(timer.is_masked()); + + // Unmask timer (bit 16 = 0) + assert!(timer.write_lvt(0x50).is_ok()); // vector 0x50, not masked + assert!(!timer.is_masked()); + + // Mask timer (bit 16 = 1) + assert!(timer.write_lvt(0x10050).is_ok()); // vector 0x50, masked + assert!(timer.is_masked()); + } + + #[test] + fn test_multiple_timers() { + let vm_id = VMId::from(1 as usize); + let timer1 = ApicTimer::new(vm_id, VCpuId::from(0 as usize)); + let timer2 = ApicTimer::new(vm_id, VCpuId::from(1 as usize)); + + // Both timers should be independent + assert!(!timer1.is_started()); + assert!(!timer2.is_started()); + assert_eq!(timer1.read_icr(), timer2.read_icr()); + assert_eq!(timer1.read_dcr(), timer2.read_dcr()); + } +} From dc1e65bbff047dad115bfaa8ad3c9e10ecafe700 Mon Sep 17 00:00:00 2001 From: TQ <128586861+YanLien@users.noreply.github.com> Date: Sun, 17 Aug 2025 14:42:06 +0800 Subject: [PATCH 025/132] feat: update Cargo.toml (#7) --- Cargo.toml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7aeabb95b..1ac51882e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,10 @@ version = "0.1.0" edition = "2024" description = "x86 Virtual Local APIC" authors = ["Keyang Hu ", "Mingxian Su "] -license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPubL-2.0 OR MulanPSL2" - +license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0" # MulanPubL2 is not included in SPDX +categories = ["virtualization"] +repository = "https://github.com/arceos-hypervisor/x86_vlapic" +keywords = ["x86", "hypervisor", "arceos"] [dependencies] log = "0.4.19" @@ -16,7 +18,6 @@ bit = "0.1.1" memory_addr = "0.4" axerrno = "0.1.0" -axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git" } -axdevice_base = { git = "https://github.com/arceos-hypervisor/axdevice_crates.git" } - -axvisor_api = { git = "https://github.com/arceos-hypervisor/axvisor_api.git" } +axaddrspace = "0.1.0" +axdevice_base = "0.1.0" +axvisor_api = "0.1.0" From f8f347c3648d89f6ce6acb183e96b4e44ac2c25f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=99=AF=E5=AE=87?= <2537738252@qq.com> Date: Fri, 28 Nov 2025 20:17:07 +0800 Subject: [PATCH 026/132] add riscv64 related files --- Cargo.lock | 1818 ++++++++------------- configs/board/qemu-riscv64.toml | 11 + kernel/src/hal/arch/riscv64/cache.rs | 5 + kernel/src/hal/arch/riscv64/mod.rs | 4 + platform/riscv64-qemu-virt/Cargo.toml | 27 + platform/riscv64-qemu-virt/axconfig.toml | 84 + platform/riscv64-qemu-virt/build.rs | 21 + platform/riscv64-qemu-virt/linker.lds.S | 99 ++ platform/riscv64-qemu-virt/src/boot.rs | 94 ++ platform/riscv64-qemu-virt/src/console.rs | 51 + platform/riscv64-qemu-virt/src/init.rs | 39 + platform/riscv64-qemu-virt/src/irq.rs | 196 +++ platform/riscv64-qemu-virt/src/lib.rs | 30 + platform/riscv64-qemu-virt/src/mem.rs | 58 + platform/riscv64-qemu-virt/src/power.rs | 35 + platform/riscv64-qemu-virt/src/time.rs | 75 + scripts/ostool/qemu-riscv64.toml | 23 + 17 files changed, 1554 insertions(+), 1116 deletions(-) create mode 100644 configs/board/qemu-riscv64.toml create mode 100644 kernel/src/hal/arch/riscv64/cache.rs create mode 100644 kernel/src/hal/arch/riscv64/mod.rs create mode 100644 platform/riscv64-qemu-virt/Cargo.toml create mode 100644 platform/riscv64-qemu-virt/axconfig.toml create mode 100644 platform/riscv64-qemu-virt/build.rs create mode 100644 platform/riscv64-qemu-virt/linker.lds.S create mode 100644 platform/riscv64-qemu-virt/src/boot.rs create mode 100644 platform/riscv64-qemu-virt/src/console.rs create mode 100644 platform/riscv64-qemu-virt/src/init.rs create mode 100644 platform/riscv64-qemu-virt/src/irq.rs create mode 100644 platform/riscv64-qemu-virt/src/lib.rs create mode 100644 platform/riscv64-qemu-virt/src/mem.rs create mode 100644 platform/riscv64-qemu-virt/src/power.rs create mode 100644 platform/riscv64-qemu-virt/src/time.rs create mode 100644 scripts/ostool/qemu-riscv64.toml diff --git a/Cargo.lock b/Cargo.lock index 0b7e56a36..b688ef121 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,17 +27,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a2c929f5025d9b8a0f549b187c4d3a39671f44015ff6ccddd0b134c874b3c1a" -[[package]] -name = "abi-singleton" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbdf894742ece5360a74aa8278c42e0f305aa9f7c35d43ebc9cceca105f7e434" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "addr2line" version = "0.25.1" @@ -74,7 +63,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "version_check", - "zerocopy 0.8.31", + "zerocopy 0.8.27", ] [[package]] @@ -88,10 +77,9 @@ dependencies = [ [[package]] name = "allocator" -version = "0.1.2" -source = "git+https://github.com/arceos-org/allocator.git?tag=v0.1.2#922e72a7e4dfb7ffc4b67d242d38f8361ebd13ac" +version = "0.1.1" +source = "git+https://github.com/arceos-org/allocator.git?tag=v0.1.1#1d5b7a1b4fd8db4c9c9cea4e6012d15d42e2bf40" dependencies = [ - "axerrno 0.1.2", "bitmap-allocator", "cfg-if", "rlsf", @@ -112,15 +100,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_rgb" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a730095eb14ee842a0f1e68504b85c8d4a19b1ef2ac2a9b4debf0ed982f9b08a" -dependencies = [ - "rgb", -] - [[package]] name = "anstream" version = "0.6.21" @@ -153,22 +132,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.11" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -196,12 +175,12 @@ dependencies = [ [[package]] name = "arceos_api" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "axalloc", "axconfig", "axdriver", - "axerrno 0.1.2", + "axerrno", "axfeat", "axfs", "axhal", @@ -214,30 +193,16 @@ dependencies = [ [[package]] name = "arm-gic-driver" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a886a953642fbf21eb5928c49a05f021fae007219ae8cb2dafbf403dfeba974a" -dependencies = [ - "aarch64-cpu", - "bitflags 2.10.0", - "enum_dispatch", - "log", - "rdif-intc 0.11.0", - "tock-registers 0.9.0", -] - -[[package]] -name = "arm-gic-driver" -version = "0.15.9" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5487b0a69ebddf2f8affd1e0d32a875fc6213b3a15e3315f9d7beb34b174d457" +checksum = "f251a1a74133f802b55eaf5e340107b0024457aa9b2ac3c72074501bfa8509a5" dependencies = [ "aarch64-cpu", "bitflags 2.10.0", "enum_dispatch", "log", "paste", - "rdif-intc 0.12.1", + "rdif-intc", "tock-registers 0.9.0", ] @@ -267,8 +232,8 @@ dependencies = [ "aarch64-cpu", "axaddrspace", "axdevice_base", - "axerrno 0.1.2", - "axvcpu", + "axerrno", + "axvcpu 0.1.2", "axvisor_api", "log", "numeric-enum-macro", @@ -286,11 +251,29 @@ dependencies = [ "aarch64_sysreg", "axaddrspace", "axdevice_base", - "axerrno 0.1.2", + "axerrno", + "axvisor_api", + "bitmaps", + "log", + "memory_addr", + "spin 0.9.8", + "tock-registers 0.10.1", +] + +[[package]] +name = "arm_vgic" +version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/arm_vgic.git#81338d6dd8a9dab04b91c9c0da3d6d84e7768be6" +dependencies = [ + "aarch64-cpu", + "aarch64_sysreg", + "axaddrspace", + "axdevice_base", + "axerrno", "axvisor_api", "bitmaps", "log", - "memory_addr 0.4.1", + "memory_addr", "spin 0.9.8", "tock-registers 0.10.1", ] @@ -315,7 +298,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -336,13 +319,13 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06b129114ab36be728ef11dd6540559c30deb6332378157d22bdc0aae6803a63" dependencies = [ - "axerrno 0.1.2", + "axerrno", "bit_field", "bitflags 2.10.0", "cfg-if", "lazyinit", "log", - "memory_addr 0.4.1", + "memory_addr", "memory_set", "numeric-enum-macro", "page_table_entry", @@ -353,26 +336,14 @@ dependencies = [ [[package]] name = "axalloc" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "allocator", - "axerrno 0.1.2", + "axerrno", "cfg-if", "kspin", "log", - "memory_addr 0.4.1", - "strum 0.27.2", -] - -[[package]] -name = "axbacktrace" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf9566516f5d799b2f791a6ec5af57eec87d17624346f7c876fa006b922c99e6" -dependencies = [ - "cfg-if", - "log", - "spin 0.10.0", + "memory_addr", ] [[package]] @@ -401,22 +372,43 @@ dependencies = [ "axconfig-gen", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "axcpu" -version = "0.3.0" -source = "git+https://github.com/arceos-org/axcpu.git?tag=dev-v03#72ef3859952b7340bae261c9a50c32705e602017" +version = "0.2.2" +dependencies = [ + "aarch64-cpu", + "cfg-if", + "lazyinit", + "linkme", + "log", + "loongArch64", + "memory_addr", + "page_table_entry", + "page_table_multiarch", + "percpu", + "riscv", + "static_assertions", + "tock-registers 0.9.0", + "x86", + "x86_64", +] + +[[package]] +name = "axcpu" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e09bc1235e3da45e942b50f47812f8397ad84cb490264bf914c65ac44e6f8b1d" dependencies = [ "aarch64-cpu", - "axbacktrace", "cfg-if", "lazyinit", "linkme", "log", "loongArch64", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "page_table_multiarch", "percpu", @@ -430,16 +422,16 @@ dependencies = [ [[package]] name = "axdevice" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axdevice.git#75d9db284fd4c9ee9607c2fd84967461eeaf5b07" +source = "git+https://github.com/arceos-hypervisor/axdevice.git#60558bb25214c2030651726beddae088d8a1cd8e" dependencies = [ - "arm_vgic", + "arm_vgic 0.1.0 (git+https://github.com/arceos-hypervisor/arm_vgic.git)", "axaddrspace", "axdevice_base", - "axerrno 0.1.2", + "axerrno", "axvmconfig", "cfg-if", "log", - "memory_addr 0.4.1", + "memory_addr", "range-alloc", "spin 0.9.8", ] @@ -451,19 +443,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c43baf33ed4790ffd3365c4ca027a1e3d1c2b6058f4605b67bca04cadf48d5" dependencies = [ "axaddrspace", - "axerrno 0.1.2", + "axerrno", "axvmconfig", "cfg-if", - "memory_addr 0.4.1", + "memory_addr", "serde", ] [[package]] name = "axdisplay" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "axdriver", + "axdriver_display", "axsync", "lazyinit", "log", @@ -472,75 +465,60 @@ dependencies = [ [[package]] name = "axdriver" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ - "arm-gic-driver 0.15.9", + "arm-gic-driver", "axalloc", "axconfig", - "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", - "axdriver_block 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", + "axdriver_base", + "axdriver_block", "axdriver_display", "axdriver_net", "axdriver_pci", "axdriver_virtio", - "axerrno 0.1.2", + "axerrno", "axhal", - "axklib", - "axmm", + "axklib 0.2.0 (git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1)", "cfg-if", "crate_interface", "dma-api 0.5.2", + "lazyinit", "log", - "memory_addr 0.4.1", - "rdif-block 0.6.2", - "rdif-intc 0.12.1", - "rdrive 0.18.11", - "smallvec", + "memory_addr", + "rdif-block", + "rdif-intc", + "rdrive", "spin 0.10.0", ] -[[package]] -name = "axdriver_base" -version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" - [[package]] name = "axdriver_base" version = "0.1.2" source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" -[[package]] -name = "axdriver_block" -version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" -dependencies = [ - "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", - "log", -] - [[package]] name = "axdriver_block" version = "0.1.2" source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" dependencies = [ - "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2)", + "axdriver_base", "log", ] [[package]] name = "axdriver_display" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" dependencies = [ - "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", + "axdriver_base", ] [[package]] name = "axdriver_net" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" dependencies = [ - "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", + "axdriver_base", "log", "spin 0.9.8", ] @@ -548,7 +526,7 @@ dependencies = [ [[package]] name = "axdriver_pci" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" dependencies = [ "virtio-drivers", ] @@ -556,44 +534,32 @@ dependencies = [ [[package]] name = "axdriver_virtio" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" dependencies = [ - "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", - "axdriver_block 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", - "log", + "axdriver_base", + "axdriver_block", "virtio-drivers", ] [[package]] name = "axerrno" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a88b1fa2ce97a6ff4ce31ba9fda3065730ca4d77a1ba50dec000fc04f1fb686" -dependencies = [ - "axerrno 0.2.2", - "log", -] - -[[package]] -name = "axerrno" -version = "0.2.2" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f961d2868582a092fb1e71b90c16cc6f2cbbe7bb5fa7e4bd6fe61d882ce6bb34" +checksum = "66ccd41dd4ef364e2385901a5c2a3adea974a41eccb2529c1f24e4c8bc93d834" dependencies = [ "log", - "strum 0.27.2", ] [[package]] name = "axfeat" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "axalloc", - "axbacktrace", "axdriver", "axfs", "axhal", + "axklib-impl", "axlog", "axruntime", "axsync", @@ -603,19 +569,33 @@ dependencies = [ [[package]] name = "axfs" -version = "0.1.0" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "axdriver", - "axdriver_block 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2)", - "axerrno 0.1.2", + "axdriver_block", + "axerrno", + "axfs_devfs", "axfs_ramfs", "axfs_vfs", "axio", + "axns", + "axsync", "cap_access", + "cfg-if", "fatfs", "lazyinit", "log", - "rsext4 0.1.0 (git+https://github.com/Dirinkbottle/rsext4.git?tag=dev-251222)", +] + +[[package]] +name = "axfs_devfs" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b87ae981272ca8d5d8f106a4452c63f4b5ac36e17ee8f848ee1b250538b9f8" +dependencies = [ + "axfs_vfs", + "log", "spin 0.9.8", ] @@ -636,7 +616,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcba2006898d7879d456a9c34b9c9460cb536f5bf69d1d5d7d0e0f19f073368d" dependencies = [ - "axerrno 0.1.2", + "axerrno", "bitflags 2.10.0", "log", ] @@ -644,24 +624,25 @@ dependencies = [ [[package]] name = "axhal" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ + "aarch64-cpu", "axalloc", "axconfig", - "axcpu", - "axplat", + "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axlog", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "axplat-aarch64-qemu-virt", "axplat-loongarch64-qemu-virt", - "axplat-riscv64-qemu-virt", + "axplat-riscv64-qemu-virt 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "axplat-x86-pc", "cfg-if", - "fdt-parser", - "heapless 0.9.2", + "heapless 0.8.0", "kernel_guard", "lazyinit", "linkme", "log", - "memory_addr 0.4.1", + "memory_addr", "page_table_multiarch", "percpu", ] @@ -669,25 +650,25 @@ dependencies = [ [[package]] name = "axhvc" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axhvc.git#8b07150208803180bffc6187e6373b7ead013054" +source = "git+https://github.com/arceos-hypervisor/axhvc.git#e4ca6f919b9900f35e42b9b30863550a50432220" dependencies = [ - "axerrno 0.2.2", + "axerrno", "numeric-enum-macro", ] [[package]] name = "axio" -version = "0.2.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e797ff4cfd17460c7b8742222a2cadd72a2f4966f0057d36b5925fabf534f7" +checksum = "30aa258a37c25c5e9d3ff45ec80e728ff7c499586e3e40719daf7908f10fd5bd" dependencies = [ - "axerrno 0.1.2", + "axerrno", ] [[package]] name = "axipi" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "axconfig", "axhal", @@ -700,17 +681,39 @@ dependencies = [ [[package]] name = "axklib" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axklib.git#7c0fc0588f978f7d75bb94f4e07477776ed37887" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ - "axerrno 0.1.2", - "memory_addr 0.4.1", + "axerrno", + "memory_addr", + "trait-ffi", +] + +[[package]] +name = "axklib" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/axklib#44b5acacc4e2380a97ac9a231a024f4ee42adb06" +dependencies = [ + "axerrno", + "axplat 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memory_addr", + "trait-ffi", +] + +[[package]] +name = "axklib-impl" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +dependencies = [ + "axhal", + "axklib 0.2.0 (git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1)", + "axmm", "trait-ffi", ] [[package]] name = "axlog" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "cfg-if", "crate_interface", @@ -721,26 +724,26 @@ dependencies = [ [[package]] name = "axmm" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "axalloc", - "axconfig", - "axerrno 0.1.2", + "axerrno", "axhal", "kspin", "lazyinit", "log", - "memory_addr 0.4.1", + "memory_addr", "memory_set", ] [[package]] name = "axnet" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "axdriver", - "axerrno 0.1.2", + "axdriver_net", + "axerrno", "axhal", "axio", "axsync", @@ -752,43 +755,93 @@ dependencies = [ "spin 0.10.0", ] +[[package]] +name = "axns" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +dependencies = [ + "crate_interface", + "lazyinit", +] + [[package]] name = "axplat" -version = "0.3.0" -source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4de04c54b63bf2ca1ff202733d2516da49d7779649cdb2f9c4ecf22909e6810" +dependencies = [ + "axplat-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 2.10.0", + "const-str", + "crate_interface", + "handler_table", + "kspin", + "memory_addr", +] + +[[package]] +name = "axplat" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" dependencies = [ - "axplat-macros", + "axplat-macros 0.1.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "bitflags 2.10.0", "const-str", "crate_interface", "handler_table", "kspin", - "memory_addr 0.4.1", + "memory_addr", +] + +[[package]] +name = "axplat-aarch64-dyn" +version = "0.3.0" +dependencies = [ + "aarch64-cpu", + "aarch64-cpu-ext", + "any-uart", + "arm-gic-driver", + "axconfig-macros", + "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "fdt-parser", + "heapless 0.8.0", + "lazyinit", + "log", + "memory_addr", + "page_table_entry", + "paste", "percpu", + "rdif-intc", + "rdrive", + "serde", + "somehal", + "spin 0.10.0", + "toml 0.8.23", ] [[package]] name = "axplat-aarch64-dyn" -version = "0.4.0" -source = "git+https://github.com/arceos-hypervisor/axplat-aarch64-dyn.git?tag=v0.4.0#05d5acd43d925807496255a9b9e1aa2d272bb591" +version = "0.3.0" +source = "git+https://github.com/arceos-hypervisor/axplat-aarch64-dyn?tag=v0.3.3#de5acd47da8540f46c81e1eef69af65965404dbd" dependencies = [ "aarch64-cpu", "aarch64-cpu-ext", "any-uart", - "arm-gic-driver 0.15.9", + "arm-gic-driver", "axconfig-macros", - "axcpu", - "axplat", + "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "fdt-parser", "heapless 0.8.0", "lazyinit", "log", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "paste", "percpu", - "rdif-intc 0.12.1", - "rdrive 0.18.11", + "rdif-intc", + "rdrive", "serde", "somehal", "spin 0.10.0", @@ -797,15 +850,15 @@ dependencies = [ [[package]] name = "axplat-aarch64-peripherals" -version = "0.3.0" -source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" dependencies = [ "aarch64-cpu", - "arm-gic-driver 0.15.9", + "arm-gic-driver", "arm_pl011", "arm_pl031", - "axcpu", - "axplat", + "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "int_ratio", "kspin", "lazyinit", @@ -816,12 +869,12 @@ dependencies = [ [[package]] name = "axplat-aarch64-qemu-virt" -version = "0.3.0" -source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" dependencies = [ "axconfig-macros", - "axcpu", - "axplat", + "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "axplat-aarch64-peripherals", "log", "page_table_entry", @@ -829,55 +882,75 @@ dependencies = [ [[package]] name = "axplat-loongarch64-qemu-virt" -version = "0.3.0" -source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" dependencies = [ "axconfig-macros", - "axcpu", - "axplat", + "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "kspin", "lazyinit", "log", "loongArch64", + "ns16550a", "page_table_entry", - "uart_16550", ] [[package]] name = "axplat-macros" version = "0.1.0" -source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90dfaee06a112fe4f810c60af1a86bc080af2172185b491cacc307b84dff748" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", +] + +[[package]] +name = "axplat-macros" +version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", ] [[package]] name = "axplat-riscv64-qemu-virt" -version = "0.3.0" -source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" +version = "0.2.0" dependencies = [ "axconfig-macros", - "axcpu", - "axplat", - "kspin", - "lazyinit", + "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "log", + "riscv", + "riscv_goldfish", + "sbi-rt", +] + +[[package]] +name = "axplat-riscv64-qemu-virt" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" +dependencies = [ + "axconfig-macros", + "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "log", "riscv", - "riscv_plic", "sbi-rt", - "uart_16550", ] [[package]] name = "axplat-x86-pc" -version = "0.3.0" -source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" dependencies = [ "axconfig-macros", - "axcpu", - "axplat", + "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "bitflags 2.10.0", "heapless 0.9.2", "int_ratio", @@ -898,8 +971,8 @@ name = "axplat-x86-qemu-q35" version = "0.1.0" dependencies = [ "axconfig-macros", - "axcpu", - "axplat", + "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "bitflags 2.10.0", "heapless 0.9.2", "int_ratio", @@ -916,17 +989,6 @@ dependencies = [ "x86_rtc", ] -[[package]] -name = "axpoll" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06a0cf7cffe0e87c338f41d59bd0a05fa75cf9c4799b4107fa0fc6cf27d8d23" -dependencies = [ - "bitflags 2.10.0", - "linux-raw-sys 0.11.0", - "spin 0.10.0", -] - [[package]] name = "axruntime" version = "0.1.0" @@ -935,15 +997,15 @@ dependencies = [ "axconfig", "axdisplay", "axdriver", - "axerrno 0.1.2", + "axerrno", "axfs", "axhal", "axipi", "axlog", "axmm", "axnet", - "axplat", - "axplat-aarch64-dyn", + "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axplat-aarch64-dyn 0.3.0 (git+https://github.com/arceos-hypervisor/axplat-aarch64-dyn?tag=v0.3.3)", "axplat-x86-qemu-q35", "axtask", "cfg-if", @@ -957,9 +1019,9 @@ dependencies = [ [[package]] name = "axsched" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad6b7b0b8d9ad1d52a834d8b7721114413da8cf3430af928b1c8651f911287a" +checksum = "44de469da35f912194e4104cc2f51bff63d6c184b65b6ebf8e90e2cd162b7f3c" dependencies = [ "linked_list_r4l", ] @@ -967,10 +1029,10 @@ dependencies = [ [[package]] name = "axstd" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "arceos_api", - "axerrno 0.1.2", + "axerrno", "axfeat", "axio", "kspin", @@ -980,10 +1042,9 @@ dependencies = [ [[package]] name = "axsync" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "axtask", - "event-listener", "kspin", "lock_api", ] @@ -991,41 +1052,37 @@ dependencies = [ [[package]] name = "axtask" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" dependencies = [ "axconfig", - "axerrno 0.1.2", "axhal", - "axpoll", "axsched", "cfg-if", "cpumask", "crate_interface", - "event-listener", - "extern-trait", - "futures-util", "kernel_guard", "kspin", "lazyinit", "log", - "memory_addr 0.4.1", + "memory_addr", "percpu", + "timer_list", ] [[package]] name = "axum" -version = "0.8.7" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" dependencies = [ "axum-core", "bytes", "form_urlencoded", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.8.0", "hyper-util", "itoa", "matchit", @@ -1053,7 +1110,7 @@ checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "mime", @@ -1064,15 +1121,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "axvcpu" +version = "0.1.1" +dependencies = [ + "axaddrspace", + "axerrno", + "axvisor_api", + "memory_addr", + "percpu", +] + [[package]] name = "axvcpu" version = "0.1.2" source = "git+https://github.com/arceos-hypervisor/axvcpu.git?branch=next#343ec3ccf99a86fb9c67a7b0372e9b7a745f0640" dependencies = [ "axaddrspace", - "axerrno 0.1.2", + "axerrno", "axvisor_api", - "memory_addr 0.4.1", + "memory_addr", "percpu", ] @@ -1082,43 +1150,42 @@ version = "0.0.0" dependencies = [ "aarch64-cpu-ext", "anyhow", - "arm-gic-driver 0.15.9", + "arm-gic-driver", "axaddrspace", "axconfig", "axdevice", "axdevice_base", - "axerrno 0.2.2", + "axerrno", "axhvc", "axruntime", "axstd", - "axvcpu", + "axvcpu 0.1.2", "axvisor_api", - "axvm", + "axvm 0.1.0 (git+https://github.com/arceos-hypervisor/axvm.git?branch=next)", "bitflags 2.10.0", "byte-unit", "cfg-if", "cpumask", "crate_interface", "driver", - "extern-trait", "fdt-parser", "kernel_guard", "kspin", "lazy_static", "lazyinit", "log", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "page_table_multiarch", "percpu", "prettyplease", "quote", - "rdif-intc 0.12.1", - "rdrive 0.18.11", + "rdif-intc", + "rdrive", "spin 0.9.8", - "syn 2.0.111", + "syn 2.0.110", "timer_list", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.8", "vm-fdt 0.3.0 (git+https://github.com/bullhh/vm-fdt.git)", ] @@ -1131,7 +1198,7 @@ dependencies = [ "axaddrspace", "axvisor_api_proc", "crate_interface", - "memory_addr 0.4.1", + "memory_addr", ] [[package]] @@ -1143,30 +1210,54 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "axvm" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axvm.git?branch=next#e161233e58c0ef0c6ec115ffa5b0d17dadd298be" dependencies = [ "arm_vcpu", - "arm_vgic", + "arm_vgic 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "axaddrspace", "axdevice", "axdevice_base", - "axerrno 0.2.2", - "axvcpu", + "axerrno", + "axvcpu 0.1.2", "axvmconfig", "cfg-if", "cpumask", "log", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "page_table_multiarch", "percpu", - "riscv_vcpu", + "riscv_vcpu 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "spin 0.9.8", + "x86_vcpu", +] + +[[package]] +name = "axvm" +version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/axvm.git?branch=next#0393f27dea948433e53285a400e356cdfd4c4fa3" +dependencies = [ + "arm_vcpu", + "arm_vgic 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "axaddrspace", + "axdevice", + "axdevice_base", + "axerrno", + "axvcpu 0.1.2", + "axvmconfig", + "cfg-if", + "cpumask", + "log", + "memory_addr", + "page_table_entry", + "page_table_multiarch", + "percpu", + "riscv_vcpu 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.9.8", "x86_vcpu", ] @@ -1176,7 +1267,7 @@ name = "axvmconfig" version = "0.1.0" source = "git+https://github.com/arceos-hypervisor/axvmconfig.git?branch=next#5a8b64a47510b17da71e54cabbdf8c999ba2e2c9" dependencies = [ - "axerrno 0.1.2", + "axerrno", "clap", "enumerable", "env_logger", @@ -1184,7 +1275,7 @@ dependencies = [ "schemars", "serde", "serde_repr", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.8", ] [[package]] @@ -1199,7 +1290,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -1208,19 +1299,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" -[[package]] -name = "bare-test" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7dfcf95987c500af4665d8a18adbc5e7d3177a2537964f48002b88e28fb055e" -dependencies = [ - "bare-test-macros", - "log", - "sparreal-kernel", - "sparreal-macros 0.9.3", - "sparreal-rt", -] - [[package]] name = "bare-test-macros" version = "0.2.0" @@ -1229,7 +1307,7 @@ checksum = "e585a01076fee271c5aabcf36212acb349fb3e638561d842fffa8ca013f4fdd8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1251,37 +1329,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "bindeps-simple" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759b427caeff86cb964340e5fee96d7b26a58853941d3a8e051f46be7b1fada3" -dependencies = [ - "anyhow", - "cargo_metadata 0.20.0", - "flate2", - "rand 0.9.2", - "reqwest 0.12.26", - "tar", -] - -[[package]] -name = "bindgen" -version = "0.71.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" -dependencies = [ - "bitflags 2.10.0", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.111", +name = "bindeps-simple" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759b427caeff86cb964340e5fee96d7b26a58853941d3a8e051f46be7b1fada3" +dependencies = [ + "anyhow", + "cargo_metadata 0.20.0", + "flate2", + "rand 0.9.2", + "reqwest 0.12.24", + "tar", ] [[package]] @@ -1346,9 +1404,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" dependencies = [ "borsh-derive", "cfg_aliases", @@ -1356,40 +1414,30 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.6.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", -] - -[[package]] -name = "buddy_system_allocator" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0108968a3a2dab95b089c0fc3f1afa7759aa5ebe6f1d86d206d6f7ba726eb" -dependencies = [ - "spin 0.9.8", + "syn 2.0.110", ] [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-unit" -version = "5.2.0" +version = "5.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6d47a4e2961fb8721bcfc54feae6455f2f64e7054f9bc67e875f0e77f4c58d" +checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174" dependencies = [ "rust_decimal", - "schemars", "serde", "utf8-width", ] @@ -1430,15 +1478,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "camino" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" dependencies = [ "serde_core", ] @@ -1463,12 +1511,11 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082" +checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4" dependencies = [ "serde", - "serde_core", ] [[package]] @@ -1509,7 +1556,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" dependencies = [ "camino", - "cargo-platform 0.3.2", + "cargo-platform 0.3.1", "semver", "serde", "serde_json", @@ -1533,23 +1580,14 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.49" +version = "1.2.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" dependencies = [ "find-msvc-tools", "shlex", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -1572,25 +1610,14 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", + "windows-link 0.2.1", ] [[package]] name = "clap" -version = "4.5.53" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -1598,9 +1625,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -1617,7 +1644,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1655,20 +1682,11 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "console" -version = "0.16.2" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" +checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4" dependencies = [ "encode_unicode", "libc", @@ -1689,26 +1707,20 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "451d0640545a0553814b4c646eb549343561618838e9b42495f466131fe3ad49" -[[package]] -name = "const_fn" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e" - [[package]] name = "convert_case" -version = "0.8.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" dependencies = [ "unicode-segmentation", ] [[package]] name = "convert_case" -version = "0.10.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ "unicode-segmentation", ] @@ -1765,14 +1777,14 @@ checksum = "70272a03a2cef15589bac05d3d15c023752f5f8f2da8be977d983a9d9e6250fb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "crc" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -1866,16 +1878,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "cstr_core" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd98742e4fdca832d40cab219dc2e3048de17d873248f83f17df47c1bea70956" -dependencies = [ - "cty", - "memchr", -] - [[package]] name = "ctor_bare" version = "0.2.1" @@ -1893,15 +1895,9 @@ checksum = "9a49d5cd78b1c748184d41407b14a58af8403c13328ff2b9f49b0a418c24e3ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - [[package]] name = "cursive" version = "0.21.1" @@ -1984,7 +1980,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1997,7 +1993,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2008,7 +2004,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2019,7 +2015,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2051,7 +2047,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2074,24 +2070,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.1.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ - "convert_case 0.10.0", + "convert_case 0.7.1", "proc-macro2", "quote", - "rustc_version", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2118,7 +2113,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2161,12 +2156,12 @@ dependencies = [ name = "driver" version = "0.1.0" dependencies = [ - "axklib", + "axklib 0.2.0 (git+https://github.com/arceos-hypervisor/axklib)", "log", - "phytium-mci 0.1.0 (git+https://github.com/YanQD/phytium-mci.git?rev=99c9ee5)", - "rdif-block 0.6.2", + "phytium-mci", + "rdif-block", "rdif-clk", - "rdrive 0.18.11", + "rdrive", "rk3568_clk", "rk3588-clk", "rockchip-pm", @@ -2234,7 +2229,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2246,7 +2241,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2267,7 +2262,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2278,7 +2273,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2299,7 +2294,7 @@ dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2352,27 +2347,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "pin-project-lite", -] - -[[package]] -name = "extern-trait" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba8f5038f5845165d06fe1453fe4130ad546d3314818bbda57e208e7b0cffe08" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -2382,7 +2356,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fatfs" version = "0.4.0" -source = "git+https://github.com/Josen-B/rust-fatfs.git?rev=41122ef#41122eff7d244cb030c9cfb9b5e14fc50b70dd24" +source = "git+https://github.com/rafalh/rust-fatfs?rev=4eccb50#4eccb50d011146fbed20e133d33b22f3c27292e7" dependencies = [ "bitflags 2.10.0", "log", @@ -2408,9 +2382,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "fitimage" @@ -2540,7 +2514,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2616,12 +2590,6 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - [[package]] name = "h2" version = "0.3.27" @@ -2652,7 +2620,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.4.0", + "http 1.3.1", "indexmap", "slab", "tokio", @@ -2707,9 +2675,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heapless" @@ -2756,11 +2724,12 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", + "fnv", "itoa", ] @@ -2782,7 +2751,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http 1.3.1", ] [[package]] @@ -2793,7 +2762,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] @@ -2842,16 +2811,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "1744436df46f0bde35af3eda22aeaba453aada65d8f1c171cd8a5f59030bd69f" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", "h2 0.4.12", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "httparse", "httpdate", @@ -2869,8 +2838,8 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.4.0", - "hyper 1.8.1", + "http 1.3.1", + "hyper 1.8.0", "hyper-util", "rustls", "rustls-pki-types", @@ -2901,7 +2870,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.8.1", + "hyper 1.8.0", "hyper-util", "native-tls", "tokio", @@ -2911,18 +2880,18 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", - "hyper 1.8.1", + "hyper 1.8.0", "ipnet", "libc", "percent-encoding", @@ -3007,9 +2976,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ "icu_collections", "icu_locale_core", @@ -3021,9 +2990,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" @@ -3069,12 +3038,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.16.0", ] [[package]] @@ -3101,15 +3070,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.10" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c" +checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" dependencies = [ "darling 0.20.11", "indoc", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3150,15 +3119,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -3195,7 +3155,7 @@ checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3216,33 +3176,21 @@ dependencies = [ "serde_json", "thiserror 2.0.17", "tokio", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.8", "tower", "tower-http", ] [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", ] -[[package]] -name = "kasm-aarch64" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e484b7a4686e2750fae1b4c4b750e14f1522eb303288d9d2723a955c2a41b7d7" -dependencies = [ - "darling 0.20.11", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "kasm-aarch64" version = "0.2.0" @@ -3252,7 +3200,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3264,14 +3212,14 @@ dependencies = [ "bitflags 2.10.0", "prettyplease", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "kernel_guard" -version = "0.1.3" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d10c55bedf6789bc3748e0d8756ee639df1ae25144fd3525ed311044bd9a739f" +checksum = "307e6be468f3d6b6d895e191f63c11602e4e76575ecca68325d8c8dbebe2870e" dependencies = [ "cfg-if", "crate_interface", @@ -3333,29 +3281,19 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.178" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" - -[[package]] -name = "libloading" -version = "0.8.9" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libredox" -version = "0.1.11" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ "bitflags 2.10.0", "libc", - "redox_syscall 0.6.0", + "redox_syscall", ] [[package]] @@ -3391,9 +3329,9 @@ dependencies = [ [[package]] name = "linked_list_r4l" -version = "0.3.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1730c4ce817dc3edb092739ca5c109fe551018e5ea5a8361a8ddaa13d79ac8ed" +checksum = "54c6e48d7df84e6414be8e53976ead35ba4d47a4ba561eaad5d1e2b1447b4c0a" [[package]] name = "linkme" @@ -3412,7 +3350,7 @@ checksum = "e5cec0ec4228b4853bb129c84dbf093a27e6c7a20526da046defc334a1b017f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3450,9 +3388,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "loongArch64" @@ -3479,15 +3417,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "lwext4_rust" -version = "0.2.0" -dependencies = [ - "bindgen", - "log", - "printf-compat", -] - [[package]] name = "lzma-rs" version = "0.3.0" @@ -3548,24 +3477,17 @@ dependencies = [ [[package]] name = "memory_addr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5438b8df0f13e16e1f46140de247695a95952a5a4479e47197a8711bf1063373" - -[[package]] -name = "memory_addr" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f0625c50adb5f6aaf47f05cae3c4dbc13a74c659241b06c4576f3d7e1da940" +checksum = "3d4054cba279515fa87761b101d857333ce06391dbe8f18a11347204a7111416" [[package]] name = "memory_set" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50a49ecd4114cf87f7e442ec5dd03bd590e7094541f987057310dbb32a6341ad" +checksum = "e7d47cbc25a4d00427f9070fd768eaf907f19c903fb72b547b19db2d56b9408e" dependencies = [ - "axerrno 0.1.2", - "memory_addr 0.4.1", + "memory_addr", ] [[package]] @@ -3584,12 +3506,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -3602,9 +3518,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", "log", @@ -3646,9 +3562,9 @@ checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" [[package]] name = "network-interface" -version = "2.0.4" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e79101e6efcffacab279462884a7eebf65ea5f4ac2cc727b60c715a9aa04722" +checksum = "07709a6d4eba90ab10ec170a0530b3aafc81cb8a2d380e4423ae41fc55fe5745" dependencies = [ "cc", "libc", @@ -3667,20 +3583,16 @@ dependencies = [ "libc", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "nop" version = "0.1.0" +[[package]] +name = "ns16550a" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3cd8abe9e54bce27659507b94f355c9334378ab15da332b6986b3583ebf7228" + [[package]] name = "num" version = "0.4.3" @@ -3815,7 +3727,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3847,9 +3759,9 @@ dependencies = [ [[package]] name = "ostool" -version = "0.8.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9faa3c583310e624402b2c8ee736594bc1fc99c225a06e40a6e7073fba98f71" +checksum = "7b1f90223d51db91747d032ef49378e725a6e42a3d53da62c246abc73fe35cb7" dependencies = [ "anyhow", "byte-unit", @@ -3868,7 +3780,7 @@ dependencies = [ "object", "ratatui", "regex", - "reqwest 0.12.26", + "reqwest 0.12.24", "schemars", "serde", "serde_json", @@ -3877,33 +3789,11 @@ dependencies = [ "tar", "tftpd", "tokio", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.8", "uboot-shell", "ureq", ] -[[package]] -name = "page-table-arm" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce2c42338660c47a35e7b2940dcccbe6612a4a0aa0485ecdf4e23aa8a2a1158" -dependencies = [ - "aarch64-cpu", - "bitflags 2.10.0", - "log", -] - -[[package]] -name = "page-table-generic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827063f64bbad7b7655092b0f98824ffbe85e89646388eb4dad1e3d797056a2f" -dependencies = [ - "bitflags 2.10.0", - "log", - "thiserror 2.0.17", -] - [[package]] name = "page-table-generic" version = "0.6.1" @@ -3918,24 +3808,25 @@ dependencies = [ [[package]] name = "page_table_entry" -version = "0.5.7" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda9891ec368fda90e4b2cc36592b4881073e25a339fe7e3eddd811f0cf6bf18" +checksum = "ba811ef8ca8fb33d776e128624cb4fe25c9804cab96f83b822d4322431e6dd5a" dependencies = [ "aarch64-cpu", "bitflags 2.10.0", - "memory_addr 0.4.1", + "memory_addr", "x86_64", ] [[package]] name = "page_table_multiarch" -version = "0.5.7" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa11a21844255e14aa6688ef0eafb058d7be19338633024fb59417f1bfb07f8" +checksum = "98cb76e21ce462270afd83b331599d5b83f876c2a98c0a70382b20d73e1da6be" dependencies = [ + "bitmaps", "log", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "riscv", "x86", @@ -3959,9 +3850,9 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -3970,12 +3861,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pasts" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efcd36303871fb977a47dabc9af736c75c492bb32a92fa26262b2741531e97ce" - [[package]] name = "pci_types" version = "0.10.0" @@ -3986,19 +3871,6 @@ dependencies = [ "bitflags 2.10.0", ] -[[package]] -name = "pcie" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e45cda4b8ef9f2a8dae7cf5b58c11b41d35fbe62a4d9693fd2d143225fbf44" -dependencies = [ - "bit_field", - "bitflags 2.10.0", - "log", - "pci_types", - "sparreal-macros 0.0.5", -] - [[package]] name = "pcie" version = "0.4.5" @@ -4040,27 +3912,7 @@ checksum = "8a9f4cc54a2e471ff72f1499461ba381ad4eae9cbd60d29c258545b995e406e0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", -] - -[[package]] -name = "phytium-mci" -version = "0.1.0" -dependencies = [ - "bare-test", - "bare-test-macros", - "bitflags 2.10.0", - "byte-unit", - "bytemuck", - "dma-api 0.2.2", - "lazy_static", - "log", - "nb", - "pcie 0.2.7", - "rlsf", - "spin 0.10.0", - "spin_on", - "tock-registers 0.9.0", + "syn 2.0.110", ] [[package]] @@ -4081,66 +3933,15 @@ dependencies = [ ] [[package]] -name = "pie-boot" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524d0fc5cd834d2179d8a88cde327b0e168dd6aedf30ce1832467a924a35594f" -dependencies = [ - "aarch64-cpu", - "aarch64-cpu-ext", - "bindeps-simple", - "fdt-parser", - "heapless 0.8.0", - "kasm-aarch64 0.1.3", - "kdef-pgtable", - "pie-boot-if 0.6.0", - "pie-boot-loader-aarch64 0.1.27", - "pie-boot-macros", -] - -[[package]] -name = "pie-boot-if" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00af8d4efee0eee91ead95b34c50c067163dc4c90b874b1cc4caa671eb1d85b" -dependencies = [ - "heapless 0.8.0", -] - -[[package]] -name = "pie-boot-if" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d979b0d1208dd8a57c5adb7d3c4e07bf15cbea3840123e864f6bfcb655c5e05" -dependencies = [ - "heapless 0.8.0", -] - -[[package]] -name = "pie-boot-loader-aarch64" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ee18ed1de7f55f318f01803bf7dc353ef492db71a2005fa3af36f116f25d28" -dependencies = [ - "aarch64-cpu", - "aarch64-cpu-ext", - "any-uart", - "bitflags 2.10.0", - "fdt-parser", - "kasm-aarch64 0.1.3", - "kdef-pgtable", - "log", - "num-align", - "page-table-generic 0.6.1", - "pie-boot-if 0.6.0", - "prettyplease", - "quote", - "spin 0.10.0", - "syn 2.0.111", - "thiserror 2.0.17", -] - -[[package]] +name = "pie-boot-if" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d979b0d1208dd8a57c5adb7d3c4e07bf15cbea3840123e864f6bfcb655c5e05" +dependencies = [ + "heapless 0.8.0", +] + +[[package]] name = "pie-boot-loader-aarch64" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4151,16 +3952,16 @@ dependencies = [ "any-uart", "bitflags 2.10.0", "fdt-parser", - "kasm-aarch64 0.2.0", + "kasm-aarch64", "kdef-pgtable", "log", "num-align", - "page-table-generic 0.6.1", - "pie-boot-if 0.8.0", + "page-table-generic", + "pie-boot-if", "prettyplease", "quote", "spin 0.10.0", - "syn 2.0.111", + "syn 2.0.110", "thiserror 2.0.17", ] @@ -4173,7 +3974,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -4230,7 +4031,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.31", + "zerocopy 0.8.27", ] [[package]] @@ -4240,19 +4041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.111", -] - -[[package]] -name = "printf-compat" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b002af28ffe3d3d67202ae717810a28125a494d5396debc43de01ee136ac404" -dependencies = [ - "bitflags 1.3.2", - "cstr_core", - "cty", - "itertools 0.9.0", + "syn 2.0.110", ] [[package]] @@ -4261,7 +4050,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit 0.23.7", ] [[package]] @@ -4283,7 +4072,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -4467,10 +4256,10 @@ dependencies = [ "crossterm 0.28.1", "indoc", "instability", - "itertools 0.13.0", + "itertools", "lru", "paste", - "strum 0.26.3", + "strum", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.0", @@ -4494,18 +4283,6 @@ dependencies = [ "bitflags 2.10.0", ] -[[package]] -name = "rdif-base" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6953f438bbbdf58e55513c31e70fa0f85daba2927e8a59130a04608141bb552" -dependencies = [ - "as-any", - "async-trait", - "rdif-def", - "thiserror 2.0.17", -] - [[package]] name = "rdif-base" version = "0.7.0" @@ -4519,16 +4296,6 @@ dependencies = [ "thiserror 2.0.17", ] -[[package]] -name = "rdif-block" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b8e19dc3cb6cd7241085a9560a91d4346edbc525bcbfc3c86e5eeb11559c19" -dependencies = [ - "cfg-if", - "rdif-base 0.6.0", -] - [[package]] name = "rdif-block" version = "0.6.2" @@ -4538,7 +4305,7 @@ dependencies = [ "cfg-if", "dma-api 0.5.2", "futures", - "rdif-base 0.7.0", + "rdif-base", "spin_on", "thiserror 2.0.17", ] @@ -4549,7 +4316,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9af012204e25d45735aa141b475c411b833b4f4bc580924905745d4afbbf606" dependencies = [ - "rdif-base 0.7.0", + "rdif-base", ] [[package]] @@ -4561,17 +4328,6 @@ dependencies = [ "thiserror 2.0.17", ] -[[package]] -name = "rdif-intc" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e7622f78dc9b40958500119553f3c15b9bb9829002d87d0f4b114ebe302a40" -dependencies = [ - "cfg-if", - "rdif-base 0.6.0", - "thiserror 2.0.17", -] - [[package]] name = "rdif-intc" version = "0.12.1" @@ -4579,7 +4335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "170ec813e6cf4d1e5fa53fa8fed0fadc7aaab96683d4f1d44c602a6109931eb4" dependencies = [ "cfg-if", - "rdif-base 0.7.0", + "rdif-base", "thiserror 2.0.17", ] @@ -4590,59 +4346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60c6e8dea6d432b2c03bc3f4238dc59a276aacac6f688a937351e7a313918738" dependencies = [ "pci_types", - "rdif-base 0.7.0", - "thiserror 2.0.17", -] - -[[package]] -name = "rdif-power" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b6eefca0d1b44a5bef1e934d8ab2c8e00e19dd3d9e071855c0933637ee17a0" -dependencies = [ - "rdif-base 0.7.0", -] - -[[package]] -name = "rdif-serial" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673854a0c554806da63f0836c95b34b08956a143ff15e327644cbd07a8e0df31" -dependencies = [ - "futures", - "rdif-base 0.7.0", - "spin_on", - "thiserror 2.0.17", -] - -[[package]] -name = "rdif-systick" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11da4f362ab6cdcdee9d8e795faabd0f15e04167cb17939fd3aca19c2ef3421" -dependencies = [ - "rdif-base 0.7.0", -] - -[[package]] -name = "rdrive" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ce47e5a3d10943dfdb8c31dcca7a91c353ea43f4ac2eb72c92462e83a2baa4" -dependencies = [ - "enum_dispatch", - "fdt-parser", - "log", - "paste", - "rdif-base 0.6.0", - "rdif-block 0.5.0", - "rdif-clk", - "rdif-intc 0.11.0", - "rdif-power", - "rdif-serial", - "rdif-systick", - "rdrive-macros", - "spin 0.10.0", + "rdif-base", "thiserror 2.0.17", ] @@ -4655,25 +4359,14 @@ dependencies = [ "fdt-parser", "log", "paste", - "pcie 0.4.5", - "rdif-base 0.7.0", + "pcie", + "rdif-base", "rdif-pcie", "rdrive-macros", "spin 0.10.0", "thiserror 2.0.17", ] -[[package]] -name = "rdrive-macro-utils" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977fcecf5b5fe8d7189d497d8754d27a4ffaedeac904cce1b7ea7bdfb5280934" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "rdrive-macros" version = "0.4.1" @@ -4682,7 +4375,7 @@ checksum = "eab3105c9af32e901a2adc7d920b39ff8b6ee0f6f0b7dfdeaf18f306ec12606f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -4694,15 +4387,6 @@ dependencies = [ "bitflags 2.10.0", ] -[[package]] -name = "redox_syscall" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" -dependencies = [ - "bitflags 2.10.0", -] - [[package]] name = "ref-cast" version = "1.0.25" @@ -4720,7 +4404,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -4819,9 +4503,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.26" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64 0.22.1", "bytes", @@ -4830,10 +4514,10 @@ dependencies = [ "futures-core", "futures-util", "h2 0.4.12", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.8.1", + "hyper 1.8.0", "hyper-rustls", "hyper-tls 0.6.0", "hyper-util", @@ -4863,15 +4547,6 @@ dependencies = [ "webpki-roots", ] -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" -dependencies = [ - "bytemuck", -] - [[package]] name = "ring" version = "0.17.14" @@ -4926,7 +4601,7 @@ checksum = "e8c4aa1ea1af6dcc83a61be12e8189f9b293c3ba5a487778a4cd89fb060fdbbc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -4936,12 +4611,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" [[package]] -name = "riscv_plic" -version = "0.2.0" +name = "riscv_goldfish" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e701d1c6ea06c35a19cb80d213fab87d264798f9bac0aed2730c0e86d297394a" +checksum = "07aac72f95e774476db82916d79f2d303191310393830573c1ab5c821b21660a" + +[[package]] +name = "riscv_vcpu" +version = "0.1.2" dependencies = [ - "tock-registers 0.10.1", + "axaddrspace", + "axerrno", + "axvcpu 0.1.2", + "axvisor_api", + "bit_field", + "bitflags 2.10.0", + "cfg-if", + "crate_interface", + "log", + "memoffset", + "memory_addr", + "page_table_entry", + "riscv", + "riscv-decode", + "riscv-h", + "rustsbi", + "sbi-rt", + "sbi-spec", + "tock-registers 0.9.0", ] [[package]] @@ -4951,8 +4648,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13f38f28fe6c02bb3ced43087c9667b23d18adf729becdc5adf1253f7df83904" dependencies = [ "axaddrspace", - "axerrno 0.1.2", - "axvcpu", + "axerrno", + "axvcpu 0.1.2", "axvisor_api", "bit_field", "bitflags 2.10.0", @@ -4960,7 +4657,7 @@ dependencies = [ "crate_interface", "log", "memoffset", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "riscv", "riscv-decode", @@ -4985,11 +4682,12 @@ dependencies = [ [[package]] name = "rk3588-clk" -version = "0.1.1" -source = "git+https://github.com/drivercraft/rk3588-clk#1476c44483e7800ff3c7cb53cdfaf01e72e320d4" +version = "0.1.0" +source = "git+https://github.com/drivercraft/rk3588-clk#2434106bb1838bc2af7c3f4de3ab6bbc15fdeb81" dependencies = [ "bare-test-macros", "log", + "mbarrier", "tock-registers 0.10.1", ] @@ -5036,36 +4734,17 @@ dependencies = [ [[package]] name = "rockchip-pm" -version = "0.4.0" -source = "git+https://github.com/drivercraft/rockchip-pm.git#5bb4dfbfcea41826e13115efb4023d670ef6b034" +version = "0.2.0" +source = "git+https://github.com/drivercraft/rockchip-pm.git#cb2afdf9f3cb5fd05f8e003e65de38e50d480cd5" dependencies = [ "bare-test-macros", "dma-api 0.5.2", "log", "mbarrier", - "rdif-base 0.7.0", + "rdif-base", "tock-registers 0.10.1", ] -[[package]] -name = "rsext4" -version = "0.1.0" -dependencies = [ - "bitflags 2.10.0", - "lazy_static", - "log", -] - -[[package]] -name = "rsext4" -version = "0.1.0" -source = "git+https://github.com/Dirinkbottle/rsext4.git?tag=dev-251222#376e253cc6b8767bc14ca5054fbc1ae49f7a8c8d" -dependencies = [ - "bitflags 2.10.0", - "lazy_static", - "log", -] - [[package]] name = "rust_decimal" version = "1.39.0" @@ -5094,15 +4773,6 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "0.38.44" @@ -5155,9 +4825,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "web-time", "zeroize", @@ -5193,7 +4863,7 @@ checksum = "a71347da9582cc6b6f3652c7d2c06516c9555690b3738ecdff7e84297f4e17fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5263,7 +4933,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5287,7 +4957,7 @@ dependencies = [ "kspin", "log", "paste", - "smccc 0.2.2", + "smccc", "spin 0.10.0", ] @@ -5379,7 +5049,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5390,7 +5060,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5425,7 +5095,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5439,9 +5109,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ "serde_core", ] @@ -5529,18 +5199,18 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.7" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "simdutf8" @@ -5560,12 +5230,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "smccc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617d17f088ec733e5a6b86da6ce4cce1414e6e856d6061c16dda51cceae6f68c" - [[package]] name = "smccc" version = "0.2.2" @@ -5611,9 +5275,9 @@ dependencies = [ [[package]] name = "somehal" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b5f763b9ab0ce9efd2d8eba9e5b457f93f6426ede68435fe9567cf7681f29d" +checksum = "5a9b180e1abae1127bd99ecee566c3906d8c0f854cb1c442b945afe911e6eb71" dependencies = [ "aarch64-cpu", "aarch64-cpu-ext", @@ -5622,100 +5286,22 @@ dependencies = [ "fdt-parser", "futures", "heapless 0.8.0", - "kasm-aarch64 0.2.0", + "kasm-aarch64", "kdef-pgtable", "log", "num-align", - "page-table-generic 0.6.1", - "pie-boot-if 0.8.0", - "pie-boot-loader-aarch64 0.3.3", + "page-table-generic", + "pie-boot-if", + "pie-boot-loader-aarch64", "pie-boot-macros", "release-dep", "serde", - "smccc 0.2.2", + "smccc", "spin 0.10.0", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.8", "url", ] -[[package]] -name = "sparreal-kernel" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b182a508314f1560ce8f94090f8c0990640bd849fab49e307ccafe2b51e67da9" -dependencies = [ - "ansi_rgb", - "anyhow", - "arrayvec", - "buddy_system_allocator", - "byte-unit", - "dma-api 0.3.1", - "fdt-parser", - "lazy_static", - "lock_api", - "log", - "memory_addr 0.3.2", - "page-table-generic 0.5.3", - "pasts", - "rdrive 0.15.3", - "rgb", - "sparreal-macros 0.9.3", - "spin 0.9.8", - "thiserror 2.0.17", -] - -[[package]] -name = "sparreal-macros" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f686073b67b2427c9243bddc10ea0a6a5300ab5354a8ee884d9126854b0abab7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "sparreal-macros" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c39b68430130f1c7587eb41f512dd1f6e48bc22a3e1dc11a69dc1b8294cdc90" -dependencies = [ - "abi-singleton", - "darling 0.20.11", - "proc-macro2", - "quote", - "rdrive-macro-utils", - "syn 2.0.111", -] - -[[package]] -name = "sparreal-rt" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9da6adb0285c99f180e9746ebbadcf4886b1b009904f6f9ab5be155ae1325a7" -dependencies = [ - "aarch64-cpu", - "aarch64-cpu-ext", - "ansi_rgb", - "any-uart", - "arm-gic-driver 0.14.9", - "arrayvec", - "buddy_system_allocator", - "fdt-parser", - "log", - "memory_addr 0.3.2", - "numeric-enum-macro", - "page-table-arm", - "page-table-generic 0.5.3", - "pie-boot", - "rgb", - "smccc 0.1.1", - "sparreal-kernel", - "sparreal-macros 0.9.3", - "spin 0.9.8", -] - [[package]] name = "spin" version = "0.9.8" @@ -5767,16 +5353,7 @@ version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros 0.27.2", + "strum_macros", ] [[package]] @@ -5789,19 +5366,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.111", -] - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5836,9 +5401,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.111" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", @@ -5868,7 +5433,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5945,9 +5510,9 @@ dependencies = [ [[package]] name = "tftpd" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee6f92408c23beea910b60784f6dc323b488c477bf6fa88aff8083675eda26c" +checksum = "abbee6dbf19d025600720ce174a82b10895e486179e0206b91f14b6fcf6cb4bf" [[package]] name = "thiserror" @@ -5975,7 +5540,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5986,7 +5551,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6096,7 +5661,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6146,14 +5711,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.10+spec-1.1.0" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.0.4", - "toml_datetime 0.7.5+spec-1.1.0", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", "toml_parser", "toml_writer", "winnow", @@ -6170,9 +5735,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.5+spec-1.1.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ "serde_core", ] @@ -6193,21 +5758,21 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ "indexmap", - "toml_datetime 0.7.5+spec-1.1.0", + "toml_datetime 0.7.3", "toml_parser", "winnow", ] [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ "winnow", ] @@ -6220,9 +5785,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "tower" @@ -6242,15 +5807,15 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "bitflags 2.10.0", "bytes", "futures-core", "futures-util", - "http 1.4.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "http-range-header", @@ -6282,9 +5847,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.44" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -6293,9 +5858,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.36" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] @@ -6310,7 +5875,7 @@ dependencies = [ "lenient_semver", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6360,9 +5925,9 @@ dependencies = [ [[package]] name = "unescaper" -version = "0.1.8" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4064ed685c487dbc25bd3f0e9548f2e34bab9d18cefc700f9ec2dba74ba1138e" +checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26" dependencies = [ "thiserror 2.0.17", ] @@ -6391,7 +5956,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ - "itertools 0.13.0", + "itertools", "unicode-segmentation", "unicode-width 0.1.14", ] @@ -6416,9 +5981,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unit-prefix" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" +checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" [[package]] name = "untrusted" @@ -6445,12 +6010,12 @@ dependencies = [ [[package]] name = "ureq-proto" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" +checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2" dependencies = [ "base64 0.22.1", - "http 1.4.0", + "http 1.3.1", "httparse", "log", ] @@ -6475,9 +6040,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8-width" -version = "0.1.8" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] name = "utf8_iter" @@ -6493,9 +6058,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.19.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "js-sys", "wasm-bindgen", @@ -6571,9 +6136,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", @@ -6584,9 +6149,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -6597,9 +6162,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6607,31 +6172,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -6686,9 +6251,9 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -6699,7 +6264,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6710,9 +6275,15 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" @@ -6721,13 +6292,22 @@ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-registry" -version = "0.6.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -6736,7 +6316,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -6745,7 +6334,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -6790,7 +6379,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -6830,7 +6419,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -6981,9 +6570,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -7045,13 +6634,12 @@ dependencies = [ [[package]] name = "x86_64" -version = "0.15.4" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7841fa0098ceb15c567d93d3fae292c49e10a7662b4936d5f6a9728594555ba" +checksum = "0f042214de98141e9c8706e8192b73f56494087cc55ebec28ce10f26c5c364ae" dependencies = [ "bit_field", "bitflags 2.10.0", - "const_fn", "rustversion", "volatile", ] @@ -7074,15 +6662,15 @@ checksum = "873e097d52e94c31be3f0175a9f8d6f2edbc77d7e2f8e6995427df9c08b30a2b" dependencies = [ "axaddrspace", "axdevice_base", - "axerrno 0.1.2", - "axvcpu", + "axerrno", + "axvcpu 0.1.2", "axvisor_api", "bit_field", "bitflags 2.10.0", "cfg-if", "crate_interface", "log", - "memory_addr 0.4.1", + "memory_addr", "numeric-enum-macro", "page_table_entry", "paste", @@ -7101,11 +6689,11 @@ checksum = "2556c62649a277ccf1c3c34c740be87bbde5f8dab0b20fcdcf4c2cd7bb6e7302" dependencies = [ "axaddrspace", "axdevice_base", - "axerrno 0.1.2", + "axerrno", "axvisor_api", "bit", "log", - "memory_addr 0.4.1", + "memory_addr", "paste", "tock-registers 0.10.1", ] @@ -7136,17 +6724,15 @@ dependencies = [ "chrono", "clap", "colored", - "flate2", "jkconfig", "ostool", - "reqwest 0.12.26", + "reqwest 0.12.24", "schemars", "serde", "serde_json", "sha2", - "tar", "tokio", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.8", ] [[package]] @@ -7168,7 +6754,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "synstructure", ] @@ -7184,11 +6770,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ - "zerocopy-derive 0.8.31", + "zerocopy-derive 0.8.27", ] [[package]] @@ -7199,18 +6785,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -7230,7 +6816,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "synstructure", ] @@ -7270,5 +6856,5 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] diff --git a/configs/board/qemu-riscv64.toml b/configs/board/qemu-riscv64.toml new file mode 100644 index 000000000..1cf8b3aa4 --- /dev/null +++ b/configs/board/qemu-riscv64.toml @@ -0,0 +1,11 @@ +cargo_args = [] +features = [ + "axstd/myplat", + "ept-level-4", + "axstd/bus-mmio", + # "fs", +] +log = "Info" +target = "riscv64gc-unknown-none-elf" +to_bin = true +vm_configs = [] \ No newline at end of file diff --git a/kernel/src/hal/arch/riscv64/cache.rs b/kernel/src/hal/arch/riscv64/cache.rs new file mode 100644 index 000000000..1a905a9d0 --- /dev/null +++ b/kernel/src/hal/arch/riscv64/cache.rs @@ -0,0 +1,5 @@ +use memory_addr::VirtAddr; + +use crate::hal::CacheOp; + +pub fn dcache_range(_op: CacheOp, _addr: VirtAddr, _size: usize) {} diff --git a/kernel/src/hal/arch/riscv64/mod.rs b/kernel/src/hal/arch/riscv64/mod.rs new file mode 100644 index 000000000..ab92f90b2 --- /dev/null +++ b/kernel/src/hal/arch/riscv64/mod.rs @@ -0,0 +1,4 @@ +pub mod cache; + +pub fn hardware_check() {} +pub fn inject_interrupt(_vector: u8) {} diff --git a/platform/riscv64-qemu-virt/Cargo.toml b/platform/riscv64-qemu-virt/Cargo.toml new file mode 100644 index 000000000..9cb0d717f --- /dev/null +++ b/platform/riscv64-qemu-virt/Cargo.toml @@ -0,0 +1,27 @@ +[package] +edition = "2024" +name = "axplat-riscv64-qemu-virt" +version = "0.2.0" + +[features] +default = [ + "irq", + "smp", +] +fp-simd = ["axcpu/fp-simd"] +irq = ["axplat/irq"] +rtc = ["riscv_goldfish"] +smp = ["axplat/smp"] + +[dependencies] +log = "0.4" +riscv = "0.14" +sbi-rt = { version = "0.0.3", features = ["legacy"] } +riscv_goldfish = { version = "0.1", optional = true } + +axconfig-macros = "0.2" +axcpu = "0.2" +axplat = { workspace = true } + +[package.metadata.docs.rs] +targets = ["riscv64gc-unknown-none-elf"] diff --git a/platform/riscv64-qemu-virt/axconfig.toml b/platform/riscv64-qemu-virt/axconfig.toml new file mode 100644 index 000000000..8997e1886 --- /dev/null +++ b/platform/riscv64-qemu-virt/axconfig.toml @@ -0,0 +1,84 @@ +# Architecture identifier. +arch = "riscv64" # str +# Platform identifier. +platform = "riscv64-qemu-virt" # str +# Platform package. +package = "axplat-riscv64-qemu-virt" # str + +# +# Platform configs +# +[plat] +# Number of CPUs. +cpu-num = 1 # uint +# Base address of the whole physical memory. +phys-memory-base = 0x8000_0000 # uint +# Size of the whole physical memory. (128M) +phys-memory-size = 0x800_0000 # uint +# Base physical address of the kernel image. +kernel-base-paddr = 0x8020_0000 # uint +# Base virtual address of the kernel image. +kernel-base-vaddr = "0xffff_ffc0_8020_0000" # uint +# Linear mapping offset, for quick conversions between physical and virtual +# addresses. +phys-virt-offset = "0xffff_ffc0_0000_0000" # uint +# Offset of bus address and phys address. some boards, the bus address is +# different from the physical address. +phys-bus-offset = 0 # uint +# Kernel address space base. +kernel-aspace-base = "0xffff_ffc0_0000_0000" # uint +# Kernel address space size. +kernel-aspace-size = "0x0000_003f_ffff_f000" # uint +# Stack size on bootstrapping. (256K) +boot-stack-size = 0x40000 # uint + +# +# Device specifications +# +[devices] +# MMIO ranges with format (`base_paddr`, `size`). +mmio-ranges = [ + [0x0010_1000, 0x1000], # RTC + [0x0c00_0000, 0x21_0000], # PLIC + [0x1000_0000, 0x1000], # UART + [0x1000_1000, 0x8000], # VirtIO + [0x3000_0000, 0x1000_0000], # PCI config space + [0x4000_0000, 0x4000_0000], # PCI memory ranges (ranges 1: 32-bit MMIO space) +] # [(uint, uint)] +# VirtIO MMIO ranges with format (`base_paddr`, `size`). +virtio-mmio-ranges = [ + [0x1000_1000, 0x1000], + [0x1000_2000, 0x1000], + [0x1000_3000, 0x1000], + [0x1000_4000, 0x1000], + [0x1000_5000, 0x1000], + [0x1000_6000, 0x1000], + [0x1000_7000, 0x1000], + [0x1000_8000, 0x1000], +] # [(uint, uint)] +# Base physical address of the PCIe ECAM space. +pci-ecam-base = 0x3000_0000 # uint +# End PCI bus number (`bus-range` property in device tree). +pci-bus-end = 0xff # uint +# PCI device memory ranges (`ranges` property in device tree). +pci-ranges = [ + [0x0300_0000, 0x1_0000], # PIO space + [0x4000_0000, 0x4000_0000], # 32-bit MMIO space + [0x4_0000_0000, 0x4_0000_0000], # 64-bit MMIO space +] # [(uint, uint)] + +# Timer interrupt frequency in Hz. +timer-frequency = 10_000_000 # uint +# Timer interrupt num. +timer-irq = "0x8000_0000_0000_0005" # uint +# IPI interrupt num +ipi-irq = "0x8000_0000_0000_0001" # uint + +# rtc@101000 { +# interrupts = <0x0b>; +# interrupt-parent = <0x03>; +# reg = <0x00 0x101000 0x00 0x1000>; +# compatible = "google,goldfish-rtc"; +# }; +# RTC (goldfish) Address +rtc-paddr = 0x10_1000 # uint \ No newline at end of file diff --git a/platform/riscv64-qemu-virt/build.rs b/platform/riscv64-qemu-virt/build.rs new file mode 100644 index 000000000..86c6182a5 --- /dev/null +++ b/platform/riscv64-qemu-virt/build.rs @@ -0,0 +1,21 @@ +fn main() { + println!("cargo:rerun-if-env-changed=AXVISOR_SMP"); + println!("cargo:rerun-if-changed=linker.lds.S"); + + let mut smp = 1; + if let Ok(s) = std::env::var("AXVISOR_SMP") { + smp = s.parse::().unwrap_or(1); + } + + let ld_content = include_str!("linker.lds.S"); + let ld_content = ld_content.replace("%ARCH%", "riscv"); + let ld_content = + ld_content.replace("%KERNEL_BASE%", &format!("{:#x}", 0xffff_ffc0_8020_0000usize)); + let ld_content = ld_content.replace("%SMP%", &format!("{smp}",)); + + // target///build/axvisor-xxxx/out + let out_dir = std::env::var("OUT_DIR").unwrap(); + let out_path = std::path::Path::new(&out_dir).join("link.x"); + println!("cargo:rustc-link-search={out_dir}"); + std::fs::write(out_path, ld_content).unwrap(); +} diff --git a/platform/riscv64-qemu-virt/linker.lds.S b/platform/riscv64-qemu-virt/linker.lds.S new file mode 100644 index 000000000..439284c71 --- /dev/null +++ b/platform/riscv64-qemu-virt/linker.lds.S @@ -0,0 +1,99 @@ +OUTPUT_ARCH(%ARCH%) + +BASE_ADDRESS = %KERNEL_BASE%; +SMP = %SMP%; + +ENTRY(_start) +SECTIONS +{ + . = BASE_ADDRESS; + _skernel = .; + + .text : ALIGN(4K) { + _stext = .; + *(.text.boot) + *(.text .text.*) + . = ALIGN(4K); + _etext = .; + } + + .rodata : ALIGN(4K) { + _srodata = .; + *(.rodata .rodata.*) + *(.srodata .srodata.*) + *(.sdata2 .sdata2.*) + . = ALIGN(4K); + _erodata = .; + } + + .data : ALIGN(4K) { + _sdata = .; + *(.data.boot_page_table) + . = ALIGN(4K); + __sdriver_register = .; + KEEP(*(.driver.register*)) + __edriver_register = .; + + *(.data .data.*) + *(.sdata .sdata.*) + *(.got .got.*) + } + + .tdata : ALIGN(0x10) { + _stdata = .; + *(.tdata .tdata.*) + _etdata = .; + } + + .tbss : ALIGN(0x10) { + _stbss = .; + *(.tbss .tbss.*) + *(.tcommon) + _etbss = .; + } + + . = ALIGN(4K); + _percpu_start = .; + _percpu_end = _percpu_start + SIZEOF(.percpu); + .percpu 0x0 : AT(_percpu_start) { + _percpu_load_start = .; + *(.percpu .percpu.*) + _percpu_load_end = .; + . = _percpu_load_start + ALIGN(64) * SMP; + } + . = _percpu_end; + + . = ALIGN(4K); + _edata = .; + + .bss : ALIGN(4K) { + boot_stack = .; + *(.bss.stack) + . = ALIGN(4K); + boot_stack_top = .; + + _sbss = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + *(COMMON) + . = ALIGN(4K); + _ebss = .; + } + + _ekernel = .; + + /DISCARD/ : { + *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) + } +} + +SECTIONS { + linkme_IRQ : { *(linkme_IRQ) } + linkm2_IRQ : { *(linkm2_IRQ) } + linkme_PAGE_FAULT : { *(linkme_PAGE_FAULT) } + linkm2_PAGE_FAULT : { *(linkm2_PAGE_FAULT) } + linkme_SYSCALL : { *(linkme_SYSCALL) } + linkm2_SYSCALL : { *(linkm2_SYSCALL) } + axns_resource : { *(axns_resource) } +} +INSERT AFTER .tbss; diff --git a/platform/riscv64-qemu-virt/src/boot.rs b/platform/riscv64-qemu-virt/src/boot.rs new file mode 100644 index 000000000..6fd866a4d --- /dev/null +++ b/platform/riscv64-qemu-virt/src/boot.rs @@ -0,0 +1,94 @@ +use crate::config::plat::{BOOT_STACK_SIZE, PHYS_VIRT_OFFSET}; +use axplat::mem::{Aligned4K, pa}; + +#[unsafe(link_section = ".bss.stack")] +static mut BOOT_STACK: [u8; BOOT_STACK_SIZE] = [0; BOOT_STACK_SIZE]; + +#[unsafe(link_section = ".data")] +static mut BOOT_PT_SV39: Aligned4K<[u64; 512]> = Aligned4K::new([0; 512]); + +#[allow(clippy::identity_op)] // (0x0 << 10) here makes sense because it's an address +unsafe fn init_boot_page_table() { + unsafe { + // 0x0000_0000..0x4000_0000, VRWX_GAD, 1G block + BOOT_PT_SV39[0] = (0x0 << 10) | 0xef; + // 0x8000_0000..0xc000_0000, VRWX_GAD, 1G block + BOOT_PT_SV39[2] = (0x80000 << 10) | 0xef; + // 0xffff_ffc0_0000_0000..0xffff_ffc0_4000_0000, VRWX_GAD, 1G block + BOOT_PT_SV39[0x100] = (0x0 << 10) | 0xef; + // 0xffff_ffc0_8000_0000..0xffff_ffc0_c000_0000, VRWX_GAD, 1G block + BOOT_PT_SV39[0x102] = (0x80000 << 10) | 0xef; + } +} + +unsafe fn init_mmu() { + unsafe { + axcpu::asm::write_kernel_page_table(pa!(&raw const BOOT_PT_SV39 as usize)); + axcpu::asm::flush_tlb(None); + } +} + +/// The earliest entry point for the primary CPU. +#[unsafe(naked)] +#[unsafe(no_mangle)] +#[unsafe(link_section = ".text.boot")] +unsafe extern "C" fn _start() -> ! { + // PC = 0x8020_0000 + // a0 = hartid + // a1 = dtb + core::arch::naked_asm!(" + mv s0, a0 // save hartid + mv s1, a1 // save DTB pointer + la sp, {boot_stack} + li t0, {boot_stack_size} + add sp, sp, t0 // setup boot stack + + call {init_boot_page_table} + call {init_mmu} // setup boot page table and enabel MMU + + li s2, {phys_virt_offset} // fix up virtual high address + add sp, sp, s2 + + mv a0, s0 + mv a1, s1 + la a2, {entry} + add a2, a2, s2 + jalr a2 // call_main(cpu_id, dtb) + j .", + phys_virt_offset = const PHYS_VIRT_OFFSET, + boot_stack_size = const BOOT_STACK_SIZE, + boot_stack = sym BOOT_STACK, + init_boot_page_table = sym init_boot_page_table, + init_mmu = sym init_mmu, + entry = sym axplat::call_main, + ) +} + +/// The earliest entry point for secondary CPUs. +#[cfg(feature = "smp")] +#[unsafe(naked)] +#[unsafe(no_mangle)] +#[unsafe(link_section = ".text.boot")] +unsafe extern "C" fn _start_secondary() -> ! { + // a0 = hartid + // a1 = SP + core::arch::naked_asm!(" + mv s0, a0 // save hartid + mv sp, a1 // set SP + + call {init_mmu} // setup boot page table and enabel MMU + + li s1, {phys_virt_offset} // fix up virtual high address + add a1, a1, s1 + add sp, sp, s1 + + mv a0, s0 + la a1, {entry} + add a1, a1, s1 + jalr a1 // call_secondary_main(cpu_id) + j .", + phys_virt_offset = const PHYS_VIRT_OFFSET, + init_mmu = sym init_mmu, + entry = sym axplat::call_secondary_main, + ) +} diff --git a/platform/riscv64-qemu-virt/src/console.rs b/platform/riscv64-qemu-virt/src/console.rs new file mode 100644 index 000000000..7c305a8b4 --- /dev/null +++ b/platform/riscv64-qemu-virt/src/console.rs @@ -0,0 +1,51 @@ +use axplat::mem::{VirtAddr, virt_to_phys}; + +/// The maximum number of bytes that can be read at once. +const MAX_RW_SIZE: usize = 256; + +/// Tries to write bytes to the console from input u8 slice. +/// Returns the number of bytes written. +fn try_write_bytes(bytes: &[u8]) -> usize { + sbi_rt::console_write(sbi_rt::Physical::new( + // A maximum of 256 bytes can be written at a time + // to prevent SBI from disabling IRQs for too long. + bytes.len().min(MAX_RW_SIZE), + virt_to_phys(VirtAddr::from_ptr_of(bytes.as_ptr())).as_usize(), + 0, + )) + .value +} + +use axplat::console::ConsoleIf; + +struct ConsoleIfImpl; + +#[impl_plat_interface] +impl ConsoleIf for ConsoleIfImpl { + /// Writes bytes to the console from input u8 slice. + fn write_bytes(bytes: &[u8]) { + let mut write_len = 0; + let mut buf = [0; MAX_RW_SIZE]; + while write_len < bytes.len() { + let n = buf.len().min(bytes.len() - write_len); + if n == 0 { + break; + } + // `bytes` can be from user space, copy it into a kernel buffer + // to correctly use `virt_to_phys`. + buf[..n].copy_from_slice(&bytes[write_len..write_len + n]); + write_len += try_write_bytes(&buf[..n]); + } + } + + /// Reads bytes from the console into the given mutable slice. + /// Returns the number of bytes read. + fn read_bytes(bytes: &mut [u8]) -> usize { + sbi_rt::console_read(sbi_rt::Physical::new( + bytes.len().min(MAX_RW_SIZE), + virt_to_phys(VirtAddr::from_mut_ptr_of(bytes.as_mut_ptr())).as_usize(), + 0, + )) + .value + } +} diff --git a/platform/riscv64-qemu-virt/src/init.rs b/platform/riscv64-qemu-virt/src/init.rs new file mode 100644 index 000000000..7090f415d --- /dev/null +++ b/platform/riscv64-qemu-virt/src/init.rs @@ -0,0 +1,39 @@ +use axplat::init::InitIf; + +struct InitIfImpl; + +#[impl_plat_interface] +impl InitIf for InitIfImpl { + /// This function should be called immediately after the kernel has booted, + /// and performed earliest platform configuration and initialization (e.g., + /// early console, clocking). + fn init_early(_cpu_id: usize, _mbi: usize) { + axcpu::init::init_trap(); + crate::time::init_early(); + } + + /// Initializes the platform at the early stage for secondary cores. + #[cfg(feature = "smp")] + fn init_early_secondary(_cpu_id: usize) { + axcpu::init::init_trap(); + } + + /// Initializes the platform at the later stage for the primary core. + /// + /// This function should be called after the kernel has done part of its + /// initialization (e.g, logging, memory management), and finalized the rest of + /// platform configuration and initialization. + fn init_later(_cpu_id: usize, _arg: usize) { + #[cfg(feature = "irq")] + crate::irq::init_percpu(); + crate::time::init_percpu(); + } + + /// Initializes the platform at the later stage for secondary cores. + #[cfg(feature = "smp")] + fn init_later_secondary(_cpu_id: usize) { + #[cfg(feature = "irq")] + crate::irq::init_percpu(); + crate::time::init_percpu(); + } +} diff --git a/platform/riscv64-qemu-virt/src/irq.rs b/platform/riscv64-qemu-virt/src/irq.rs new file mode 100644 index 000000000..250c4c855 --- /dev/null +++ b/platform/riscv64-qemu-virt/src/irq.rs @@ -0,0 +1,196 @@ +//! TODO: PLIC + +use axplat::irq::{HandlerTable, IpiTarget, IrqHandler, IrqIf}; +use core::sync::atomic::{AtomicPtr, Ordering}; +use riscv::register::sie; +use sbi_rt::HartMask; + +/// `Interrupt` bit in `scause` +pub(super) const INTC_IRQ_BASE: usize = 1 << (usize::BITS - 1); + +/// Supervisor software interrupt in `scause` +#[allow(unused)] +pub(super) const S_SOFT: usize = INTC_IRQ_BASE + 1; + +/// Supervisor timer interrupt in `scause` +pub(super) const S_TIMER: usize = INTC_IRQ_BASE + 5; + +/// Supervisor external interrupt in `scause` +pub(super) const S_EXT: usize = INTC_IRQ_BASE + 9; + +static TIMER_HANDLER: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut()); + +static IPI_HANDLER: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut()); + +/// The maximum number of IRQs. +pub const MAX_IRQ_COUNT: usize = 1024; + +static IRQ_HANDLER_TABLE: HandlerTable = HandlerTable::new(); + +macro_rules! with_cause { + ($cause: expr, @S_TIMER => $timer_op: expr, @S_SOFT => $ipi_op: expr, @S_EXT => $ext_op: expr, @EX_IRQ => $plic_op: expr $(,)?) => { + match $cause { + S_TIMER => $timer_op, + S_SOFT => $ipi_op, + S_EXT => $ext_op, + other => { + if other & INTC_IRQ_BASE == 0 { + // Device-side interrupts read from PLIC + $plic_op + } else { + // Other CPU-side interrupts + panic!("Unknown IRQ cause: {}", other); + } + } + } + }; +} + +pub(super) fn init_percpu() { + // enable soft interrupts, timer interrupts, and external interrupts + unsafe { + sie::set_ssoft(); + sie::set_stimer(); + sie::set_sext(); + } +} + +struct IrqIfImpl; + +#[impl_plat_interface] +impl IrqIf for IrqIfImpl { + /// Enables or disables the given IRQ. + fn set_enable(irq: usize, _enabled: bool) { + // TODO: set enable in PLIC + warn!("set_enable is not implemented for IRQ {}", irq); + } + + /// Registers an IRQ handler for the given IRQ. + /// + /// It also enables the IRQ if the registration succeeds. It returns `false` if + /// the registration failed. + /// + /// The `irq` parameter has the following semantics + /// 1. If its highest bit is 1, it means it is an interrupt on the CPU side. Its + /// value comes from `scause`, where [`S_SOFT`] represents software interrupt + /// and [`S_TIMER`] represents timer interrupt. If its value is [`S_EXT`], it + /// means it is an external interrupt, and the real IRQ number needs to + /// be obtained from PLIC. + /// 2. If its highest bit is 0, it means it is an interrupt on the device side, + /// and its value is equal to the IRQ number provided by PLIC. + fn register(irq: usize, handler: IrqHandler) -> bool { + with_cause!( + irq, + @S_TIMER => TIMER_HANDLER.compare_exchange(core::ptr::null_mut(), handler as *mut _, Ordering::AcqRel, Ordering::Acquire).is_ok(), + @S_SOFT => IPI_HANDLER.compare_exchange(core::ptr::null_mut(), handler as *mut _, Ordering::AcqRel, Ordering::Acquire).is_ok(), + @S_EXT => { + warn!("External IRQ should be got from PLIC, not scause"); + false + }, + @EX_IRQ => { + if IRQ_HANDLER_TABLE.register_handler(irq, handler) { + Self::set_enable(irq, true); + true + } else { + warn!("register handler for External IRQ {} failed", irq); + false + } + } + ) + } + + /// Unregisters the IRQ handler for the given IRQ. + /// + /// It also disables the IRQ if the unregistration succeeds. It returns the + /// existing handler if it is registered, `None` otherwise. + fn unregister(irq: usize) -> Option { + with_cause!( + irq, + @S_TIMER => { + let handler = TIMER_HANDLER.swap(core::ptr::null_mut(), Ordering::AcqRel); + if !handler.is_null() { + Some(unsafe { core::mem::transmute::<*mut (), IrqHandler>(handler) }) + } else { + None + } + }, + @S_SOFT => { + let handler = IPI_HANDLER.swap(core::ptr::null_mut(), Ordering::AcqRel); + if !handler.is_null() { + Some(unsafe { core::mem::transmute::<*mut (), IrqHandler>(handler) }) + } else { + None + } + }, + @S_EXT => { + warn!("External IRQ should be got from PLIC, not scause"); + None + }, + @EX_IRQ => IRQ_HANDLER_TABLE.unregister_handler(irq) + ) + } + + /// Handles the IRQ. + /// + /// It is called by the common interrupt handler. It should look up in the + /// IRQ handler table and calls the corresponding handler. If necessary, it + /// also acknowledges the interrupt controller after handling. + fn handle(irq: usize) { + with_cause!( + irq, + @S_TIMER => { + trace!("IRQ: timer"); + let handler = TIMER_HANDLER.load(Ordering::Acquire); + if !handler.is_null() { + // SAFETY: The handler is guaranteed to be a valid function pointer. + unsafe { core::mem::transmute::<*mut (), IrqHandler>(handler)() }; + } + }, + @S_SOFT => { + trace!("IRQ: IPI"); + let handler = IPI_HANDLER.load(Ordering::Acquire); + if !handler.is_null() { + // SAFETY: The handler is guaranteed to be a valid function pointer. + unsafe { core::mem::transmute::<*mut (), IrqHandler>(handler)() }; + } + }, + @S_EXT => { + // TODO: get IRQ number from PLIC + if !IRQ_HANDLER_TABLE.handle(0) { + warn!("Unhandled IRQ {}", 0); + } + }, + @EX_IRQ => { + unreachable!("Device-side IRQs should be handled by triggering the External Interrupt."); + } + ) + } + + /// Sends an inter-processor interrupt (IPI) to the specified target CPU or all CPUs. + fn send_ipi(_irq_num: usize, target: IpiTarget) { + match target { + IpiTarget::Current { cpu_id } => { + let res = sbi_rt::send_ipi(HartMask::from_mask_base(1 << cpu_id, 0)); + if res.is_err() { + warn!("send_ipi failed: {:?}", res); + } + } + IpiTarget::Other { cpu_id } => { + let res = sbi_rt::send_ipi(HartMask::from_mask_base(1 << cpu_id, 0)); + if res.is_err() { + warn!("send_ipi failed: {:?}", res); + } + } + IpiTarget::AllExceptCurrent { cpu_id, cpu_num } => { + for i in 0..cpu_num { + if i != cpu_id { + let res = sbi_rt::send_ipi(HartMask::from_mask_base(1 << i, 0)); + if res.is_err() { + warn!("send_ipi_all_others failed: {:?}", res); + } + } + } + } + } + } +} diff --git a/platform/riscv64-qemu-virt/src/lib.rs b/platform/riscv64-qemu-virt/src/lib.rs new file mode 100644 index 000000000..e5939e048 --- /dev/null +++ b/platform/riscv64-qemu-virt/src/lib.rs @@ -0,0 +1,30 @@ +#![no_std] + +#[macro_use] +extern crate log; +#[macro_use] +extern crate axplat; + +mod boot; +mod console; +mod init; +#[cfg(feature = "irq")] +mod irq; +mod mem; +mod power; +mod time; + +pub mod config { + //! Platform configuration module. + //! + //! If the `AX_CONFIG_PATH` environment variable is set, it will load the configuration from the specified path. + //! Otherwise, it will fall back to the `axconfig.toml` file in the current directory and generate the default configuration. + //! + //! If the `PACKAGE` field in the configuration does not match the package name, it will panic with an error message. + axconfig_macros::include_configs!(path_env = "AX_CONFIG_PATH", fallback = "axconfig.toml"); + assert_str_eq!( + PACKAGE, + env!("CARGO_PKG_NAME"), + "`PACKAGE` field in the configuration does not match the Package name. Please check your configuration file." + ); +} diff --git a/platform/riscv64-qemu-virt/src/mem.rs b/platform/riscv64-qemu-virt/src/mem.rs new file mode 100644 index 000000000..13000df04 --- /dev/null +++ b/platform/riscv64-qemu-virt/src/mem.rs @@ -0,0 +1,58 @@ +use axplat::mem::{MemIf, PhysAddr, RawRange, VirtAddr, pa, va}; + +use crate::config::devices::MMIO_RANGES; +use crate::config::plat::{ + KERNEL_BASE_PADDR, PHYS_MEMORY_BASE, PHYS_MEMORY_SIZE, PHYS_VIRT_OFFSET, +}; + +struct MemIfImpl; + +#[impl_plat_interface] +impl MemIf for MemIfImpl { + /// Returns all physical memory (RAM) ranges on the platform. + /// + /// All memory ranges except reserved ranges (including the kernel loaded + /// range) are free for allocation. + fn phys_ram_ranges() -> &'static [RawRange] { + // TODO: paser dtb to get the available memory ranges + // We can't directly use `PHYS_MEMORY_BASE` here, because it may has been used by sbi. + &[( + KERNEL_BASE_PADDR, + PHYS_MEMORY_BASE + PHYS_MEMORY_SIZE - KERNEL_BASE_PADDR, + )] + } + + /// Returns all reserved physical memory ranges on the platform. + /// + /// Reserved memory can be contained in [`phys_ram_ranges`], they are not + /// allocatable but should be mapped to kernel's address space. + /// + /// Note that the ranges returned should not include the range where the + /// kernel is loaded. + fn reserved_phys_ram_ranges() -> &'static [RawRange] { + &[] + } + + /// Returns all device memory (MMIO) ranges on the platform. + fn mmio_ranges() -> &'static [RawRange] { + &MMIO_RANGES + } + + /// Translates a physical address to a virtual address. + fn phys_to_virt(paddr: PhysAddr) -> VirtAddr { + va!(paddr.as_usize() + PHYS_VIRT_OFFSET) + } + + /// Translates a virtual address to a physical address. + fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr { + pa!(vaddr.as_usize() - PHYS_VIRT_OFFSET) + } + + /// Returns the kernel address space base virtual address and size. + fn kernel_aspace() -> (VirtAddr, usize) { + ( + va!(crate::config::plat::KERNEL_ASPACE_BASE), + crate::config::plat::KERNEL_ASPACE_SIZE, + ) + } +} diff --git a/platform/riscv64-qemu-virt/src/power.rs b/platform/riscv64-qemu-virt/src/power.rs new file mode 100644 index 000000000..c4a87924b --- /dev/null +++ b/platform/riscv64-qemu-virt/src/power.rs @@ -0,0 +1,35 @@ +use axplat::power::PowerIf; + +struct PowerImpl; + +#[impl_plat_interface] +impl PowerIf for PowerImpl { + /// Bootstraps the given CPU core with the given initial stack (in physical + /// address). + /// + /// Where `cpu_id` is the logical CPU ID (0, 1, ..., N-1, N is the number of + /// CPU cores on the platform). + #[cfg(feature = "smp")] + fn cpu_boot(cpu_id: usize, stack_top_paddr: usize) { + use axplat::mem::{va, virt_to_phys}; + unsafe extern "C" { + fn _start_secondary(); + } + if sbi_rt::probe_extension(sbi_rt::Hsm).is_unavailable() { + warn!("HSM SBI extension is not supported for current SEE."); + return; + } + let entry = virt_to_phys(va!(_start_secondary as usize)); + sbi_rt::hart_start(cpu_id, entry.as_usize(), stack_top_paddr); + } + + /// Shutdown the whole system. + fn system_off() -> ! { + info!("Shutting down..."); + sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::NoReason); + warn!("It should shutdown!"); + loop { + axcpu::asm::halt(); + } + } +} diff --git a/platform/riscv64-qemu-virt/src/time.rs b/platform/riscv64-qemu-virt/src/time.rs new file mode 100644 index 000000000..a8c84e1dc --- /dev/null +++ b/platform/riscv64-qemu-virt/src/time.rs @@ -0,0 +1,75 @@ +use riscv::register::time; + +use axplat::time::TimeIf; + +const NANOS_PER_SEC: u64 = 1_000_000_000; + +const NANOS_PER_TICK: u64 = NANOS_PER_SEC / crate::config::devices::TIMER_FREQUENCY as u64; +/// RTC wall time offset in nanoseconds at monotonic time base. +static mut RTC_EPOCHOFFSET_NANOS: u64 = 0; + +pub(super) fn init_early() { + #[cfg(feature = "rtc")] + use crate::config::devices::RTC_PADDR; + + #[cfg(feature = "rtc")] + if RTC_PADDR != 0 { + use axplat::mem::{PhysAddr, pa, phys_to_virt}; + + use riscv_goldfish::Rtc; + + const GOLDFISH_BASE: PhysAddr = pa!(RTC_PADDR); + // Get the current time in microseconds since the epoch (1970-01-01) from the riscv RTC. + // Subtract the timer ticks to get the actual time when ArceOS was booted. + let epoch_time_nanos = + Rtc::new(phys_to_virt(GOLDFISH_BASE).as_usize()).get_unix_timestamp() * 1_000_000_000; + + unsafe { + RTC_EPOCHOFFSET_NANOS = + epoch_time_nanos - TimeIfImpl::ticks_to_nanos(TimeIfImpl::current_ticks()); + } + } +} + +pub(super) fn init_percpu() { + #[cfg(feature = "irq")] + sbi_rt::set_timer(0); +} + +struct TimeIfImpl; + +#[impl_plat_interface] +impl TimeIf for TimeIfImpl { + /// Returns the IRQ number for the timer interrupt. + fn irq_num() -> usize { + crate::config::devices::TIMER_IRQ + } + + /// Returns the current clock time in hardware ticks. + fn current_ticks() -> u64 { + time::read() as u64 + } + + /// Converts hardware ticks to nanoseconds. + fn ticks_to_nanos(ticks: u64) -> u64 { + ticks * NANOS_PER_TICK + } + + /// Converts nanoseconds to hardware ticks. + fn nanos_to_ticks(nanos: u64) -> u64 { + nanos / NANOS_PER_TICK + } + + /// Return epoch offset in nanoseconds (wall time offset to monotonic clock start). + fn epochoffset_nanos() -> u64 { + unsafe { RTC_EPOCHOFFSET_NANOS } + } + + /// Set a one-shot timer. + /// + /// A timer interrupt will be triggered at the specified monotonic time deadline (in nanoseconds). + #[cfg(feature = "irq")] + fn set_oneshot_timer(deadline_ns: u64) { + sbi_rt::set_timer(Self::nanos_to_ticks(deadline_ns)); + } +} diff --git a/scripts/ostool/qemu-riscv64.toml b/scripts/ostool/qemu-riscv64.toml new file mode 100644 index 000000000..7106493d5 --- /dev/null +++ b/scripts/ostool/qemu-riscv64.toml @@ -0,0 +1,23 @@ +args = [ + "-nographic", + "-cpu", + "rv64", + "-machine", + "virt", + "-bios", + "default", + "-smp", + "4", + "-device", + "virtio-blk-device,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", + "-append", + "root=/dev/vda rw init=/init", + "-m", + "8g", +] +fail_regex = [] +success_regex = [] +to_bin = true +uefi = false From 933c2858779a48f1a34614c1e30a1c702ed9f162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=99=AF=E5=AE=87?= <2537738252@qq.com> Date: Fri, 28 Nov 2025 20:33:21 +0800 Subject: [PATCH 027/132] riscv64: initial support --- .cargo/config.toml | 8 ++++++++ Cargo.lock | 1 + Cargo.toml | 1 + kernel/src/hal/mod.rs | 1 + modules/axruntime/Cargo.toml | 3 +++ modules/axruntime/src/lib.rs | 3 +++ xtask/src/cargo.rs | 5 ++++- 7 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 676b0e8a5..ecc88a18e 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,6 +5,14 @@ rustflags = [ "-Clink-args=-Tlink.x", ] +[target.riscv64gc-unknown-none-elf] +rustflags = [ + "-Clink-args=-no-pie", + "-Clink-args=-znostart-stop-gc", + "-Clink-args=-Tlink.x", +] + + [target.'cfg(target_os = "none")'] runner = "cargo osrun" diff --git a/Cargo.lock b/Cargo.lock index b688ef121..5343b3a37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1006,6 +1006,7 @@ dependencies = [ "axnet", "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "axplat-aarch64-dyn 0.3.0 (git+https://github.com/arceos-hypervisor/axplat-aarch64-dyn?tag=v0.3.3)", + "axplat-riscv64-qemu-virt 0.2.0", "axplat-x86-qemu-q35", "axtask", "cfg-if", diff --git a/Cargo.toml b/Cargo.toml index f06c0194b..6686f9b60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,6 +86,7 @@ driver = {path = "modules/driver"} # platform axplat-x86-qemu-q35 = {path = "platform/x86-qemu-q35"} +axplat-riscv64-qemu-virt = {path = "platform/riscv64-qemu-virt"} axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "next"} [patch.crates-io] diff --git a/kernel/src/hal/mod.rs b/kernel/src/hal/mod.rs index dc058723b..3b3577795 100644 --- a/kernel/src/hal/mod.rs +++ b/kernel/src/hal/mod.rs @@ -14,6 +14,7 @@ use axvm::{AxVMHal, AxVMPerCpu}; #[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/mod.rs")] #[cfg_attr(target_arch = "x86_64", path = "arch/x86_64/mod.rs")] +#[cfg_attr(target_arch = "riscv64", path = "arch/riscv64/mod.rs")] pub mod arch; use crate::{hal::arch::hardware_check, task::AsVCpuTask, vmm}; diff --git a/modules/axruntime/Cargo.toml b/modules/axruntime/Cargo.toml index c89c419e5..a7e216b15 100644 --- a/modules/axruntime/Cargo.toml +++ b/modules/axruntime/Cargo.toml @@ -52,6 +52,9 @@ chrono = {version = "0.4.38", default-features = false} [target.'cfg(target_arch = "x86_64")'.dependencies] axplat-x86-qemu-q35 = {workspace = true} +[target.'cfg(target_arch = "riscv64")'.dependencies] +axplat-riscv64-qemu-virt = {workspace = true} + [target.'cfg(target_arch = "aarch64")'.dependencies] axplat-aarch64-dyn = {git = "https://github.com/arceos-hypervisor/axplat-aarch64-dyn.git", tag = "v0.4.0", features = ["irq", "smp", "hv"]} somehal = "0.4" \ No newline at end of file diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index 2586dfad4..59c0cfec9 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -25,6 +25,9 @@ extern crate axlog; #[cfg(target_arch = "x86_64")] extern crate axplat_x86_qemu_q35; +#[cfg(target_arch = "riscv64")] +extern crate axplat_riscv64_qemu_virt; + #[cfg(target_arch = "aarch64")] extern crate axplat_aarch64_dyn; diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index 0fe676f39..93cd532b8 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -11,7 +11,9 @@ impl Context { Arch::Aarch64 } else if build_config.target.contains("x86_64") { Arch::X86_64 - } else { + } else if build_config.target.contains("riscv64") { + Arch::Riscv64 + }else { return Err(anyhow::anyhow!( "Unsupported target architecture: {}", build_config.target @@ -64,4 +66,5 @@ impl Context { enum Arch { Aarch64, X86_64, + Riscv64, } From 079cb8b447733301882518e35a35b12c7e20a0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=99=AF=E5=AE=87?= <2537738252@qq.com> Date: Fri, 28 Nov 2025 21:04:46 +0800 Subject: [PATCH 028/132] update axvm dependency --- Cargo.lock | 23 +++++++++++------------ Cargo.toml | 4 ++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5343b3a37..5c303faef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,9 +1160,9 @@ dependencies = [ "axhvc", "axruntime", "axstd", - "axvcpu 0.1.2", + "axvcpu 0.1.1", "axvisor_api", - "axvm 0.1.0 (git+https://github.com/arceos-hypervisor/axvm.git?branch=next)", + "axvm 0.1.0 (git+https://github.com/liulog/axvm.git?branch=next)", "bitflags 2.10.0", "byte-unit", "cfg-if", @@ -1224,7 +1224,7 @@ dependencies = [ "axdevice", "axdevice_base", "axerrno", - "axvcpu 0.1.2", + "axvcpu 0.1.1", "axvmconfig", "cfg-if", "cpumask", @@ -1233,7 +1233,7 @@ dependencies = [ "page_table_entry", "page_table_multiarch", "percpu", - "riscv_vcpu 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "riscv_vcpu 0.1.2 (git+https://github.com/liulog/riscv_vcpu.git?branch=master)", "spin 0.9.8", "x86_vcpu", ] @@ -1241,7 +1241,7 @@ dependencies = [ [[package]] name = "axvm" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axvm.git?branch=next#0393f27dea948433e53285a400e356cdfd4c4fa3" +source = "git+https://github.com/liulog/axvm.git?branch=next#22ca8ff823b1433c6f2f15bc99810fe0b4a2ccac" dependencies = [ "arm_vcpu", "arm_vgic 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1249,7 +1249,7 @@ dependencies = [ "axdevice", "axdevice_base", "axerrno", - "axvcpu 0.1.2", + "axvcpu 0.1.1", "axvmconfig", "cfg-if", "cpumask", @@ -1258,7 +1258,7 @@ dependencies = [ "page_table_entry", "page_table_multiarch", "percpu", - "riscv_vcpu 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "riscv_vcpu 0.1.2 (git+https://github.com/liulog/riscv_vcpu.git?branch=master)", "spin 0.9.8", "x86_vcpu", ] @@ -4623,7 +4623,7 @@ version = "0.1.2" dependencies = [ "axaddrspace", "axerrno", - "axvcpu 0.1.2", + "axvcpu 0.1.1", "axvisor_api", "bit_field", "bitflags 2.10.0", @@ -4645,12 +4645,11 @@ dependencies = [ [[package]] name = "riscv_vcpu" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f38f28fe6c02bb3ced43087c9667b23d18adf729becdc5adf1253f7df83904" +source = "git+https://github.com/liulog/riscv_vcpu.git?branch=master#6a82b377ce526d927e48ffde90f245c6a558a841" dependencies = [ "axaddrspace", "axerrno", - "axvcpu 0.1.2", + "axvcpu 0.1.1", "axvisor_api", "bit_field", "bitflags 2.10.0", @@ -6664,7 +6663,7 @@ dependencies = [ "axaddrspace", "axdevice_base", "axerrno", - "axvcpu 0.1.2", + "axvcpu 0.1.1", "axvisor_api", "bit_field", "bitflags 2.10.0", diff --git a/Cargo.toml b/Cargo.toml index 6686f9b60..5d03514bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ axklib = {git = "https://github.com/arceos-hypervisor/axklib.git"} axruntime = {path = "modules/axruntime"} axfs = {path = "modules/axfs"} axvcpu = "0.1" -axvm = {git = "https://github.com/arceos-hypervisor/axvm.git", branch = "next"} +axvm = {git = "https://github.com/liulog/axvm.git", branch = "next"} # System independent crates provided by ArceOS, these crates could be imported by remote url. axerrno = "0.2" @@ -90,7 +90,7 @@ axplat-riscv64-qemu-virt = {path = "platform/riscv64-qemu-virt"} axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "next"} [patch.crates-io] -axvcpu = {git = "https://github.com/arceos-hypervisor/axvcpu.git", branch = "next"} +axvcpu = {path="crates/axvcpu"} axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "next"} [patch."https://github.com/arceos-org/arceos"] From 076f79f2628108d6aca16f33dfa5a1a9b936fcf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=99=AF=E5=AE=87?= <2537738252@qq.com> Date: Sat, 29 Nov 2025 22:03:01 +0800 Subject: [PATCH 029/132] riscv64: support smp=4 boot --- configs/board/qemu-riscv64.toml | 3 ++- kernel/src/hal/arch/riscv64/mod.rs | 7 ++++++- modules/axruntime/src/lib.rs | 2 ++ platform/riscv64-qemu-virt/axconfig.toml | 2 +- platform/riscv64-qemu-virt/src/lib.rs | 4 ++++ 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/configs/board/qemu-riscv64.toml b/configs/board/qemu-riscv64.toml index 1cf8b3aa4..5e3d701b4 100644 --- a/configs/board/qemu-riscv64.toml +++ b/configs/board/qemu-riscv64.toml @@ -8,4 +8,5 @@ features = [ log = "Info" target = "riscv64gc-unknown-none-elf" to_bin = true -vm_configs = [] \ No newline at end of file +vm_configs = [] +smp = 4 \ No newline at end of file diff --git a/kernel/src/hal/arch/riscv64/mod.rs b/kernel/src/hal/arch/riscv64/mod.rs index ab92f90b2..64ff8cd98 100644 --- a/kernel/src/hal/arch/riscv64/mod.rs +++ b/kernel/src/hal/arch/riscv64/mod.rs @@ -1,4 +1,9 @@ pub mod cache; -pub fn hardware_check() {} +pub fn hardware_check() { + // TODO: implement hardware checks for RISC-V64 + // check page table level like aarch64 + +} + pub fn inject_interrupt(_vector: u8) {} diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index 59c0cfec9..1c4b9096e 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -327,6 +327,8 @@ pub fn cpu_count() -> usize { cpu_count = axplat_x86_qemu_q35::cpu_count() } else if #[cfg(target_arch = "aarch64")] { cpu_count = somehal::mem::cpu_id_list().count() + } else if #[cfg(all(target_arch = "riscv64", target_os = "none"))] { + cpu_count = axplat_riscv64_qemu_virt::cpu_count() } else { cpu_count = 1; } diff --git a/platform/riscv64-qemu-virt/axconfig.toml b/platform/riscv64-qemu-virt/axconfig.toml index 8997e1886..662b05079 100644 --- a/platform/riscv64-qemu-virt/axconfig.toml +++ b/platform/riscv64-qemu-virt/axconfig.toml @@ -10,7 +10,7 @@ package = "axplat-riscv64-qemu-virt" # str # [plat] # Number of CPUs. -cpu-num = 1 # uint +cpu-num = 4 # uint # Base address of the whole physical memory. phys-memory-base = 0x8000_0000 # uint # Size of the whole physical memory. (128M) diff --git a/platform/riscv64-qemu-virt/src/lib.rs b/platform/riscv64-qemu-virt/src/lib.rs index e5939e048..66242ea60 100644 --- a/platform/riscv64-qemu-virt/src/lib.rs +++ b/platform/riscv64-qemu-virt/src/lib.rs @@ -28,3 +28,7 @@ pub mod config { "`PACKAGE` field in the configuration does not match the Package name. Please check your configuration file." ); } + +pub fn cpu_count() -> usize { + config::plat::CPU_NUM +} From 7ff53d29b649c5972c2d031e4b55b02468be5d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=99=AF=E5=AE=87?= <2537738252@qq.com> Date: Sun, 30 Nov 2025 20:23:50 +0800 Subject: [PATCH 030/132] riscv64: support arceos vm --- Cargo.lock | 42 ++++++++++--- configs/vms/arceos-riscv64-qemu-smp1.toml | 75 +++++++++++++++++++++++ platform/riscv64-qemu-virt/Cargo.toml | 2 +- platform/riscv64-qemu-virt/axconfig.toml | 4 +- platform/riscv64-qemu-virt/src/boot.rs | 8 ++- scripts/ostool/qemu-riscv64.toml | 2 +- 6 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 configs/vms/arceos-riscv64-qemu-smp1.toml diff --git a/Cargo.lock b/Cargo.lock index 5c303faef..6f2a25442 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -389,7 +389,7 @@ dependencies = [ "page_table_entry", "page_table_multiarch", "percpu", - "riscv", + "riscv 0.14.0", "static_assertions", "tock-registers 0.9.0", "x86", @@ -412,7 +412,7 @@ dependencies = [ "page_table_entry", "page_table_multiarch", "percpu", - "riscv", + "riscv 0.14.0", "static_assertions", "tock-registers 0.9.0", "x86", @@ -925,7 +925,7 @@ dependencies = [ "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "log", - "riscv", + "riscv 0.15.0", "riscv_goldfish", "sbi-rt", ] @@ -939,7 +939,7 @@ dependencies = [ "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "log", - "riscv", + "riscv 0.14.0", "sbi-rt", ] @@ -3829,7 +3829,7 @@ dependencies = [ "log", "memory_addr", "page_table_entry", - "riscv", + "riscv 0.14.0", "x86", ] @@ -4571,7 +4571,20 @@ dependencies = [ "critical-section", "embedded-hal", "paste", - "riscv-macros", + "riscv-macros 0.2.0", + "riscv-pac", +] + +[[package]] +name = "riscv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05cfa3f7b30c84536a9025150d44d26b8e1cc20ddf436448d74cd9591eefb25" +dependencies = [ + "critical-section", + "embedded-hal", + "paste", + "riscv-macros 0.3.0", "riscv-pac", ] @@ -4591,7 +4604,7 @@ dependencies = [ "bit_field", "bitflags 2.10.0", "log", - "riscv", + "riscv 0.14.0", ] [[package]] @@ -4605,6 +4618,17 @@ dependencies = [ "syn 2.0.110", ] +[[package]] +name = "riscv-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d323d13972c1b104aa036bc692cd08b822c8bbf23d79a27c526095856499799" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "riscv-pac" version = "0.2.0" @@ -4633,7 +4657,7 @@ dependencies = [ "memoffset", "memory_addr", "page_table_entry", - "riscv", + "riscv 0.15.0", "riscv-decode", "riscv-h", "rustsbi", @@ -4659,7 +4683,7 @@ dependencies = [ "memoffset", "memory_addr", "page_table_entry", - "riscv", + "riscv 0.14.0", "riscv-decode", "riscv-h", "rustsbi", diff --git a/configs/vms/arceos-riscv64-qemu-smp1.toml b/configs/vms/arceos-riscv64-qemu-smp1.toml new file mode 100644 index 000000000..3c68c0d09 --- /dev/null +++ b/configs/vms/arceos-riscv64-qemu-smp1.toml @@ -0,0 +1,75 @@ +# Vm base info configs +# +[base] +# Guest vm id. +id = 1 +# Guest vm name. +name = "arceos-qemu" +# Virtualization type. +vm_type = 1 +# The number of virtual CPUs. +cpu_num = 1 +# Guest vm physical cpu ids. +phys_cpu_ids = [0] + +# +# Vm kernel configs +# +[kernel] +# The entry point of the kernel image. +entry_point = 0x8020_0000 +# The location of image: "memory" | "fs". +# load from memory. +image_location = "memory" +# The file path of the kernel image. +kernel_path = "path/arceos-riscv64-smp1.bin" +# The load address of the kernel image. +kernel_load_addr = 0x8020_0000 +# The file path of the device tree blob (DTB). +#dtb_path = "path/aarch64-qemu-gicv3.dtb" +# The load address of the device tree blob (DTB). +dtb_load_addr = 0x8220_0000 + +## The file path of the ramdisk image. +# ramdisk_path = "" +## The load address of the ramdisk image. +# ramdisk_load_addr = 0 +## The path of the disk image. +# disk_path = "disk.img" + +# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). +# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. +memory_regions = [ + [0x8000_0000, 0x4000_0000, 0x7, 1], # System RAM 1G MAP_ALLOC +] + +# +# Device specifications +# +[devices] +# Pass-through devices. +passthrough_devices = [ + ["/",], +] + +# Passthrough addresses. +# Base-GPA Length. +passthrough_addresses = [ + #[0x28041000, 0x100_0000] +] + +# Devices that are not desired to be passed through to the guest +excluded_devices = [ + ["/pci@30000000"], +] + +# Emu_devices. +# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. +emu_devices = [ + # ["gppt-gicd", 0x0800_0000, 0x1_0000, 0, 0x21, []], + # ["gppt-gicr", 0x080a_0000, 0x2_0000, 0, 0x20, [1, 0x2_0000, 0]], # 1 vcpu, stride 0x20000, starts with pcpu 0 + # ["gppt-gits", 0x0808_0000, 0x2_0000, 0, 0x22, [0x0808_0000]], # host_gits_base +] + +interrupt_mode = "passthrough" + diff --git a/platform/riscv64-qemu-virt/Cargo.toml b/platform/riscv64-qemu-virt/Cargo.toml index 9cb0d717f..2b9b866c7 100644 --- a/platform/riscv64-qemu-virt/Cargo.toml +++ b/platform/riscv64-qemu-virt/Cargo.toml @@ -15,7 +15,7 @@ smp = ["axplat/smp"] [dependencies] log = "0.4" -riscv = "0.14" +riscv = "0.15.0" sbi-rt = { version = "0.0.3", features = ["legacy"] } riscv_goldfish = { version = "0.1", optional = true } diff --git a/platform/riscv64-qemu-virt/axconfig.toml b/platform/riscv64-qemu-virt/axconfig.toml index 662b05079..ba0e4dbd5 100644 --- a/platform/riscv64-qemu-virt/axconfig.toml +++ b/platform/riscv64-qemu-virt/axconfig.toml @@ -13,8 +13,8 @@ package = "axplat-riscv64-qemu-virt" # str cpu-num = 4 # uint # Base address of the whole physical memory. phys-memory-base = 0x8000_0000 # uint -# Size of the whole physical memory. (128M) -phys-memory-size = 0x800_0000 # uint +# Size of the whole physical memory. (4GB) +phys-memory-size = 0x1_0000_0000 # uint # Base physical address of the kernel image. kernel-base-paddr = 0x8020_0000 # uint # Base virtual address of the kernel image. diff --git a/platform/riscv64-qemu-virt/src/boot.rs b/platform/riscv64-qemu-virt/src/boot.rs index 6fd866a4d..45bc437a2 100644 --- a/platform/riscv64-qemu-virt/src/boot.rs +++ b/platform/riscv64-qemu-virt/src/boot.rs @@ -12,12 +12,18 @@ unsafe fn init_boot_page_table() { unsafe { // 0x0000_0000..0x4000_0000, VRWX_GAD, 1G block BOOT_PT_SV39[0] = (0x0 << 10) | 0xef; - // 0x8000_0000..0xc000_0000, VRWX_GAD, 1G block + // 0x8000_0000..0xc000_0000, VRWX_GAD, 4G block BOOT_PT_SV39[2] = (0x80000 << 10) | 0xef; + BOOT_PT_SV39[3] = (0xC0000 << 10) | 0xef; + BOOT_PT_SV39[4] = (0x100000 << 10) | 0xef; + BOOT_PT_SV39[5] = (0x140000 << 10) | 0xef; // 0xffff_ffc0_0000_0000..0xffff_ffc0_4000_0000, VRWX_GAD, 1G block BOOT_PT_SV39[0x100] = (0x0 << 10) | 0xef; // 0xffff_ffc0_8000_0000..0xffff_ffc0_c000_0000, VRWX_GAD, 1G block BOOT_PT_SV39[0x102] = (0x80000 << 10) | 0xef; + BOOT_PT_SV39[0x103] = (0xC0000 << 10) | 0xef; + BOOT_PT_SV39[0x104] = (0x100000 << 10) | 0xef; + BOOT_PT_SV39[0x105] = (0x140000 << 10) | 0xef; } } diff --git a/scripts/ostool/qemu-riscv64.toml b/scripts/ostool/qemu-riscv64.toml index 7106493d5..5fcd64120 100644 --- a/scripts/ostool/qemu-riscv64.toml +++ b/scripts/ostool/qemu-riscv64.toml @@ -15,7 +15,7 @@ args = [ "-append", "root=/dev/vda rw init=/init", "-m", - "8g", + "4g", ] fail_regex = [] success_regex = [] From 57920b9a73ef38b796bd6d4be405c7c3b8eef7ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=99=AF=E5=AE=87?= <2537738252@qq.com> Date: Mon, 8 Dec 2025 13:24:58 +0800 Subject: [PATCH 031/132] riscv64: support linux with no irq --- Cargo.lock | 897 +++++++++++----------- Cargo.toml | 15 +- configs/vms/linux-riscv64-qemu-smp1.toml | 70 ++ kernel/src/vmm/images/mod.rs | 8 +- platform/riscv64-qemu-virt/Cargo.toml | 10 +- platform/riscv64-qemu-virt/axconfig.toml | 22 +- platform/riscv64-qemu-virt/src/boot.rs | 4 +- platform/riscv64-qemu-virt/src/console.rs | 69 +- platform/riscv64-qemu-virt/src/init.rs | 3 +- platform/riscv64-qemu-virt/src/irq.rs | 108 ++- platform/riscv64-qemu-virt/src/mem.rs | 2 +- platform/riscv64-qemu-virt/src/power.rs | 7 +- platform/riscv64-qemu-virt/src/time.rs | 24 +- 13 files changed, 702 insertions(+), 537 deletions(-) create mode 100644 configs/vms/linux-riscv64-qemu-smp1.toml diff --git a/Cargo.lock b/Cargo.lock index 6f2a25442..43ae86ecf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,7 +63,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "version_check", - "zerocopy 0.8.27", + "zerocopy 0.8.28", ] [[package]] @@ -77,9 +77,10 @@ dependencies = [ [[package]] name = "allocator" -version = "0.1.1" -source = "git+https://github.com/arceos-org/allocator.git?tag=v0.1.1#1d5b7a1b4fd8db4c9c9cea4e6012d15d42e2bf40" +version = "0.1.2" +source = "git+https://github.com/arceos-org/allocator.git?tag=v0.1.2#922e72a7e4dfb7ffc4b67d242d38f8361ebd13ac" dependencies = [ + "axerrno 0.1.2", "bitmap-allocator", "cfg-if", "rlsf", @@ -132,22 +133,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -175,12 +176,12 @@ dependencies = [ [[package]] name = "arceos_api" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axalloc", "axconfig", "axdriver", - "axerrno", + "axerrno 0.1.2", "axfeat", "axfs", "axhal", @@ -227,13 +228,12 @@ dependencies = [ [[package]] name = "arm_vcpu" version = "0.1.1" -source = "git+https://github.com/arceos-hypervisor/arm_vcpu?branch=next#b24cc3635c049302ab8d58d3b54007bb5a053a96" dependencies = [ "aarch64-cpu", "axaddrspace", "axdevice_base", - "axerrno", - "axvcpu 0.1.2", + "axerrno 0.1.2", + "axvcpu", "axvisor_api", "log", "numeric-enum-macro", @@ -251,7 +251,7 @@ dependencies = [ "aarch64_sysreg", "axaddrspace", "axdevice_base", - "axerrno", + "axerrno 0.1.2", "axvisor_api", "bitmaps", "log", @@ -269,7 +269,7 @@ dependencies = [ "aarch64_sysreg", "axaddrspace", "axdevice_base", - "axerrno", + "axerrno 0.1.2", "axvisor_api", "bitmaps", "log", @@ -298,7 +298,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -319,7 +319,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06b129114ab36be728ef11dd6540559c30deb6332378157d22bdc0aae6803a63" dependencies = [ - "axerrno", + "axerrno 0.1.2", "bit_field", "bitflags 2.10.0", "cfg-if", @@ -336,14 +336,26 @@ dependencies = [ [[package]] name = "axalloc" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "allocator", - "axerrno", + "axerrno 0.1.2", "cfg-if", "kspin", "log", "memory_addr", + "strum 0.27.2", +] + +[[package]] +name = "axbacktrace" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf9566516f5d799b2f791a6ec5af57eec87d17624346f7c876fa006b922c99e6" +dependencies = [ + "cfg-if", + "log", + "spin 0.10.0", ] [[package]] @@ -372,12 +384,14 @@ dependencies = [ "axconfig-gen", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] name = "axcpu" version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e09bc1235e3da45e942b50f47812f8397ad84cb490264bf914c65ac44e6f8b1d" dependencies = [ "aarch64-cpu", "cfg-if", @@ -398,11 +412,10 @@ dependencies = [ [[package]] name = "axcpu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e09bc1235e3da45e942b50f47812f8397ad84cb490264bf914c65ac44e6f8b1d" +version = "0.3.0" dependencies = [ "aarch64-cpu", + "axbacktrace", "cfg-if", "lazyinit", "linkme", @@ -427,7 +440,7 @@ dependencies = [ "arm_vgic 0.1.0 (git+https://github.com/arceos-hypervisor/arm_vgic.git)", "axaddrspace", "axdevice_base", - "axerrno", + "axerrno 0.1.2", "axvmconfig", "cfg-if", "log", @@ -443,7 +456,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c43baf33ed4790ffd3365c4ca027a1e3d1c2b6058f4605b67bca04cadf48d5" dependencies = [ "axaddrspace", - "axerrno", + "axerrno 0.1.2", "axvmconfig", "cfg-if", "memory_addr", @@ -453,10 +466,9 @@ dependencies = [ [[package]] name = "axdisplay" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axdriver", - "axdriver_display", "axsync", "lazyinit", "log", @@ -465,7 +477,7 @@ dependencies = [ [[package]] name = "axdriver" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "arm-gic-driver", "axalloc", @@ -476,30 +488,31 @@ dependencies = [ "axdriver_net", "axdriver_pci", "axdriver_virtio", - "axerrno", + "axerrno 0.1.2", "axhal", - "axklib 0.2.0 (git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1)", + "axklib", + "axmm", "cfg-if", "crate_interface", "dma-api 0.5.2", - "lazyinit", "log", "memory_addr", "rdif-block", "rdif-intc", "rdrive", + "smallvec", "spin 0.10.0", ] [[package]] name = "axdriver_base" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" [[package]] name = "axdriver_block" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" dependencies = [ "axdriver_base", "log", @@ -508,7 +521,7 @@ dependencies = [ [[package]] name = "axdriver_display" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" dependencies = [ "axdriver_base", ] @@ -516,7 +529,7 @@ dependencies = [ [[package]] name = "axdriver_net" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" dependencies = [ "axdriver_base", "log", @@ -526,7 +539,7 @@ dependencies = [ [[package]] name = "axdriver_pci" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" dependencies = [ "virtio-drivers", ] @@ -534,32 +547,44 @@ dependencies = [ [[package]] name = "axdriver_virtio" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" dependencies = [ "axdriver_base", "axdriver_block", + "log", "virtio-drivers", ] [[package]] name = "axerrno" -version = "0.1.0" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a88b1fa2ce97a6ff4ce31ba9fda3065730ca4d77a1ba50dec000fc04f1fb686" +dependencies = [ + "axerrno 0.2.2", + "log", +] + +[[package]] +name = "axerrno" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66ccd41dd4ef364e2385901a5c2a3adea974a41eccb2529c1f24e4c8bc93d834" +checksum = "f961d2868582a092fb1e71b90c16cc6f2cbbe7bb5fa7e4bd6fe61d882ce6bb34" dependencies = [ "log", + "strum 0.27.2", ] [[package]] name = "axfeat" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axalloc", + "axbacktrace", "axdriver", "axfs", "axhal", - "axklib-impl", "axlog", "axruntime", "axsync", @@ -570,22 +595,22 @@ dependencies = [ [[package]] name = "axfs" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axdriver", "axdriver_block", - "axerrno", + "axerrno 0.1.2", "axfs_devfs", "axfs_ramfs", "axfs_vfs", "axio", - "axns", "axsync", "cap_access", "cfg-if", "fatfs", "lazyinit", "log", + "scope-local", ] [[package]] @@ -616,7 +641,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcba2006898d7879d456a9c34b9c9460cb536f5bf69d1d5d7d0e0f19f073368d" dependencies = [ - "axerrno", + "axerrno 0.1.2", "bitflags 2.10.0", "log", ] @@ -624,20 +649,18 @@ dependencies = [ [[package]] name = "axhal" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ - "aarch64-cpu", "axalloc", "axconfig", - "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axlog", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axcpu 0.3.0", + "axplat 0.3.0", "axplat-aarch64-qemu-virt", "axplat-loongarch64-qemu-virt", - "axplat-riscv64-qemu-virt 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axplat-riscv64-qemu-virt 0.3.0 (git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03)", "axplat-x86-pc", "cfg-if", - "heapless 0.8.0", + "heapless 0.9.2", "kernel_guard", "lazyinit", "linkme", @@ -650,9 +673,9 @@ dependencies = [ [[package]] name = "axhvc" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axhvc.git#e4ca6f919b9900f35e42b9b30863550a50432220" +source = "git+https://github.com/arceos-hypervisor/axhvc.git#8b07150208803180bffc6187e6373b7ead013054" dependencies = [ - "axerrno", + "axerrno 0.2.2", "numeric-enum-macro", ] @@ -662,13 +685,13 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30aa258a37c25c5e9d3ff45ec80e728ff7c499586e3e40719daf7908f10fd5bd" dependencies = [ - "axerrno", + "axerrno 0.1.2", ] [[package]] name = "axipi" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axconfig", "axhal", @@ -681,39 +704,17 @@ dependencies = [ [[package]] name = "axklib" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" -dependencies = [ - "axerrno", - "memory_addr", - "trait-ffi", -] - -[[package]] -name = "axklib" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axklib#44b5acacc4e2380a97ac9a231a024f4ee42adb06" +source = "git+https://github.com/arceos-hypervisor/axklib.git#7c0fc0588f978f7d75bb94f4e07477776ed37887" dependencies = [ - "axerrno", - "axplat 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "axerrno 0.1.2", "memory_addr", "trait-ffi", ] -[[package]] -name = "axklib-impl" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" -dependencies = [ - "axhal", - "axklib 0.2.0 (git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1)", - "axmm", - "trait-ffi", -] - [[package]] name = "axlog" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "cfg-if", "crate_interface", @@ -724,10 +725,11 @@ dependencies = [ [[package]] name = "axmm" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axalloc", - "axerrno", + "axconfig", + "axerrno 0.1.2", "axhal", "kspin", "lazyinit", @@ -739,11 +741,10 @@ dependencies = [ [[package]] name = "axnet" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axdriver", - "axdriver_net", - "axerrno", + "axerrno 0.1.2", "axhal", "axio", "axsync", @@ -755,22 +756,12 @@ dependencies = [ "spin 0.10.0", ] -[[package]] -name = "axns" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" -dependencies = [ - "crate_interface", - "lazyinit", -] - [[package]] name = "axplat" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4de04c54b63bf2ca1ff202733d2516da49d7779649cdb2f9c4ecf22909e6810" +source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" dependencies = [ - "axplat-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "axplat-macros 0.1.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", "bitflags 2.10.0", "const-str", "crate_interface", @@ -781,16 +772,17 @@ dependencies = [ [[package]] name = "axplat" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" +version = "0.3.0" +source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ - "axplat-macros 0.1.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axplat-macros 0.1.0 (git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03)", "bitflags 2.10.0", "const-str", "crate_interface", "handler_table", "kspin", "memory_addr", + "percpu", ] [[package]] @@ -802,8 +794,8 @@ dependencies = [ "any-uart", "arm-gic-driver", "axconfig-macros", - "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axcpu 0.2.2", + "axplat 0.2.0", "fdt-parser", "heapless 0.8.0", "lazyinit", @@ -822,16 +814,16 @@ dependencies = [ [[package]] name = "axplat-aarch64-dyn" -version = "0.3.0" -source = "git+https://github.com/arceos-hypervisor/axplat-aarch64-dyn?tag=v0.3.3#de5acd47da8540f46c81e1eef69af65965404dbd" +version = "0.4.0" +source = "git+https://github.com/arceos-hypervisor/axplat-aarch64-dyn.git?tag=v0.4.0#05d5acd43d925807496255a9b9e1aa2d272bb591" dependencies = [ "aarch64-cpu", "aarch64-cpu-ext", "any-uart", "arm-gic-driver", "axconfig-macros", - "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axcpu 0.3.0", + "axplat 0.3.0", "fdt-parser", "heapless 0.8.0", "lazyinit", @@ -850,15 +842,15 @@ dependencies = [ [[package]] name = "axplat-aarch64-peripherals" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" +version = "0.3.0" +source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ "aarch64-cpu", "arm-gic-driver", "arm_pl011", "arm_pl031", - "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axcpu 0.3.0", + "axplat 0.3.0", "int_ratio", "kspin", "lazyinit", @@ -869,12 +861,12 @@ dependencies = [ [[package]] name = "axplat-aarch64-qemu-virt" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" +version = "0.3.0" +source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ "axconfig-macros", - "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axcpu 0.3.0", + "axplat 0.3.0", "axplat-aarch64-peripherals", "log", "page_table_entry", @@ -882,29 +874,28 @@ dependencies = [ [[package]] name = "axplat-loongarch64-qemu-virt" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" +version = "0.3.0" +source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ "axconfig-macros", - "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axcpu 0.3.0", + "axplat 0.3.0", "kspin", "lazyinit", "log", "loongArch64", - "ns16550a", "page_table_entry", + "uart_16550", ] [[package]] name = "axplat-macros" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90dfaee06a112fe4f810c60af1a86bc080af2172185b491cacc307b84dff748" +source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -914,43 +905,51 @@ source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0# dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] name = "axplat-riscv64-qemu-virt" -version = "0.2.0" +version = "0.3.0" dependencies = [ "axconfig-macros", - "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axcpu 0.3.0", + "axplat 0.3.0", + "kspin", + "lazyinit", "log", "riscv 0.15.0", "riscv_goldfish", + "riscv_plic", "sbi-rt", + "uart_16550", ] [[package]] name = "axplat-riscv64-qemu-virt" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" +version = "0.3.0" +source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ "axconfig-macros", - "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axcpu 0.3.0", + "axplat 0.3.0", + "kspin", + "lazyinit", "log", "riscv 0.14.0", + "riscv_plic", "sbi-rt", + "uart_16550", ] [[package]] name = "axplat-x86-pc" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" +version = "0.3.0" +source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ "axconfig-macros", - "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axcpu 0.3.0", + "axplat 0.3.0", "bitflags 2.10.0", "heapless 0.9.2", "int_ratio", @@ -971,8 +970,8 @@ name = "axplat-x86-qemu-q35" version = "0.1.0" dependencies = [ "axconfig-macros", - "axcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", + "axcpu 0.3.0", + "axplat 0.3.0", "bitflags 2.10.0", "heapless 0.9.2", "int_ratio", @@ -989,6 +988,17 @@ dependencies = [ "x86_rtc", ] +[[package]] +name = "axpoll" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06a0cf7cffe0e87c338f41d59bd0a05fa75cf9c4799b4107fa0fc6cf27d8d23" +dependencies = [ + "bitflags 2.10.0", + "linux-raw-sys 0.11.0", + "spin 0.10.0", +] + [[package]] name = "axruntime" version = "0.1.0" @@ -997,16 +1007,16 @@ dependencies = [ "axconfig", "axdisplay", "axdriver", - "axerrno", + "axerrno 0.1.2", "axfs", "axhal", "axipi", "axlog", "axmm", "axnet", - "axplat 0.2.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", - "axplat-aarch64-dyn 0.3.0 (git+https://github.com/arceos-hypervisor/axplat-aarch64-dyn?tag=v0.3.3)", - "axplat-riscv64-qemu-virt 0.2.0", + "axplat 0.3.0", + "axplat-aarch64-dyn 0.4.0", + "axplat-riscv64-qemu-virt 0.3.0", "axplat-x86-qemu-q35", "axtask", "cfg-if", @@ -1030,10 +1040,10 @@ dependencies = [ [[package]] name = "axstd" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "arceos_api", - "axerrno", + "axerrno 0.1.2", "axfeat", "axio", "kspin", @@ -1043,9 +1053,10 @@ dependencies = [ [[package]] name = "axsync" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axtask", + "event-listener", "kspin", "lock_api", ] @@ -1053,37 +1064,41 @@ dependencies = [ [[package]] name = "axtask" version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/arceos.git?tag=hv-0.4.1#781fd8e99456c531568ccdc906312e7ef98d2fbf" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axconfig", + "axerrno 0.1.2", "axhal", + "axpoll", "axsched", "cfg-if", "cpumask", "crate_interface", + "event-listener", + "extern-trait", + "futures-util", "kernel_guard", "kspin", "lazyinit", "log", "memory_addr", "percpu", - "timer_list", ] [[package]] name = "axum" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" +checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" dependencies = [ "axum-core", "bytes", "form_urlencoded", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.8.0", + "hyper 1.8.1", "hyper-util", "itoa", "matchit", @@ -1111,7 +1126,7 @@ checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "mime", @@ -1122,24 +1137,12 @@ dependencies = [ "tracing", ] -[[package]] -name = "axvcpu" -version = "0.1.1" -dependencies = [ - "axaddrspace", - "axerrno", - "axvisor_api", - "memory_addr", - "percpu", -] - [[package]] name = "axvcpu" version = "0.1.2" -source = "git+https://github.com/arceos-hypervisor/axvcpu.git?branch=next#343ec3ccf99a86fb9c67a7b0372e9b7a745f0640" dependencies = [ "axaddrspace", - "axerrno", + "axerrno 0.1.2", "axvisor_api", "memory_addr", "percpu", @@ -1156,19 +1159,20 @@ dependencies = [ "axconfig", "axdevice", "axdevice_base", - "axerrno", + "axerrno 0.2.2", "axhvc", "axruntime", "axstd", - "axvcpu 0.1.1", + "axvcpu", "axvisor_api", - "axvm 0.1.0 (git+https://github.com/liulog/axvm.git?branch=next)", + "axvm", "bitflags 2.10.0", "byte-unit", "cfg-if", "cpumask", "crate_interface", "driver", + "extern-trait", "fdt-parser", "kernel_guard", "kspin", @@ -1184,7 +1188,7 @@ dependencies = [ "rdif-intc", "rdrive", "spin 0.9.8", - "syn 2.0.110", + "syn 2.0.111", "timer_list", "toml 0.9.8", "vm-fdt 0.3.0 (git+https://github.com/bullhh/vm-fdt.git)", @@ -1211,45 +1215,20 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.110", -] - -[[package]] -name = "axvm" -version = "0.1.0" -dependencies = [ - "arm_vcpu", - "arm_vgic 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "axaddrspace", - "axdevice", - "axdevice_base", - "axerrno", - "axvcpu 0.1.1", - "axvmconfig", - "cfg-if", - "cpumask", - "log", - "memory_addr", - "page_table_entry", - "page_table_multiarch", - "percpu", - "riscv_vcpu 0.1.2 (git+https://github.com/liulog/riscv_vcpu.git?branch=master)", - "spin 0.9.8", - "x86_vcpu", + "syn 2.0.111", ] [[package]] name = "axvm" version = "0.1.0" -source = "git+https://github.com/liulog/axvm.git?branch=next#22ca8ff823b1433c6f2f15bc99810fe0b4a2ccac" dependencies = [ "arm_vcpu", "arm_vgic 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "axaddrspace", "axdevice", "axdevice_base", - "axerrno", - "axvcpu 0.1.1", + "axerrno 0.2.2", + "axvcpu", "axvmconfig", "cfg-if", "cpumask", @@ -1258,7 +1237,7 @@ dependencies = [ "page_table_entry", "page_table_multiarch", "percpu", - "riscv_vcpu 0.1.2 (git+https://github.com/liulog/riscv_vcpu.git?branch=master)", + "riscv_vcpu", "spin 0.9.8", "x86_vcpu", ] @@ -1268,7 +1247,7 @@ name = "axvmconfig" version = "0.1.0" source = "git+https://github.com/arceos-hypervisor/axvmconfig.git?branch=next#5a8b64a47510b17da71e54cabbdf8c999ba2e2c9" dependencies = [ - "axerrno", + "axerrno 0.1.2", "clap", "enumerable", "env_logger", @@ -1291,7 +1270,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -1308,7 +1287,7 @@ checksum = "e585a01076fee271c5aabcf36212acb349fb3e638561d842fffa8ca013f4fdd8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -1405,9 +1384,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" dependencies = [ "borsh-derive", "cfg_aliases", @@ -1415,15 +1394,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -1434,11 +1413,12 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-unit" -version = "5.1.6" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174" +checksum = "8c6d47a4e2961fb8721bcfc54feae6455f2f64e7054f9bc67e875f0e77f4c58d" dependencies = [ "rust_decimal", + "schemars", "serde", "utf8-width", ] @@ -1479,9 +1459,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "camino" @@ -1581,9 +1561,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.45" +version = "1.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" dependencies = [ "find-msvc-tools", "shlex", @@ -1611,14 +1591,14 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-link 0.2.1", + "windows-link", ] [[package]] name = "clap" -version = "4.5.51" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", @@ -1626,9 +1606,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -1645,7 +1625,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -1683,6 +1663,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console" version = "0.16.1" @@ -1708,6 +1697,12 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "451d0640545a0553814b4c646eb549343561618838e9b42495f466131fe3ad49" +[[package]] +name = "const_fn" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e" + [[package]] name = "convert_case" version = "0.7.1" @@ -1738,9 +1733,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -1778,14 +1773,14 @@ checksum = "70272a03a2cef15589bac05d3d15c023752f5f8f2da8be977d983a9d9e6250fb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] name = "crc" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] @@ -1896,7 +1891,7 @@ checksum = "9a49d5cd78b1c748184d41407b14a58af8403c13328ff2b9f49b0a418c24e3ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -1981,7 +1976,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -1994,7 +1989,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2005,7 +2000,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2016,7 +2011,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2048,7 +2043,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2087,7 +2082,7 @@ dependencies = [ "convert_case 0.7.1", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2114,7 +2109,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2157,7 +2152,7 @@ dependencies = [ name = "driver" version = "0.1.0" dependencies = [ - "axklib 0.2.0 (git+https://github.com/arceos-hypervisor/axklib)", + "axklib", "log", "phytium-mci", "rdif-block", @@ -2230,7 +2225,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2242,7 +2237,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2263,7 +2258,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2274,7 +2269,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2295,7 +2290,7 @@ dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2348,6 +2343,27 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "pin-project-lite", +] + +[[package]] +name = "extern-trait" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8f5038f5845165d06fe1453fe4130ad546d3314818bbda57e208e7b0cffe08" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -2383,9 +2399,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "fitimage" @@ -2515,7 +2531,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -2621,7 +2637,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.3.1", + "http 1.4.0", "indexmap", "slab", "tokio", @@ -2676,9 +2692,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heapless" @@ -2725,12 +2741,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -2752,7 +2767,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -2763,7 +2778,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "pin-project-lite", ] @@ -2812,16 +2827,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744436df46f0bde35af3eda22aeaba453aada65d8f1c171cd8a5f59030bd69f" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", "h2 0.4.12", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "httparse", "httpdate", @@ -2839,8 +2854,8 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", - "hyper 1.8.0", + "http 1.4.0", + "hyper 1.8.1", "hyper-util", "rustls", "rustls-pki-types", @@ -2871,7 +2886,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.8.0", + "hyper 1.8.1", "hyper-util", "native-tls", "tokio", @@ -2881,18 +2896,18 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", - "hyper 1.8.0", + "hyper 1.8.1", "ipnet", "libc", "percent-encoding", @@ -3039,12 +3054,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", ] [[package]] @@ -3071,15 +3086,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" +checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c" dependencies = [ "darling 0.20.11", "indoc", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -3156,7 +3171,7 @@ checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -3184,9 +3199,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -3201,7 +3216,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -3213,7 +3228,7 @@ dependencies = [ "bitflags 2.10.0", "prettyplease", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -3351,7 +3366,7 @@ checksum = "e5cec0ec4228b4853bb129c84dbf093a27e6c7a20526da046defc334a1b017f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -3478,16 +3493,17 @@ dependencies = [ [[package]] name = "memory_addr" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4054cba279515fa87761b101d857333ce06391dbe8f18a11347204a7111416" +checksum = "b1f0625c50adb5f6aaf47f05cae3c4dbc13a74c659241b06c4576f3d7e1da940" [[package]] name = "memory_set" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d47cbc25a4d00427f9070fd768eaf907f19c903fb72b547b19db2d56b9408e" +checksum = "50a49ecd4114cf87f7e442ec5dd03bd590e7094541f987057310dbb32a6341ad" dependencies = [ + "axerrno 0.1.2", "memory_addr", ] @@ -3588,12 +3604,6 @@ dependencies = [ name = "nop" version = "0.1.0" -[[package]] -name = "ns16550a" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cd8abe9e54bce27659507b94f355c9334378ab15da332b6986b3583ebf7228" - [[package]] name = "num" version = "0.4.3" @@ -3728,7 +3738,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -3760,9 +3770,9 @@ dependencies = [ [[package]] name = "ostool" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1f90223d51db91747d032ef49378e725a6e42a3d53da62c246abc73fe35cb7" +checksum = "e7c843408786c18af02d88ba7de7d25f4ff94a137b238a43ce1653c7eceb4b7b" dependencies = [ "anyhow", "byte-unit", @@ -3809,9 +3819,9 @@ dependencies = [ [[package]] name = "page_table_entry" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba811ef8ca8fb33d776e128624cb4fe25c9804cab96f83b822d4322431e6dd5a" +checksum = "dda9891ec368fda90e4b2cc36592b4881073e25a339fe7e3eddd811f0cf6bf18" dependencies = [ "aarch64-cpu", "bitflags 2.10.0", @@ -3821,11 +3831,10 @@ dependencies = [ [[package]] name = "page_table_multiarch" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cb76e21ce462270afd83b331599d5b83f876c2a98c0a70382b20d73e1da6be" +checksum = "9fa11a21844255e14aa6688ef0eafb058d7be19338633024fb59417f1bfb07f8" dependencies = [ - "bitmaps", "log", "memory_addr", "page_table_entry", @@ -3853,7 +3862,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3913,7 +3922,7 @@ checksum = "8a9f4cc54a2e471ff72f1499461ba381ad4eae9cbd60d29c258545b995e406e0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -3962,7 +3971,7 @@ dependencies = [ "prettyplease", "quote", "spin 0.10.0", - "syn 2.0.110", + "syn 2.0.111", "thiserror 2.0.17", ] @@ -3975,7 +3984,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -4032,7 +4041,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.27", + "zerocopy 0.8.28", ] [[package]] @@ -4042,7 +4051,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -4073,7 +4082,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -4162,9 +4171,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -4260,7 +4269,7 @@ dependencies = [ "itertools", "lru", "paste", - "strum", + "strum 0.26.3", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.0", @@ -4376,7 +4385,7 @@ checksum = "eab3105c9af32e901a2adc7d920b39ff8b6ee0f6f0b7dfdeaf18f306ec12606f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -4405,7 +4414,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -4515,10 +4524,10 @@ dependencies = [ "futures-core", "futures-util", "h2 0.4.12", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.8.0", + "hyper 1.8.1", "hyper-rustls", "hyper-tls 0.6.0", "hyper-util", @@ -4572,20 +4581,19 @@ dependencies = [ "embedded-hal", "paste", "riscv-macros 0.2.0", - "riscv-pac", + "riscv-pac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "riscv" version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05cfa3f7b30c84536a9025150d44d26b8e1cc20ddf436448d74cd9591eefb25" +source = "git+https://github.com/liulog/riscv.git?branch=master#9722c430a2f624c35f4be64ba36c6a64e4f1096a" dependencies = [ "critical-section", "embedded-hal", "paste", "riscv-macros 0.3.0", - "riscv-pac", + "riscv-pac 0.2.0 (git+https://github.com/liulog/riscv.git?branch=master)", ] [[package]] @@ -4615,18 +4623,17 @@ checksum = "e8c4aa1ea1af6dcc83a61be12e8189f9b293c3ba5a487778a4cd89fb060fdbbc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] name = "riscv-macros" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d323d13972c1b104aa036bc692cd08b822c8bbf23d79a27c526095856499799" +source = "git+https://github.com/liulog/riscv.git?branch=master#9722c430a2f624c35f4be64ba36c6a64e4f1096a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -4635,6 +4642,11 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" +[[package]] +name = "riscv-pac" +version = "0.2.0" +source = "git+https://github.com/liulog/riscv.git?branch=master#9722c430a2f624c35f4be64ba36c6a64e4f1096a" + [[package]] name = "riscv_goldfish" version = "0.1.1" @@ -4642,38 +4654,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07aac72f95e774476db82916d79f2d303191310393830573c1ab5c821b21660a" [[package]] -name = "riscv_vcpu" -version = "0.1.2" +name = "riscv_plic" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e701d1c6ea06c35a19cb80d213fab87d264798f9bac0aed2730c0e86d297394a" dependencies = [ - "axaddrspace", - "axerrno", - "axvcpu 0.1.1", - "axvisor_api", - "bit_field", - "bitflags 2.10.0", - "cfg-if", - "crate_interface", - "log", - "memoffset", - "memory_addr", - "page_table_entry", - "riscv 0.15.0", - "riscv-decode", - "riscv-h", - "rustsbi", - "sbi-rt", - "sbi-spec", - "tock-registers 0.9.0", + "tock-registers 0.10.1", ] [[package]] name = "riscv_vcpu" version = "0.1.2" -source = "git+https://github.com/liulog/riscv_vcpu.git?branch=master#6a82b377ce526d927e48ffde90f245c6a558a841" dependencies = [ "axaddrspace", - "axerrno", - "axvcpu 0.1.1", + "axerrno 0.1.2", + "axvcpu", "axvisor_api", "bit_field", "bitflags 2.10.0", @@ -4683,7 +4678,7 @@ dependencies = [ "memoffset", "memory_addr", "page_table_entry", - "riscv 0.14.0", + "riscv 0.15.0", "riscv-decode", "riscv-h", "rustsbi", @@ -4758,8 +4753,8 @@ dependencies = [ [[package]] name = "rockchip-pm" -version = "0.2.0" -source = "git+https://github.com/drivercraft/rockchip-pm.git#cb2afdf9f3cb5fd05f8e003e65de38e50d480cd5" +version = "0.4.0" +source = "git+https://github.com/drivercraft/rockchip-pm.git#5bb4dfbfcea41826e13115efb4023d670ef6b034" dependencies = [ "bare-test-macros", "dma-api 0.5.2", @@ -4849,9 +4844,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" dependencies = [ "web-time", "zeroize", @@ -4887,7 +4882,7 @@ checksum = "a71347da9582cc6b6f3652c7d2c06516c9555690b3738ecdff7e84297f4e17fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -4957,7 +4952,17 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.110", + "syn 2.0.111", +] + +[[package]] +name = "scope-local" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "231e441739fbe668e91acf8440a0b1ac83c73faec7a6d984190ee25354c2ec9a" +dependencies = [ + "percpu", + "spin 0.9.8", ] [[package]] @@ -5073,7 +5078,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -5084,7 +5089,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -5119,7 +5124,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -5154,22 +5159,21 @@ dependencies = [ [[package]] name = "serialport" -version = "4.8.1" +version = "4.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21f60a586160667241d7702c420fc223939fb3c0bb8d3fac84f78768e8970dee" +checksum = "2acaf3f973e8616d7ceac415f53fc60e190b2a686fbcf8d27d0256c741c5007b" dependencies = [ "bitflags 2.10.0", "cfg-if", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "io-kit-sys", "libudev", "mach2", "nix", - "quote", "scopeguard", "unescaper", - "windows-sys 0.52.0", + "winapi", ] [[package]] @@ -5223,9 +5227,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -5299,9 +5303,9 @@ dependencies = [ [[package]] name = "somehal" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9b180e1abae1127bd99ecee566c3906d8c0f854cb1c442b945afe911e6eb71" +checksum = "13b5f763b9ab0ce9efd2d8eba9e5b457f93f6426ede68435fe9567cf7681f29d" dependencies = [ "aarch64-cpu", "aarch64-cpu-ext", @@ -5377,7 +5381,16 @@ version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros", + "strum_macros 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", ] [[package]] @@ -5390,7 +5403,19 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.110", + "syn 2.0.111", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] @@ -5425,9 +5450,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.110" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -5457,7 +5482,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -5534,9 +5559,9 @@ dependencies = [ [[package]] name = "tftpd" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abbee6dbf19d025600720ce174a82b10895e486179e0206b91f14b6fcf6cb4bf" +checksum = "dee6f92408c23beea910b60784f6dc323b488c477bf6fa88aff8083675eda26c" [[package]] name = "thiserror" @@ -5564,7 +5589,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -5575,7 +5600,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -5685,7 +5710,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -5831,15 +5856,15 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" dependencies = [ "bitflags 2.10.0", "bytes", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "http-range-header", @@ -5871,9 +5896,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -5882,9 +5907,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", ] @@ -5899,7 +5924,7 @@ dependencies = [ "lenient_semver", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -6005,9 +6030,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unit-prefix" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" [[package]] name = "untrusted" @@ -6034,12 +6059,12 @@ dependencies = [ [[package]] name = "ureq-proto" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2" +checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" dependencies = [ "base64 0.22.1", - "http 1.3.1", + "http 1.4.0", "httparse", "log", ] @@ -6064,9 +6089,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8-width" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" +checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" [[package]] name = "utf8_iter" @@ -6160,9 +6185,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -6173,9 +6198,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -6186,9 +6211,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6196,31 +6221,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -6275,9 +6300,9 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -6288,7 +6313,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -6299,15 +6324,9 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.1" @@ -6316,22 +6335,13 @@ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-registry" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" -dependencies = [ - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-result" -version = "0.3.4" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.1.3", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -6340,16 +6350,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -6358,7 +6359,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -6403,7 +6404,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -6443,7 +6444,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -6594,9 +6595,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -6658,12 +6659,13 @@ dependencies = [ [[package]] name = "x86_64" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f042214de98141e9c8706e8192b73f56494087cc55ebec28ce10f26c5c364ae" +checksum = "f7841fa0098ceb15c567d93d3fae292c49e10a7662b4936d5f6a9728594555ba" dependencies = [ "bit_field", "bitflags 2.10.0", + "const_fn", "rustversion", "volatile", ] @@ -6686,8 +6688,8 @@ checksum = "873e097d52e94c31be3f0175a9f8d6f2edbc77d7e2f8e6995427df9c08b30a2b" dependencies = [ "axaddrspace", "axdevice_base", - "axerrno", - "axvcpu 0.1.1", + "axerrno 0.1.2", + "axvcpu", "axvisor_api", "bit_field", "bitflags 2.10.0", @@ -6713,7 +6715,7 @@ checksum = "2556c62649a277ccf1c3c34c740be87bbde5f8dab0b20fcdcf4c2cd7bb6e7302" dependencies = [ "axaddrspace", "axdevice_base", - "axerrno", + "axerrno 0.1.2", "axvisor_api", "bit", "log", @@ -6748,6 +6750,7 @@ dependencies = [ "chrono", "clap", "colored", + "flate2", "jkconfig", "ostool", "reqwest 0.12.24", @@ -6755,8 +6758,10 @@ dependencies = [ "serde", "serde_json", "sha2", + "tar", "tokio", "toml 0.9.8", + "zerocopy 0.8.28", ] [[package]] @@ -6778,7 +6783,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", "synstructure", ] @@ -6794,11 +6799,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" dependencies = [ - "zerocopy-derive 0.8.27", + "zerocopy-derive 0.8.28", ] [[package]] @@ -6809,18 +6814,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] [[package]] @@ -6840,7 +6845,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", "synstructure", ] @@ -6880,5 +6885,5 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.111", ] diff --git a/Cargo.toml b/Cargo.toml index 5d03514bc..8256b2926 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ axklib = {git = "https://github.com/arceos-hypervisor/axklib.git"} axruntime = {path = "modules/axruntime"} axfs = {path = "modules/axfs"} axvcpu = "0.1" -axvm = {git = "https://github.com/liulog/axvm.git", branch = "next"} +axvm = {git = "https://github.com/arceos-hypervisor/axvm.git", branch = "next"} # System independent crates provided by ArceOS, these crates could be imported by remote url. axerrno = "0.2" @@ -92,8 +92,21 @@ axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branc [patch.crates-io] axvcpu = {path="crates/axvcpu"} axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "next"} +riscv = {git = "https://github.com/liulog/riscv.git", branch = "master"} [patch."https://github.com/arceos-org/arceos"] axconfig = {path = "modules/axconfig"} axruntime = {path = "modules/axruntime"} axfs = {path = "modules/axfs"} + +[patch."https://github.com/arceos-hypervisor/arm_vcpu.git"] +arm_vcpu = {path = "crates/arm_vcpu"} + +[patch."https://github.com/arceos-hypervisor/axvm.git"] +axvm = {path = "crates/axvm"} + +[patch."https://github.com/liulog/riscv_vcpu.git"] +riscv_vcpu = {path = "crates/riscv_vcpu"} + +[patch."https://github.com/arceos-org/axcpu.git"] +axcpu = {path = "crates/axcpu"} diff --git a/configs/vms/linux-riscv64-qemu-smp1.toml b/configs/vms/linux-riscv64-qemu-smp1.toml new file mode 100644 index 000000000..e849a54d9 --- /dev/null +++ b/configs/vms/linux-riscv64-qemu-smp1.toml @@ -0,0 +1,70 @@ +# Vm base info configs +# +[base] +# Guest vm id. +id = 1 +# Guest vm name. +name = "linux-qemu" +# Virtualization type. +vm_type = 1 +# The number of virtual CPUs. +cpu_num = 1 +# Guest vm physical cpu sets. +phys_cpu_ids = [0] + +# +# Vm kernel configs +# +[kernel] +# The entry point of the kernel image. +entry_point = 0x8020_0000 +# The location of image: "memory" | "fs". +# load from memory. +image_location = "memory" +# The file path of the kernel image. +# kernel_path = "linux-6.6.62.bin" +kernel_path = "tmp/Image" +# The load address of the kernel image. +kernel_load_addr = 0x8020_0000 +# The file path of the device tree blob (DTB). +#dtb_path = "tmp/linux-aarch64-qemu-smp1.dtb" +# The load address of the device tree blob (DTB). +dtb_load_addr = 0x8000_0000 + +# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). +# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. +memory_regions = [ + [0x8000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL +] + +# +# Device specifications +# +[devices] +# Pass-through devices. +# Name Base-Ipa Base-Pa Length Alloc-Irq. +passthrough_devices = [ + ["/"], + #["/timer"], +] + +# Passthrough addresses. +# Base-GPA Length. +passthrough_addresses = [ + #[0x28041000, 0x100_0000] +] + +# Devices that are not desired to be passed through to the guest +excluded_devices = [ + # ["/gic-v3"], +] + +# Emu_devices. +# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. +emu_devices = [ + # ["gppt-gicd", 0x0800_0000, 0x1_0000, 0, 0x21, []], + # ["gppt-gicr", 0x080a_0000, 0x2_0000, 0, 0x20, [1, 0x2_0000, 0]], # 1 vcpu, stride 0x20000, starts with pcpu 0 + # ["gppt-gits", 0x0808_0000, 0x2_0000, 0, 0x22, [0x0808_0000]], # host_gits_base +] + +interrupt_mode = "passthrough" diff --git a/kernel/src/vmm/images/mod.rs b/kernel/src/vmm/images/mod.rs index 23addd730..612871a06 100644 --- a/kernel/src/vmm/images/mod.rs +++ b/kernel/src/vmm/images/mod.rs @@ -111,7 +111,13 @@ impl ImageLoader { self.vm.clone(), ); } else { - info!("dtb_load_gpa not provided"); + if let Some(buffer) = vm_imags.dtb { + #[cfg(target_arch = "riscv64")] + load_vm_image_from_memory(buffer, self.dtb_load_gpa.unwrap(), self.vm.clone()) + .expect("Failed to load DTB images"); + } else { + info!("dtb_load_gpa not provided"); + } } // Load BIOS image diff --git a/platform/riscv64-qemu-virt/Cargo.toml b/platform/riscv64-qemu-virt/Cargo.toml index 2b9b866c7..b1f338178 100644 --- a/platform/riscv64-qemu-virt/Cargo.toml +++ b/platform/riscv64-qemu-virt/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2024" name = "axplat-riscv64-qemu-virt" -version = "0.2.0" +version = "0.3.0" [features] default = [ @@ -14,13 +14,17 @@ rtc = ["riscv_goldfish"] smp = ["axplat/smp"] [dependencies] +kspin = "0.1" +lazyinit = "0.2" log = "0.4" riscv = "0.15.0" -sbi-rt = { version = "0.0.3", features = ["legacy"] } riscv_goldfish = { version = "0.1", optional = true } +riscv_plic = "0.2" +sbi-rt = { version = "0.0.3", features = ["legacy"] } +uart_16550 = "0.4.0" axconfig-macros = "0.2" -axcpu = "0.2" +axcpu = { workspace = true } axplat = { workspace = true } [package.metadata.docs.rs] diff --git a/platform/riscv64-qemu-virt/axconfig.toml b/platform/riscv64-qemu-virt/axconfig.toml index ba0e4dbd5..f541767c3 100644 --- a/platform/riscv64-qemu-virt/axconfig.toml +++ b/platform/riscv64-qemu-virt/axconfig.toml @@ -81,4 +81,24 @@ ipi-irq = "0x8000_0000_0000_0001" # uint # compatible = "google,goldfish-rtc"; # }; # RTC (goldfish) Address -rtc-paddr = 0x10_1000 # uint \ No newline at end of file +rtc-paddr = 0x10_1000 # uint + +# plic@c000000 { +# phandle = <0x03>; +# riscv,ndev = <0x5f>; +# reg = <0x00 0xc000000 0x00 0x600000>; +# interrupts-extended = <0x02 0x0b 0x02 0x09>; +# interrupt-controller; +# compatible = "sifive,plic-1.0.0\0riscv,plic0"; +# }; +plic-paddr = 0x0c00_0000 # uint + +# serial@10000000 { +# interrupts = <0x0a>; +# interrupt-parent = <0x03>; +# clock-frequency = "\08@"; +# reg = <0x00 0x10000000 0x00 0x100>; +# compatible = "ns16550a"; +# }; +uart-paddr = 0x1000_0000 # uint +uart-irq = 0x0a # uint \ No newline at end of file diff --git a/platform/riscv64-qemu-virt/src/boot.rs b/platform/riscv64-qemu-virt/src/boot.rs index 45bc437a2..12a7fc473 100644 --- a/platform/riscv64-qemu-virt/src/boot.rs +++ b/platform/riscv64-qemu-virt/src/boot.rs @@ -73,9 +73,7 @@ unsafe extern "C" fn _start() -> ! { /// The earliest entry point for secondary CPUs. #[cfg(feature = "smp")] #[unsafe(naked)] -#[unsafe(no_mangle)] -#[unsafe(link_section = ".text.boot")] -unsafe extern "C" fn _start_secondary() -> ! { +pub(crate) unsafe extern "C" fn _start_secondary() -> ! { // a0 = hartid // a1 = SP core::arch::naked_asm!(" diff --git a/platform/riscv64-qemu-virt/src/console.rs b/platform/riscv64-qemu-virt/src/console.rs index 7c305a8b4..63f4799ad 100644 --- a/platform/riscv64-qemu-virt/src/console.rs +++ b/platform/riscv64-qemu-virt/src/console.rs @@ -1,22 +1,19 @@ -use axplat::mem::{VirtAddr, virt_to_phys}; +use axplat::console::ConsoleIf; +use kspin::SpinNoIrq; +use lazyinit::LazyInit; +use uart_16550::MmioSerialPort; -/// The maximum number of bytes that can be read at once. -const MAX_RW_SIZE: usize = 256; +use crate::config::{devices::UART_PADDR, plat::PHYS_VIRT_OFFSET}; -/// Tries to write bytes to the console from input u8 slice. -/// Returns the number of bytes written. -fn try_write_bytes(bytes: &[u8]) -> usize { - sbi_rt::console_write(sbi_rt::Physical::new( - // A maximum of 256 bytes can be written at a time - // to prevent SBI from disabling IRQs for too long. - bytes.len().min(MAX_RW_SIZE), - virt_to_phys(VirtAddr::from_ptr_of(bytes.as_ptr())).as_usize(), - 0, - )) - .value -} +static UART: LazyInit> = LazyInit::new(); -use axplat::console::ConsoleIf; +pub(crate) fn init_early() { + UART.init_once({ + let mut uart = unsafe { MmioSerialPort::new(UART_PADDR + PHYS_VIRT_OFFSET) }; + uart.init(); + SpinNoIrq::new(uart) + }); +} struct ConsoleIfImpl; @@ -24,28 +21,34 @@ struct ConsoleIfImpl; impl ConsoleIf for ConsoleIfImpl { /// Writes bytes to the console from input u8 slice. fn write_bytes(bytes: &[u8]) { - let mut write_len = 0; - let mut buf = [0; MAX_RW_SIZE]; - while write_len < bytes.len() { - let n = buf.len().min(bytes.len() - write_len); - if n == 0 { - break; + for &c in bytes { + let mut uart = UART.lock(); + match c { + b'\n' => { + uart.send_raw(b'\r'); + uart.send_raw(b'\n'); + } + c => uart.send_raw(c), } - // `bytes` can be from user space, copy it into a kernel buffer - // to correctly use `virt_to_phys`. - buf[..n].copy_from_slice(&bytes[write_len..write_len + n]); - write_len += try_write_bytes(&buf[..n]); } } /// Reads bytes from the console into the given mutable slice. /// Returns the number of bytes read. fn read_bytes(bytes: &mut [u8]) -> usize { - sbi_rt::console_read(sbi_rt::Physical::new( - bytes.len().min(MAX_RW_SIZE), - virt_to_phys(VirtAddr::from_mut_ptr_of(bytes.as_mut_ptr())).as_usize(), - 0, - )) - .value + let mut uart = UART.lock(); + for (i, byte) in bytes.iter_mut().enumerate() { + match uart.try_receive() { + Ok(c) => *byte = c, + Err(_) => return i, + } + } + bytes.len() } -} + + /// Returns the IRQ number for the console, if applicable. + #[cfg(feature = "irq")] + fn irq_num() -> Option { + Some(crate::config::devices::UART_IRQ) + } +} \ No newline at end of file diff --git a/platform/riscv64-qemu-virt/src/init.rs b/platform/riscv64-qemu-virt/src/init.rs index 7090f415d..3f191049b 100644 --- a/platform/riscv64-qemu-virt/src/init.rs +++ b/platform/riscv64-qemu-virt/src/init.rs @@ -9,6 +9,7 @@ impl InitIf for InitIfImpl { /// early console, clocking). fn init_early(_cpu_id: usize, _mbi: usize) { axcpu::init::init_trap(); + crate::console::init_early(); crate::time::init_early(); } @@ -36,4 +37,4 @@ impl InitIf for InitIfImpl { crate::irq::init_percpu(); crate::time::init_percpu(); } -} +} \ No newline at end of file diff --git a/platform/riscv64-qemu-virt/src/irq.rs b/platform/riscv64-qemu-virt/src/irq.rs index 250c4c855..9350f2703 100644 --- a/platform/riscv64-qemu-virt/src/irq.rs +++ b/platform/riscv64-qemu-virt/src/irq.rs @@ -1,10 +1,20 @@ -//! TODO: PLIC - -use axplat::irq::{HandlerTable, IpiTarget, IrqHandler, IrqIf}; -use core::sync::atomic::{AtomicPtr, Ordering}; +use core::{ + num::NonZeroU32, + ptr::NonNull, + sync::atomic::{AtomicPtr, Ordering}, +}; + +use axplat::{ + irq::{HandlerTable, IpiTarget, IrqHandler, IrqIf}, + percpu::this_cpu_id, +}; +use kspin::SpinNoIrq; use riscv::register::sie; +use riscv_plic::Plic; use sbi_rt::HartMask; +use crate::config::{devices::PLIC_PADDR, plat::PHYS_VIRT_OFFSET}; + /// `Interrupt` bit in `scause` pub(super) const INTC_IRQ_BASE: usize = 1 << (usize::BITS - 1); @@ -27,6 +37,25 @@ pub const MAX_IRQ_COUNT: usize = 1024; static IRQ_HANDLER_TABLE: HandlerTable = HandlerTable::new(); +static PLIC: SpinNoIrq = SpinNoIrq::new(unsafe { + Plic::new(NonNull::new((PHYS_VIRT_OFFSET + PLIC_PADDR) as *mut _).unwrap()) +}); + +fn this_context() -> usize { + let hart_id = this_cpu_id(); + hart_id * 2 + 1 // supervisor context +} + +pub(super) fn init_percpu() { + // enable soft interrupts, timer interrupts, and external interrupts + unsafe { + sie::set_ssoft(); + sie::set_stimer(); + sie::set_sext(); + } + PLIC.lock().init_by_context(this_context()); +} + macro_rules! with_cause { ($cause: expr, @S_TIMER => $timer_op: expr, @S_SOFT => $ipi_op: expr, @S_EXT => $ext_op: expr, @EX_IRQ => $plic_op: expr $(,)?) => { match $cause { @@ -39,30 +68,46 @@ macro_rules! with_cause { $plic_op } else { // Other CPU-side interrupts - panic!("Unknown IRQ cause: {}", other); + panic!("Unknown IRQ cause: {other}"); } } } }; } -pub(super) fn init_percpu() { - // enable soft interrupts, timer interrupts, and external interrupts - unsafe { - sie::set_ssoft(); - sie::set_stimer(); - sie::set_sext(); - } -} - struct IrqIfImpl; #[impl_plat_interface] impl IrqIf for IrqIfImpl { /// Enables or disables the given IRQ. - fn set_enable(irq: usize, _enabled: bool) { - // TODO: set enable in PLIC - warn!("set_enable is not implemented for IRQ {}", irq); + fn set_enable(irq: usize, enabled: bool) { + with_cause!( + irq, + @S_TIMER => { + unsafe { + if enabled { + sie::set_stimer(); + } else { + sie::clear_stimer(); + } + } + }, + @S_SOFT => {}, + @S_EXT => {}, + @EX_IRQ => { + let Some(irq) = NonZeroU32::new(irq as _) else { + return; + }; + trace!("PLIC set enable: {irq} {enabled}"); + let mut plic = PLIC.lock(); + if enabled { + plic.set_priority(irq, 6); + plic.enable(irq, this_context()); + } else { + plic.disable(irq, this_context()); + } + } + ); } /// Registers an IRQ handler for the given IRQ. @@ -92,7 +137,7 @@ impl IrqIf for IrqIfImpl { Self::set_enable(irq, true); true } else { - warn!("register handler for External IRQ {} failed", irq); + warn!("register handler for External IRQ {irq} failed"); false } } @@ -126,7 +171,7 @@ impl IrqIf for IrqIfImpl { warn!("External IRQ should be got from PLIC, not scause"); None }, - @EX_IRQ => IRQ_HANDLER_TABLE.unregister_handler(irq) + @EX_IRQ => IRQ_HANDLER_TABLE.unregister_handler(irq).inspect(|_| Self::set_enable(irq, false)) ) } @@ -135,7 +180,7 @@ impl IrqIf for IrqIfImpl { /// It is called by the common interrupt handler. It should look up in the /// IRQ handler table and calls the corresponding handler. If necessary, it /// also acknowledges the interrupt controller after handling. - fn handle(irq: usize) { + fn handle(irq: usize) -> Option { with_cause!( irq, @S_TIMER => { @@ -145,6 +190,7 @@ impl IrqIf for IrqIfImpl { // SAFETY: The handler is guaranteed to be a valid function pointer. unsafe { core::mem::transmute::<*mut (), IrqHandler>(handler)() }; } + Some(irq) }, @S_SOFT => { trace!("IRQ: IPI"); @@ -153,12 +199,18 @@ impl IrqIf for IrqIfImpl { // SAFETY: The handler is guaranteed to be a valid function pointer. unsafe { core::mem::transmute::<*mut (), IrqHandler>(handler)() }; } + Some(irq) }, @S_EXT => { - // TODO: get IRQ number from PLIC - if !IRQ_HANDLER_TABLE.handle(0) { - warn!("Unhandled IRQ {}", 0); - } + let mut plic = PLIC.lock(); + let Some(irq) = plic.claim(this_context()) else { + debug!("Spurious external IRQ"); + return None; + }; + trace!("IRQ: external {irq}"); + IRQ_HANDLER_TABLE.handle(irq.get() as usize); + plic.complete(this_context(), irq); + Some(irq.get() as usize) }, @EX_IRQ => { unreachable!("Device-side IRQs should be handled by triggering the External Interrupt."); @@ -172,13 +224,13 @@ impl IrqIf for IrqIfImpl { IpiTarget::Current { cpu_id } => { let res = sbi_rt::send_ipi(HartMask::from_mask_base(1 << cpu_id, 0)); if res.is_err() { - warn!("send_ipi failed: {:?}", res); + warn!("send_ipi failed: {res:?}"); } } IpiTarget::Other { cpu_id } => { let res = sbi_rt::send_ipi(HartMask::from_mask_base(1 << cpu_id, 0)); if res.is_err() { - warn!("send_ipi failed: {:?}", res); + warn!("send_ipi failed: {res:?}"); } } IpiTarget::AllExceptCurrent { cpu_id, cpu_num } => { @@ -186,11 +238,11 @@ impl IrqIf for IrqIfImpl { if i != cpu_id { let res = sbi_rt::send_ipi(HartMask::from_mask_base(1 << i, 0)); if res.is_err() { - warn!("send_ipi_all_others failed: {:?}", res); + warn!("send_ipi_all_others failed: {res:?}"); } } } } } } -} +} \ No newline at end of file diff --git a/platform/riscv64-qemu-virt/src/mem.rs b/platform/riscv64-qemu-virt/src/mem.rs index 13000df04..09930ff14 100644 --- a/platform/riscv64-qemu-virt/src/mem.rs +++ b/platform/riscv64-qemu-virt/src/mem.rs @@ -55,4 +55,4 @@ impl MemIf for MemIfImpl { crate::config::plat::KERNEL_ASPACE_SIZE, ) } -} +} \ No newline at end of file diff --git a/platform/riscv64-qemu-virt/src/power.rs b/platform/riscv64-qemu-virt/src/power.rs index c4a87924b..bf6c5aaf5 100644 --- a/platform/riscv64-qemu-virt/src/power.rs +++ b/platform/riscv64-qemu-virt/src/power.rs @@ -12,14 +12,11 @@ impl PowerIf for PowerImpl { #[cfg(feature = "smp")] fn cpu_boot(cpu_id: usize, stack_top_paddr: usize) { use axplat::mem::{va, virt_to_phys}; - unsafe extern "C" { - fn _start_secondary(); - } if sbi_rt::probe_extension(sbi_rt::Hsm).is_unavailable() { warn!("HSM SBI extension is not supported for current SEE."); return; } - let entry = virt_to_phys(va!(_start_secondary as usize)); + let entry = virt_to_phys(va!(crate::boot::_start_secondary as *const () as usize)); sbi_rt::hart_start(cpu_id, entry.as_usize(), stack_top_paddr); } @@ -32,4 +29,4 @@ impl PowerIf for PowerImpl { axcpu::asm::halt(); } } -} +} \ No newline at end of file diff --git a/platform/riscv64-qemu-virt/src/time.rs b/platform/riscv64-qemu-virt/src/time.rs index a8c84e1dc..121d1e544 100644 --- a/platform/riscv64-qemu-virt/src/time.rs +++ b/platform/riscv64-qemu-virt/src/time.rs @@ -1,8 +1,6 @@ use riscv::register::time; -use axplat::time::TimeIf; - -const NANOS_PER_SEC: u64 = 1_000_000_000; +use axplat::time::{NANOS_PER_SEC, TimeIf}; const NANOS_PER_TICK: u64 = NANOS_PER_SEC / crate::config::devices::TIMER_FREQUENCY as u64; /// RTC wall time offset in nanoseconds at monotonic time base. @@ -10,19 +8,16 @@ static mut RTC_EPOCHOFFSET_NANOS: u64 = 0; pub(super) fn init_early() { #[cfg(feature = "rtc")] - use crate::config::devices::RTC_PADDR; + use crate::config::{devices::RTC_PADDR, plat::PHYS_VIRT_OFFSET}; #[cfg(feature = "rtc")] if RTC_PADDR != 0 { - use axplat::mem::{PhysAddr, pa, phys_to_virt}; - use riscv_goldfish::Rtc; - const GOLDFISH_BASE: PhysAddr = pa!(RTC_PADDR); // Get the current time in microseconds since the epoch (1970-01-01) from the riscv RTC. // Subtract the timer ticks to get the actual time when ArceOS was booted. let epoch_time_nanos = - Rtc::new(phys_to_virt(GOLDFISH_BASE).as_usize()).get_unix_timestamp() * 1_000_000_000; + Rtc::new(RTC_PADDR + PHYS_VIRT_OFFSET).get_unix_timestamp() * 1_000_000_000; unsafe { RTC_EPOCHOFFSET_NANOS = @@ -40,11 +35,6 @@ struct TimeIfImpl; #[impl_plat_interface] impl TimeIf for TimeIfImpl { - /// Returns the IRQ number for the timer interrupt. - fn irq_num() -> usize { - crate::config::devices::TIMER_IRQ - } - /// Returns the current clock time in hardware ticks. fn current_ticks() -> u64 { time::read() as u64 @@ -65,6 +55,12 @@ impl TimeIf for TimeIfImpl { unsafe { RTC_EPOCHOFFSET_NANOS } } + /// Returns the IRQ number for the timer interrupt. + #[cfg(feature = "irq")] + fn irq_num() -> usize { + crate::config::devices::TIMER_IRQ + } + /// Set a one-shot timer. /// /// A timer interrupt will be triggered at the specified monotonic time deadline (in nanoseconds). @@ -72,4 +68,4 @@ impl TimeIf for TimeIfImpl { fn set_oneshot_timer(deadline_ns: u64) { sbi_rt::set_timer(Self::nanos_to_ticks(deadline_ns)); } -} +} \ No newline at end of file From 10cd9a5c056f119f8a1c3e458e8521037221004e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=99=AF=E5=AE=87?= <2537738252@qq.com> Date: Wed, 24 Dec 2025 18:42:28 +0800 Subject: [PATCH 032/132] support riscv64 --- Cargo.lock | 263 +++++++++++++++++--------- Cargo.toml | 24 ++- kernel/Cargo.toml | 4 + kernel/build.rs | 2 + kernel/src/hal/arch/riscv64/api.rs | 6 + kernel/src/hal/arch/riscv64/mod.rs | 21 +- kernel/src/vmm/vcpus.rs | 10 +- modules/axruntime/src/lib.rs | 5 + platform/riscv64-qemu-virt/Cargo.toml | 3 +- platform/riscv64-qemu-virt/src/irq.rs | 14 +- platform/riscv64-qemu-virt/src/lib.rs | 6 +- 11 files changed, 255 insertions(+), 103 deletions(-) create mode 100644 kernel/src/hal/arch/riscv64/api.rs diff --git a/Cargo.lock b/Cargo.lock index 43ae86ecf..efbc09399 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,7 +179,7 @@ version = "0.2.0" source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axalloc", - "axconfig", + "axconfig 0.1.0", "axdriver", "axerrno 0.1.2", "axfeat", @@ -188,8 +188,8 @@ dependencies = [ "axio", "axlog", "axruntime", - "axsync", - "axtask", + "axsync 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", + "axtask 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", ] [[package]] @@ -228,12 +228,13 @@ dependencies = [ [[package]] name = "arm_vcpu" version = "0.1.1" +source = "git+https://github.com/arceos-hypervisor/arm_vcpu?branch=next#b24cc3635c049302ab8d58d3b54007bb5a053a96" dependencies = [ "aarch64-cpu", "axaddrspace", "axdevice_base", "axerrno 0.1.2", - "axvcpu", + "axvcpu 0.1.2 (git+https://github.com/arceos-hypervisor/axvcpu.git?branch=next)", "axvisor_api", "log", "numeric-enum-macro", @@ -290,6 +291,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063" +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + [[package]] name = "async-trait" version = "0.1.89" @@ -316,13 +323,14 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axaddrspace" version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06b129114ab36be728ef11dd6540559c30deb6332378157d22bdc0aae6803a63" dependencies = [ + "assert_matches", "axerrno 0.1.2", + "axin", "bit_field", "bitflags 2.10.0", "cfg-if", + "lazy_static", "lazyinit", "log", "memory_addr", @@ -330,13 +338,13 @@ dependencies = [ "numeric-enum-macro", "page_table_entry", "page_table_multiarch", + "spin 0.10.0", "x86", ] [[package]] name = "axalloc" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "allocator", "axerrno 0.1.2", @@ -365,6 +373,13 @@ dependencies = [ "axconfig-macros", ] +[[package]] +name = "axconfig" +version = "0.2.0" +dependencies = [ + "axconfig-macros", +] + [[package]] name = "axconfig-gen" version = "0.2.0" @@ -403,7 +418,7 @@ dependencies = [ "page_table_entry", "page_table_multiarch", "percpu", - "riscv 0.14.0", + "riscv", "static_assertions", "tock-registers 0.9.0", "x86", @@ -425,7 +440,7 @@ dependencies = [ "page_table_entry", "page_table_multiarch", "percpu", - "riscv 0.14.0", + "riscv", "static_assertions", "tock-registers 0.9.0", "x86", @@ -435,7 +450,6 @@ dependencies = [ [[package]] name = "axdevice" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axdevice.git#60558bb25214c2030651726beddae088d8a1cd8e" dependencies = [ "arm_vgic 0.1.0 (git+https://github.com/arceos-hypervisor/arm_vgic.git)", "axaddrspace", @@ -446,6 +460,25 @@ dependencies = [ "log", "memory_addr", "range-alloc", + "riscv_vplic 0.1.0 (git+https://github.com/liulog/riscv_vplic.git)", + "spin 0.9.8", +] + +[[package]] +name = "axdevice" +version = "0.1.0" +source = "git+https://github.com/liulog/axdevice?branch=master#50ecc20e3d4fe82480b041c769bad18845ce80c5" +dependencies = [ + "arm_vgic 0.1.0 (git+https://github.com/arceos-hypervisor/arm_vgic.git)", + "axaddrspace", + "axdevice_base", + "axerrno 0.1.2", + "axvmconfig", + "cfg-if", + "log", + "memory_addr", + "range-alloc", + "riscv_vplic 0.1.0 (git+https://github.com/liulog/riscv_vplic.git)", "spin 0.9.8", ] @@ -469,7 +502,7 @@ version = "0.2.0" source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axdriver", - "axsync", + "axsync 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", "lazyinit", "log", ] @@ -477,11 +510,10 @@ dependencies = [ [[package]] name = "axdriver" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "arm-gic-driver", "axalloc", - "axconfig", + "axconfig 0.2.0", "axdriver_base", "axdriver_block", "axdriver_display", @@ -491,7 +523,7 @@ dependencies = [ "axerrno 0.1.2", "axhal", "axklib", - "axmm", + "axmm 0.2.0", "cfg-if", "crate_interface", "dma-api 0.5.2", @@ -507,12 +539,12 @@ dependencies = [ [[package]] name = "axdriver_base" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" [[package]] name = "axdriver_block" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" dependencies = [ "axdriver_base", "log", @@ -521,7 +553,7 @@ dependencies = [ [[package]] name = "axdriver_display" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" dependencies = [ "axdriver_base", ] @@ -529,7 +561,7 @@ dependencies = [ [[package]] name = "axdriver_net" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" dependencies = [ "axdriver_base", "log", @@ -539,7 +571,7 @@ dependencies = [ [[package]] name = "axdriver_pci" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" dependencies = [ "virtio-drivers", ] @@ -547,7 +579,7 @@ dependencies = [ [[package]] name = "axdriver_virtio" version = "0.1.2" -source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#cada10715c1f4c08b63d5d60345d2bedbc39bcd8" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" dependencies = [ "axdriver_base", "axdriver_block", @@ -587,15 +619,14 @@ dependencies = [ "axhal", "axlog", "axruntime", - "axsync", - "axtask", + "axsync 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", + "axtask 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", "kspin", ] [[package]] name = "axfs" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axdriver", "axdriver_block", @@ -604,7 +635,7 @@ dependencies = [ "axfs_ramfs", "axfs_vfs", "axio", - "axsync", + "axsync 0.2.0", "cap_access", "cfg-if", "fatfs", @@ -649,10 +680,9 @@ dependencies = [ [[package]] name = "axhal" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axalloc", - "axconfig", + "axconfig 0.2.0", "axcpu 0.3.0", "axplat 0.3.0", "axplat-aarch64-qemu-virt", @@ -679,6 +709,18 @@ dependencies = [ "numeric-enum-macro", ] +[[package]] +name = "axin" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db62cb7067e33d432df247b32ee450ae267cb16319c8c5de247381c3652a639" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "axio" version = "0.1.1" @@ -693,7 +735,7 @@ name = "axipi" version = "0.2.0" source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ - "axconfig", + "axconfig 0.1.0", "axhal", "kspin", "lazyinit", @@ -722,13 +764,28 @@ dependencies = [ "log", ] +[[package]] +name = "axmm" +version = "0.2.0" +dependencies = [ + "axalloc", + "axconfig 0.2.0", + "axerrno 0.1.2", + "axhal", + "kspin", + "lazyinit", + "log", + "memory_addr", + "memory_set", +] + [[package]] name = "axmm" version = "0.2.0" source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ "axalloc", - "axconfig", + "axconfig 0.1.0", "axerrno 0.1.2", "axhal", "kspin", @@ -747,8 +804,8 @@ dependencies = [ "axerrno 0.1.2", "axhal", "axio", - "axsync", - "axtask", + "axsync 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", + "axtask 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", "cfg-if", "lazyinit", "log", @@ -915,10 +972,11 @@ dependencies = [ "axconfig-macros", "axcpu 0.3.0", "axplat 0.3.0", + "axvisor_api", "kspin", "lazyinit", "log", - "riscv 0.15.0", + "riscv", "riscv_goldfish", "riscv_plic", "sbi-rt", @@ -936,7 +994,7 @@ dependencies = [ "kspin", "lazyinit", "log", - "riscv 0.14.0", + "riscv", "riscv_plic", "sbi-rt", "uart_16550", @@ -1004,7 +1062,7 @@ name = "axruntime" version = "0.1.0" dependencies = [ "axalloc", - "axconfig", + "axconfig 0.1.0", "axdisplay", "axdriver", "axerrno 0.1.2", @@ -1012,13 +1070,13 @@ dependencies = [ "axhal", "axipi", "axlog", - "axmm", + "axmm 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", "axnet", "axplat 0.3.0", "axplat-aarch64-dyn 0.4.0", "axplat-riscv64-qemu-virt 0.3.0", "axplat-x86-qemu-q35", - "axtask", + "axtask 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", "cfg-if", "chrono", "crate_interface", @@ -1050,23 +1108,43 @@ dependencies = [ "lock_api", ] +[[package]] +name = "axsync" +version = "0.2.0" +dependencies = [ + "axtask 0.2.0", + "event-listener", + "kspin", + "lock_api", +] + [[package]] name = "axsync" version = "0.2.0" source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ - "axtask", + "axtask 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", "event-listener", "kspin", "lock_api", ] +[[package]] +name = "axtask" +version = "0.2.0" +dependencies = [ + "axerrno 0.1.2", + "axhal", + "cfg-if", + "log", +] + [[package]] name = "axtask" version = "0.2.0" source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" dependencies = [ - "axconfig", + "axconfig 0.1.0", "axerrno 0.1.2", "axhal", "axpoll", @@ -1148,6 +1226,18 @@ dependencies = [ "percpu", ] +[[package]] +name = "axvcpu" +version = "0.1.2" +source = "git+https://github.com/arceos-hypervisor/axvcpu.git?branch=next#343ec3ccf99a86fb9c67a7b0372e9b7a745f0640" +dependencies = [ + "axaddrspace", + "axerrno 0.1.2", + "axvisor_api", + "memory_addr", + "percpu", +] + [[package]] name = "axvisor" version = "0.0.0" @@ -1156,14 +1246,14 @@ dependencies = [ "anyhow", "arm-gic-driver", "axaddrspace", - "axconfig", - "axdevice", + "axconfig 0.1.0", + "axdevice 0.1.0 (git+https://github.com/liulog/axdevice?branch=master)", "axdevice_base", "axerrno 0.2.2", "axhvc", "axruntime", "axstd", - "axvcpu", + "axvcpu 0.1.2", "axvisor_api", "axvm", "bitflags 2.10.0", @@ -1187,6 +1277,8 @@ dependencies = [ "quote", "rdif-intc", "rdrive", + "riscv_vcpu", + "riscv_vplic 0.1.0", "spin 0.9.8", "syn 2.0.111", "timer_list", @@ -1197,8 +1289,6 @@ dependencies = [ [[package]] name = "axvisor_api" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7233b2a1338dc06a80e2779b572b4df02007ea128ef7b235b66fc3eeac0ca6" dependencies = [ "axaddrspace", "axvisor_api_proc", @@ -1209,8 +1299,6 @@ dependencies = [ [[package]] name = "axvisor_api_proc" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a64eb4410ae8357ac8c01c2fb201e57d7aeeb5436ed4d0f5774bfa11cc5902" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1225,10 +1313,10 @@ dependencies = [ "arm_vcpu", "arm_vgic 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "axaddrspace", - "axdevice", + "axdevice 0.1.0 (git+https://github.com/liulog/axdevice?branch=master)", "axdevice_base", "axerrno 0.2.2", - "axvcpu", + "axvcpu 0.1.2", "axvmconfig", "cfg-if", "cpumask", @@ -1245,7 +1333,6 @@ dependencies = [ [[package]] name = "axvmconfig" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axvmconfig.git?branch=next#5a8b64a47510b17da71e54cabbdf8c999ba2e2c9" dependencies = [ "axerrno 0.1.2", "clap", @@ -2340,7 +2427,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3820,8 +3907,6 @@ dependencies = [ [[package]] name = "page_table_entry" version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda9891ec368fda90e4b2cc36592b4881073e25a339fe7e3eddd811f0cf6bf18" dependencies = [ "aarch64-cpu", "bitflags 2.10.0", @@ -3832,13 +3917,11 @@ dependencies = [ [[package]] name = "page_table_multiarch" version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa11a21844255e14aa6688ef0eafb058d7be19338633024fb59417f1bfb07f8" dependencies = [ "log", "memory_addr", "page_table_entry", - "riscv 0.14.0", + "riscv", "x86", ] @@ -4166,7 +4249,7 @@ dependencies = [ "once_cell", "socket2 0.6.1", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -4580,20 +4663,8 @@ dependencies = [ "critical-section", "embedded-hal", "paste", - "riscv-macros 0.2.0", - "riscv-pac 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "riscv" -version = "0.15.0" -source = "git+https://github.com/liulog/riscv.git?branch=master#9722c430a2f624c35f4be64ba36c6a64e4f1096a" -dependencies = [ - "critical-section", - "embedded-hal", - "paste", - "riscv-macros 0.3.0", - "riscv-pac 0.2.0 (git+https://github.com/liulog/riscv.git?branch=master)", + "riscv-macros", + "riscv-pac", ] [[package]] @@ -4612,7 +4683,7 @@ dependencies = [ "bit_field", "bitflags 2.10.0", "log", - "riscv 0.14.0", + "riscv", ] [[package]] @@ -4626,27 +4697,12 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "riscv-macros" -version = "0.3.0" -source = "git+https://github.com/liulog/riscv.git?branch=master#9722c430a2f624c35f4be64ba36c6a64e4f1096a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "riscv-pac" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" -[[package]] -name = "riscv-pac" -version = "0.2.0" -source = "git+https://github.com/liulog/riscv.git?branch=master#9722c430a2f624c35f4be64ba36c6a64e4f1096a" - [[package]] name = "riscv_goldfish" version = "0.1.1" @@ -4668,7 +4724,7 @@ version = "0.1.2" dependencies = [ "axaddrspace", "axerrno 0.1.2", - "axvcpu", + "axvcpu 0.1.2", "axvisor_api", "bit_field", "bitflags 2.10.0", @@ -4678,7 +4734,7 @@ dependencies = [ "memoffset", "memory_addr", "page_table_entry", - "riscv 0.15.0", + "riscv", "riscv-decode", "riscv-h", "rustsbi", @@ -4687,6 +4743,35 @@ dependencies = [ "tock-registers 0.9.0", ] +[[package]] +name = "riscv_vplic" +version = "0.1.0" +dependencies = [ + "axaddrspace", + "axdevice_base", + "axerrno 0.1.2", + "axvisor_api", + "bitmaps", + "log", + "riscv-h", + "spin 0.9.8", +] + +[[package]] +name = "riscv_vplic" +version = "0.1.0" +source = "git+https://github.com/liulog/riscv_vplic.git#5ed1fb6bb06d85675ba8f60813d32744b512b23d" +dependencies = [ + "axaddrspace", + "axdevice_base", + "axerrno 0.1.2", + "axvisor_api", + "bitmaps", + "log", + "riscv-h", + "spin 0.9.8", +] + [[package]] name = "rk3568_clk" version = "0.1.0" @@ -4815,7 +4900,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5554,7 +5639,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6689,7 +6774,7 @@ dependencies = [ "axaddrspace", "axdevice_base", "axerrno 0.1.2", - "axvcpu", + "axvcpu 0.1.2", "axvisor_api", "bit_field", "bitflags 2.10.0", diff --git a/Cargo.toml b/Cargo.toml index 8256b2926..83702541c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,16 +91,24 @@ axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branc [patch.crates-io] axvcpu = {path="crates/axvcpu"} -axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "next"} -riscv = {git = "https://github.com/liulog/riscv.git", branch = "master"} +axvmconfig = {path = "crates/axvmconfig"} +# riscv = {git = "https://github.com/liulog/riscv.git", branch = "master"} +axcpu = {path = "crates/axcpu"} +axvisor_api = {path = "../axvisor_api"} +page_table_entry = { path = "../page_table_multiarch/page_table_entry" } +page_table_multiarch = { path = "../page_table_multiarch/page_table_multiarch", features = ["stage-2"]} +axaddrspace = {path = "crates/axaddrspace"} + +[patch."https://github.com/arceos-hypervisor/axvmconfig.git"] +axvmconfig = {path = "crates/axvmconfig"} [patch."https://github.com/arceos-org/arceos"] axconfig = {path = "modules/axconfig"} axruntime = {path = "modules/axruntime"} axfs = {path = "modules/axfs"} - -[patch."https://github.com/arceos-hypervisor/arm_vcpu.git"] -arm_vcpu = {path = "crates/arm_vcpu"} +axhal = {path = "../arceos/modules/axhal"} +axalloc = {path = "../arceos/modules/axalloc"} +axdriver = {path = "../arceos/modules/axdriver"} [patch."https://github.com/arceos-hypervisor/axvm.git"] axvm = {path = "crates/axvm"} @@ -110,3 +118,9 @@ riscv_vcpu = {path = "crates/riscv_vcpu"} [patch."https://github.com/arceos-org/axcpu.git"] axcpu = {path = "crates/axcpu"} + +[patch."https://github.com/arceos-hypervisor/arm_vcpu.git"] +arm_vcpu = {path = "crates/arm_vcpu"} + +[patch."https://github.com/arceos-hypervisor/axdevice.git"] +axdevice = {git = "https://github.com/liulog/axdevice", branch = "master"} diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index ea99ff32e..066feae1a 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -62,6 +62,10 @@ driver.workspace = true aarch64-cpu-ext = "0.1" arm-gic-driver = {version = "0.15.5", features = ["rdif"]} +[target.'cfg(target_arch = "riscv64")'.dependencies] +riscv_vplic = { path="../crates/riscv_vplic" } +riscv_vcpu = { git = "https://github.com/liulog/riscv_vcpu.git", branch = "master" } + [build-dependencies] axconfig.workspace = true prettyplease = "0.2" diff --git a/kernel/build.rs b/kernel/build.rs index 711c372e9..b5561025e 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -246,6 +246,8 @@ fn main() -> anyhow::Result<()> { "aarch64-generic".to_string() } else if arch == "x86_64" { "x86-qemu-q35".to_string() + } else if arch == "riscv64" { + "riscv64-qemu-virt".to_string() } else { "dummy".to_string() }; diff --git a/kernel/src/hal/arch/riscv64/api.rs b/kernel/src/hal/arch/riscv64/api.rs new file mode 100644 index 000000000..34779dc7c --- /dev/null +++ b/kernel/src/hal/arch/riscv64/api.rs @@ -0,0 +1,6 @@ +#[axvisor_api::api_mod_impl(axvisor_api::arch)] +mod arch_api_impl { + extern fn inject_virtual_interrupt(irq: usize) { + crate::hal::arch::inject_interrupt(irq); + } +} diff --git a/kernel/src/hal/arch/riscv64/mod.rs b/kernel/src/hal/arch/riscv64/mod.rs index 64ff8cd98..ff1f75892 100644 --- a/kernel/src/hal/arch/riscv64/mod.rs +++ b/kernel/src/hal/arch/riscv64/mod.rs @@ -1,9 +1,28 @@ +mod api; pub mod cache; +use crate::vmm::vm_list::get_vm_by_id; +use axaddrspace::{GuestPhysAddr, device::AccessWidth}; +use axvisor_api::vmm::current_vm_id; + pub fn hardware_check() { // TODO: implement hardware checks for RISC-V64 // check page table level like aarch64 } -pub fn inject_interrupt(_vector: u8) {} +pub fn inject_interrupt(irq_id: usize) { + debug!("injecting interrupt id: {}", irq_id); + + // Get the instance of the vplic, and then inject virtual interrupt. + let vplic = get_vm_by_id(current_vm_id()).unwrap().get_devices().find_mmio_dev(GuestPhysAddr::from_usize(axruntime::plic_base())).unwrap(); + + // Calulate the pending register offset and value. + let reg_offset = riscv_vplic::PLIC_PENDING_OFFSET + (irq_id / 32) * 4; + let addr = GuestPhysAddr::from_usize(axruntime::plic_base() + reg_offset); + let width = AccessWidth::Dword; + let val: u32 = 1 << (irq_id % 32); + + // Use a trick write to set the pending bit. + let _ = vplic.handle_write(addr, width, val as _); +} diff --git a/kernel/src/vmm/vcpus.rs b/kernel/src/vmm/vcpus.rs index 22cb47fe4..57e862c4e 100644 --- a/kernel/src/vmm/vcpus.rs +++ b/kernel/src/vmm/vcpus.rs @@ -309,16 +309,17 @@ fn vcpu_on(vm: VMRef, vcpu_id: usize, entry_point: GuestPhysAddr, arg: usize) { vcpu.set_entry(entry_point) .expect("vcpu_on: set_entry failed"); + #[cfg(not(target_arch = "riscv64"))] vcpu.set_gpr(0, arg); #[cfg(target_arch = "riscv64")] { - debug!( + info!( "vcpu_on: vcpu[{}] entry={:x} opaque={:x}", vcpu_id, entry_point, arg ); - vcpu.set_gpr(0, vcpu_id); - vcpu.set_gpr(1, arg); + vcpu.set_gpr(riscv_vcpu::GprIndex::A0 as usize, vcpu_id); + vcpu.set_gpr(riscv_vcpu::GprIndex::A1 as usize, arg); } let vcpu_task = alloc_vcpu_task(&vm, vcpu); @@ -507,7 +508,10 @@ fn vcpu_run() { }); vcpu_on(vm.clone(), target_vcpu_id, entry_point, arg as _); + #[cfg(not(target_arch = "riscv64"))] vcpu.set_gpr(0, 0); + #[cfg(target_arch = "riscv64")] + vcpu.set_gpr(riscv_vcpu::GprIndex::A0 as usize, 0); } AxVCpuExitReason::SystemDown => { warn!("VM[{vm_id}] run VCpu[{vcpu_id}] SystemDown"); diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index 1c4b9096e..4d483fe99 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -340,3 +340,8 @@ pub fn cpu_count() -> usize { cpu_count } + +#[cfg(target_arch = "riscv64")] +pub fn plic_base() -> usize { + axplat_riscv64_qemu_virt::plic_base() +} diff --git a/platform/riscv64-qemu-virt/Cargo.toml b/platform/riscv64-qemu-virt/Cargo.toml index b1f338178..9d07de9e7 100644 --- a/platform/riscv64-qemu-virt/Cargo.toml +++ b/platform/riscv64-qemu-virt/Cargo.toml @@ -17,7 +17,7 @@ smp = ["axplat/smp"] kspin = "0.1" lazyinit = "0.2" log = "0.4" -riscv = "0.15.0" +riscv = "0.14.0" riscv_goldfish = { version = "0.1", optional = true } riscv_plic = "0.2" sbi-rt = { version = "0.0.3", features = ["legacy"] } @@ -26,6 +26,7 @@ uart_16550 = "0.4.0" axconfig-macros = "0.2" axcpu = { workspace = true } axplat = { workspace = true } +axvisor_api = { workspace = true } [package.metadata.docs.rs] targets = ["riscv64gc-unknown-none-elf"] diff --git a/platform/riscv64-qemu-virt/src/irq.rs b/platform/riscv64-qemu-virt/src/irq.rs index 9350f2703..a05f23589 100644 --- a/platform/riscv64-qemu-virt/src/irq.rs +++ b/platform/riscv64-qemu-virt/src/irq.rs @@ -202,14 +202,22 @@ impl IrqIf for IrqIfImpl { Some(irq) }, @S_EXT => { + // TODO: judge irq's ownership before handling (axvisor or any vm). + // Maybe later it will be done by registering all irqs IQR_HANDLER_TABLE. + let mut plic = PLIC.lock(); let Some(irq) = plic.claim(this_context()) else { debug!("Spurious external IRQ"); return None; }; - trace!("IRQ: external {irq}"); - IRQ_HANDLER_TABLE.handle(irq.get() as usize); - plic.complete(this_context(), irq); + drop(plic); + // Inject the virtual interrupt to the guest VM + axvisor_api::arch::inject_virtual_interrupt(irq.get() as usize); + + // trace!("IRQ: external {irq}"); + // IRQ_HANDLER_TABLE.handle(irq.get() as usize); + // Only for irqs that belong to axvisor, complete the IRQ. + // plic.complete(this_context(), irq); Some(irq.get() as usize) }, @EX_IRQ => { diff --git a/platform/riscv64-qemu-virt/src/lib.rs b/platform/riscv64-qemu-virt/src/lib.rs index 66242ea60..067407bb1 100644 --- a/platform/riscv64-qemu-virt/src/lib.rs +++ b/platform/riscv64-qemu-virt/src/lib.rs @@ -29,6 +29,10 @@ pub mod config { ); } -pub fn cpu_count() -> usize { +pub const fn cpu_count() -> usize { config::plat::CPU_NUM } + +pub const fn plic_base() -> usize { + config::devices::PLIC_PADDR +} From 9a393d914cc55d48333d1cadaca8f9e5c11e6ad0 Mon Sep 17 00:00:00 2001 From: YanLien Date: Tue, 6 Jan 2026 17:19:16 +0800 Subject: [PATCH 033/132] Refactor Cargo.toml and module configurations --- .github/workflows/qemu-riscv64.toml | 23 + Cargo.lock | 894 +++++++++++----------- Cargo.toml | 28 +- configs/vms/arceos-riscv64-qemu-smp1.toml | 4 +- configs/vms/linux-riscv64-qemu-smp1.dts | 132 ++++ configs/vms/linux-riscv64-qemu-smp1.toml | 6 +- kernel/Cargo.toml | 2 +- modules/axconfig/src/lib.rs | 2 +- modules/axruntime/Cargo.toml | 2 +- platform/riscv64-qemu-virt/axconfig.toml | 4 +- xtask/src/image.rs | 2 +- 11 files changed, 615 insertions(+), 484 deletions(-) create mode 100644 .github/workflows/qemu-riscv64.toml create mode 100644 configs/vms/linux-riscv64-qemu-smp1.dts diff --git a/.github/workflows/qemu-riscv64.toml b/.github/workflows/qemu-riscv64.toml new file mode 100644 index 000000000..f201c2366 --- /dev/null +++ b/.github/workflows/qemu-riscv64.toml @@ -0,0 +1,23 @@ +args = [ + "-nographic", + "-cpu", + "rv64", + "-machine", + "virt", + "-bios", + "default", + "-smp", + "4", + "-device", + "virtio-blk-device,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", + "-append", + "root=/dev/vda rw init=/init", + "-m", + "8g", +] +fail_regex = [] +success_regex = ["test pass!"] +to_bin = true +uefi = false \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index efbc09399..f3ab8b489 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,7 +63,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "version_check", - "zerocopy 0.8.28", + "zerocopy 0.8.31", ] [[package]] @@ -176,27 +176,27 @@ dependencies = [ [[package]] name = "arceos_api" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "axalloc", - "axconfig 0.1.0", + "axconfig", "axdriver", "axerrno 0.1.2", "axfeat", - "axfs", + "axfs 0.2.0", "axhal", "axio", "axlog", "axruntime", - "axsync 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", - "axtask 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", + "axsync", + "axtask", ] [[package]] name = "arm-gic-driver" -version = "0.15.8" +version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f251a1a74133f802b55eaf5e340107b0024457aa9b2ac3c72074501bfa8509a5" +checksum = "ad71090ed958939b87f6c99e0acd3c476b3a0dbd54f2e2d9aa1ca1f40dc4417e" dependencies = [ "aarch64-cpu", "bitflags 2.10.0", @@ -234,7 +234,7 @@ dependencies = [ "axaddrspace", "axdevice_base", "axerrno 0.1.2", - "axvcpu 0.1.2 (git+https://github.com/arceos-hypervisor/axvcpu.git?branch=next)", + "axvcpu 0.1.2", "axvisor_api", "log", "numeric-enum-macro", @@ -291,12 +291,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063" -[[package]] -name = "assert_matches" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" - [[package]] name = "async-trait" version = "0.1.89" @@ -305,7 +299,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -323,14 +317,13 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axaddrspace" version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06b129114ab36be728ef11dd6540559c30deb6332378157d22bdc0aae6803a63" dependencies = [ - "assert_matches", "axerrno 0.1.2", - "axin", "bit_field", "bitflags 2.10.0", "cfg-if", - "lazy_static", "lazyinit", "log", "memory_addr", @@ -338,13 +331,13 @@ dependencies = [ "numeric-enum-macro", "page_table_entry", "page_table_multiarch", - "spin 0.10.0", "x86", ] [[package]] name = "axalloc" version = "0.2.0" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "allocator", "axerrno 0.1.2", @@ -373,13 +366,6 @@ dependencies = [ "axconfig-macros", ] -[[package]] -name = "axconfig" -version = "0.2.0" -dependencies = [ - "axconfig-macros", -] - [[package]] name = "axconfig-gen" version = "0.2.0" @@ -399,35 +385,13 @@ dependencies = [ "axconfig-gen", "proc-macro2", "quote", - "syn 2.0.111", -] - -[[package]] -name = "axcpu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e09bc1235e3da45e942b50f47812f8397ad84cb490264bf914c65ac44e6f8b1d" -dependencies = [ - "aarch64-cpu", - "cfg-if", - "lazyinit", - "linkme", - "log", - "loongArch64", - "memory_addr", - "page_table_entry", - "page_table_multiarch", - "percpu", - "riscv", - "static_assertions", - "tock-registers 0.9.0", - "x86", - "x86_64", + "syn 2.0.113", ] [[package]] name = "axcpu" version = "0.3.0" +source = "git+https://github.com/arceos-org/axcpu.git?tag=dev-v03#72ef3859952b7340bae261c9a50c32705e602017" dependencies = [ "aarch64-cpu", "axbacktrace", @@ -460,7 +424,7 @@ dependencies = [ "log", "memory_addr", "range-alloc", - "riscv_vplic 0.1.0 (git+https://github.com/liulog/riscv_vplic.git)", + "riscv_vplic 0.1.0 (git+https://github.com/arceos-hypervisor/riscv_vplic.git?rev=8bc5213)", "spin 0.9.8", ] @@ -499,10 +463,10 @@ dependencies = [ [[package]] name = "axdisplay" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "axdriver", - "axsync 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", + "axsync", "lazyinit", "log", ] @@ -510,12 +474,13 @@ dependencies = [ [[package]] name = "axdriver" version = "0.2.0" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "arm-gic-driver", "axalloc", - "axconfig 0.2.0", - "axdriver_base", - "axdriver_block", + "axconfig", + "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", + "axdriver_block 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", "axdriver_display", "axdriver_net", "axdriver_pci", @@ -523,7 +488,7 @@ dependencies = [ "axerrno 0.1.2", "axhal", "axklib", - "axmm 0.2.0", + "axmm", "cfg-if", "crate_interface", "dma-api 0.5.2", @@ -541,12 +506,26 @@ name = "axdriver_base" version = "0.1.2" source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" +[[package]] +name = "axdriver_base" +version = "0.1.2" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" + [[package]] name = "axdriver_block" version = "0.1.2" source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" dependencies = [ - "axdriver_base", + "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", + "log", +] + +[[package]] +name = "axdriver_block" +version = "0.1.2" +source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2#84eb2170f865e6fa29b78995a86f4fcdcede63df" +dependencies = [ + "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2)", "log", ] @@ -555,7 +534,7 @@ name = "axdriver_display" version = "0.1.2" source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" dependencies = [ - "axdriver_base", + "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", ] [[package]] @@ -563,7 +542,7 @@ name = "axdriver_net" version = "0.1.2" source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" dependencies = [ - "axdriver_base", + "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", "log", "spin 0.9.8", ] @@ -581,8 +560,8 @@ name = "axdriver_virtio" version = "0.1.2" source = "git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01#16f1ae864e1317f67d4e22a645fcb7399ae86596" dependencies = [ - "axdriver_base", - "axdriver_block", + "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", + "axdriver_block 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", "log", "virtio-drivers", ] @@ -610,35 +589,54 @@ dependencies = [ [[package]] name = "axfeat" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "axalloc", "axbacktrace", "axdriver", - "axfs", + "axfs 0.2.0", "axhal", "axlog", "axruntime", - "axsync 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", - "axtask 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", + "axsync", + "axtask", "kspin", ] +[[package]] +name = "axfs" +version = "0.1.0" +dependencies = [ + "axdriver", + "axdriver_block 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=v0.1.2)", + "axerrno 0.1.2", + "axfs_ramfs", + "axfs_vfs", + "axio", + "cap_access", + "fatfs 0.4.0 (git+https://github.com/Josen-B/rust-fatfs.git?rev=41122ef)", + "lazyinit", + "log", + "rsext4", + "spin 0.9.8", +] + [[package]] name = "axfs" version = "0.2.0" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "axdriver", - "axdriver_block", + "axdriver_block 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", "axerrno 0.1.2", "axfs_devfs", "axfs_ramfs", "axfs_vfs", "axio", - "axsync 0.2.0", + "axsync", "cap_access", "cfg-if", - "fatfs", + "fatfs 0.4.0 (git+https://github.com/rafalh/rust-fatfs?rev=4eccb50)", "lazyinit", "log", "scope-local", @@ -680,16 +678,18 @@ dependencies = [ [[package]] name = "axhal" version = "0.2.0" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "axalloc", - "axconfig 0.2.0", - "axcpu 0.3.0", - "axplat 0.3.0", + "axconfig", + "axcpu", + "axplat", "axplat-aarch64-qemu-virt", "axplat-loongarch64-qemu-virt", "axplat-riscv64-qemu-virt 0.3.0 (git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03)", "axplat-x86-pc", "cfg-if", + "fdt-parser", "heapless 0.9.2", "kernel_guard", "lazyinit", @@ -709,33 +709,22 @@ dependencies = [ "numeric-enum-macro", ] -[[package]] -name = "axin" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db62cb7067e33d432df247b32ee450ae267cb16319c8c5de247381c3652a639" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "axio" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30aa258a37c25c5e9d3ff45ec80e728ff7c499586e3e40719daf7908f10fd5bd" +checksum = "92a675c98dc5af5cca52cfdd1044ae960816909853cd13870737d55cb23f5d4e" dependencies = [ + "autocfg", "axerrno 0.1.2", ] [[package]] name = "axipi" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ - "axconfig 0.1.0", + "axconfig", "axhal", "kspin", "lazyinit", @@ -756,7 +745,7 @@ dependencies = [ [[package]] name = "axlog" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "cfg-if", "crate_interface", @@ -767,25 +756,10 @@ dependencies = [ [[package]] name = "axmm" version = "0.2.0" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "axalloc", - "axconfig 0.2.0", - "axerrno 0.1.2", - "axhal", - "kspin", - "lazyinit", - "log", - "memory_addr", - "memory_set", -] - -[[package]] -name = "axmm" -version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" -dependencies = [ - "axalloc", - "axconfig 0.1.0", + "axconfig", "axerrno 0.1.2", "axhal", "kspin", @@ -798,14 +772,14 @@ dependencies = [ [[package]] name = "axnet" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "axdriver", "axerrno 0.1.2", "axhal", "axio", - "axsync 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", - "axtask 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", + "axsync", + "axtask", "cfg-if", "lazyinit", "log", @@ -813,26 +787,12 @@ dependencies = [ "spin 0.10.0", ] -[[package]] -name = "axplat" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" -dependencies = [ - "axplat-macros 0.1.0 (git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0)", - "bitflags 2.10.0", - "const-str", - "crate_interface", - "handler_table", - "kspin", - "memory_addr", -] - [[package]] name = "axplat" version = "0.3.0" source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ - "axplat-macros 0.1.0 (git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03)", + "axplat-macros", "bitflags 2.10.0", "const-str", "crate_interface", @@ -842,33 +802,6 @@ dependencies = [ "percpu", ] -[[package]] -name = "axplat-aarch64-dyn" -version = "0.3.0" -dependencies = [ - "aarch64-cpu", - "aarch64-cpu-ext", - "any-uart", - "arm-gic-driver", - "axconfig-macros", - "axcpu 0.2.2", - "axplat 0.2.0", - "fdt-parser", - "heapless 0.8.0", - "lazyinit", - "log", - "memory_addr", - "page_table_entry", - "paste", - "percpu", - "rdif-intc", - "rdrive", - "serde", - "somehal", - "spin 0.10.0", - "toml 0.8.23", -] - [[package]] name = "axplat-aarch64-dyn" version = "0.4.0" @@ -879,8 +812,8 @@ dependencies = [ "any-uart", "arm-gic-driver", "axconfig-macros", - "axcpu 0.3.0", - "axplat 0.3.0", + "axcpu", + "axplat", "fdt-parser", "heapless 0.8.0", "lazyinit", @@ -906,8 +839,8 @@ dependencies = [ "arm-gic-driver", "arm_pl011", "arm_pl031", - "axcpu 0.3.0", - "axplat 0.3.0", + "axcpu", + "axplat", "int_ratio", "kspin", "lazyinit", @@ -922,8 +855,8 @@ version = "0.3.0" source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ "axconfig-macros", - "axcpu 0.3.0", - "axplat 0.3.0", + "axcpu", + "axplat", "axplat-aarch64-peripherals", "log", "page_table_entry", @@ -935,8 +868,8 @@ version = "0.3.0" source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ "axconfig-macros", - "axcpu 0.3.0", - "axplat 0.3.0", + "axcpu", + "axplat", "kspin", "lazyinit", "log", @@ -952,17 +885,7 @@ source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df071 dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", -] - -[[package]] -name = "axplat-macros" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axplat_crates?tag=vmm-v0.3.0#3345f58f3da5ef2eafcfa1fcf1ac32e5e80e1a7d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -970,8 +893,8 @@ name = "axplat-riscv64-qemu-virt" version = "0.3.0" dependencies = [ "axconfig-macros", - "axcpu 0.3.0", - "axplat 0.3.0", + "axcpu", + "axplat", "axvisor_api", "kspin", "lazyinit", @@ -989,8 +912,8 @@ version = "0.3.0" source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ "axconfig-macros", - "axcpu 0.3.0", - "axplat 0.3.0", + "axcpu", + "axplat", "kspin", "lazyinit", "log", @@ -1006,8 +929,8 @@ version = "0.3.0" source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ "axconfig-macros", - "axcpu 0.3.0", - "axplat 0.3.0", + "axcpu", + "axplat", "bitflags 2.10.0", "heapless 0.9.2", "int_ratio", @@ -1028,8 +951,8 @@ name = "axplat-x86-qemu-q35" version = "0.1.0" dependencies = [ "axconfig-macros", - "axcpu 0.3.0", - "axplat 0.3.0", + "axcpu", + "axplat", "bitflags 2.10.0", "heapless 0.9.2", "int_ratio", @@ -1062,21 +985,21 @@ name = "axruntime" version = "0.1.0" dependencies = [ "axalloc", - "axconfig 0.1.0", + "axconfig", "axdisplay", "axdriver", - "axerrno 0.1.2", - "axfs", + "axerrno 0.2.2", + "axfs 0.1.0", "axhal", "axipi", "axlog", - "axmm 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", + "axmm", "axnet", - "axplat 0.3.0", - "axplat-aarch64-dyn 0.4.0", + "axplat", + "axplat-aarch64-dyn", "axplat-riscv64-qemu-virt 0.3.0", "axplat-x86-qemu-q35", - "axtask 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", + "axtask", "cfg-if", "chrono", "crate_interface", @@ -1088,9 +1011,9 @@ dependencies = [ [[package]] name = "axsched" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44de469da35f912194e4104cc2f51bff63d6c184b65b6ebf8e90e2cd162b7f3c" +checksum = "cad6b7b0b8d9ad1d52a834d8b7721114413da8cf3430af928b1c8651f911287a" dependencies = [ "linked_list_r4l", ] @@ -1098,7 +1021,7 @@ dependencies = [ [[package]] name = "axstd" version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "arceos_api", "axerrno 0.1.2", @@ -1111,19 +1034,9 @@ dependencies = [ [[package]] name = "axsync" version = "0.2.0" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ - "axtask 0.2.0", - "event-listener", - "kspin", - "lock_api", -] - -[[package]] -name = "axsync" -version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" -dependencies = [ - "axtask 0.2.0 (git+https://github.com/arceos-org/arceos.git?tag=dev-251202)", + "axtask", "event-listener", "kspin", "lock_api", @@ -1132,19 +1045,9 @@ dependencies = [ [[package]] name = "axtask" version = "0.2.0" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ - "axerrno 0.1.2", - "axhal", - "cfg-if", - "log", -] - -[[package]] -name = "axtask" -version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251202#fe9bf0f2ac3825ecc580b7ef149ff093cfc6aaca" -dependencies = [ - "axconfig 0.1.0", + "axconfig", "axerrno 0.1.2", "axhal", "axpoll", @@ -1165,9 +1068,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" dependencies = [ "axum-core", "bytes", @@ -1198,9 +1101,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", @@ -1217,7 +1120,9 @@ dependencies = [ [[package]] name = "axvcpu" -version = "0.1.2" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefe761786c0c69a7c0eaf45602c460f721bce9d8b8decc4f733b488e9da657f" dependencies = [ "axaddrspace", "axerrno 0.1.2", @@ -1246,14 +1151,14 @@ dependencies = [ "anyhow", "arm-gic-driver", "axaddrspace", - "axconfig 0.1.0", + "axconfig", "axdevice 0.1.0 (git+https://github.com/liulog/axdevice?branch=master)", "axdevice_base", "axerrno 0.2.2", "axhvc", "axruntime", "axstd", - "axvcpu 0.1.2", + "axvcpu 0.1.1", "axvisor_api", "axvm", "bitflags 2.10.0", @@ -1278,11 +1183,11 @@ dependencies = [ "rdif-intc", "rdrive", "riscv_vcpu", - "riscv_vplic 0.1.0", + "riscv_vplic 0.1.0 (git+https://github.com/arceos-hypervisor/riscv_vplic.git?rev=8bc5213)", "spin 0.9.8", - "syn 2.0.111", + "syn 2.0.113", "timer_list", - "toml 0.9.8", + "toml 0.9.10+spec-1.1.0", "vm-fdt 0.3.0 (git+https://github.com/bullhh/vm-fdt.git)", ] @@ -1303,7 +1208,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -1316,7 +1221,7 @@ dependencies = [ "axdevice 0.1.0 (git+https://github.com/liulog/axdevice?branch=master)", "axdevice_base", "axerrno 0.2.2", - "axvcpu 0.1.2", + "axvcpu 0.1.1", "axvmconfig", "cfg-if", "cpumask", @@ -1342,7 +1247,7 @@ dependencies = [ "schemars", "serde", "serde_repr", - "toml 0.9.8", + "toml 0.9.10+spec-1.1.0", ] [[package]] @@ -1374,7 +1279,7 @@ checksum = "e585a01076fee271c5aabcf36212acb349fb3e638561d842fffa8ca013f4fdd8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -1405,7 +1310,7 @@ dependencies = [ "cargo_metadata 0.20.0", "flate2", "rand 0.9.2", - "reqwest 0.12.24", + "reqwest 0.12.28", "tar", ] @@ -1489,14 +1394,14 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byte-unit" @@ -1552,9 +1457,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "camino" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" dependencies = [ "serde_core", ] @@ -1579,11 +1484,12 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4" +checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082" dependencies = [ "serde", + "serde_core", ] [[package]] @@ -1624,7 +1530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" dependencies = [ "camino", - "cargo-platform 0.3.1", + "cargo-platform 0.3.2", "semver", "serde", "serde_json", @@ -1648,9 +1554,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.48" +version = "1.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" dependencies = [ "find-msvc-tools", "shlex", @@ -1683,9 +1589,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -1693,9 +1599,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -1712,7 +1618,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -1761,9 +1667,9 @@ dependencies = [ [[package]] name = "console" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4" +checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" dependencies = [ "encode_unicode", "libc", @@ -1792,18 +1698,18 @@ checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e" [[package]] name = "convert_case" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ "unicode-segmentation", ] [[package]] name = "convert_case" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] @@ -1820,9 +1726,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" dependencies = [ "core-foundation-sys", "libc", @@ -1860,7 +1766,7 @@ checksum = "70272a03a2cef15589bac05d3d15c023752f5f8f2da8be977d983a9d9e6250fb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -1936,7 +1842,7 @@ dependencies = [ "document-features", "mio", "parking_lot", - "rustix 1.1.2", + "rustix 1.1.3", "signal-hook", "signal-hook-mio", "winapi", @@ -1978,7 +1884,7 @@ checksum = "9a49d5cd78b1c748184d41407b14a58af8403c13328ff2b9f49b0a418c24e3ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2052,6 +1958,16 @@ dependencies = [ "darling_macro 0.21.3", ] +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + [[package]] name = "darling_core" version = "0.20.11" @@ -2063,7 +1979,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2076,7 +1992,20 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.113", ] [[package]] @@ -2087,7 +2016,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2098,7 +2027,18 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.111", + "syn 2.0.113", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.113", ] [[package]] @@ -2130,7 +2070,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2153,23 +2093,24 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case 0.7.1", + "convert_case 0.10.0", "proc-macro2", "quote", - "syn 2.0.111", + "rustc_version", + "syn 2.0.113", ] [[package]] @@ -2196,7 +2137,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2312,7 +2253,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2324,7 +2265,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2345,7 +2286,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2356,7 +2297,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2377,7 +2318,7 @@ dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2427,7 +2368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2448,7 +2389,7 @@ checksum = "ba8f5038f5845165d06fe1453fe4130ad546d3314818bbda57e208e7b0cffe08" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2457,6 +2398,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fatfs" +version = "0.4.0" +source = "git+https://github.com/Josen-B/rust-fatfs.git?rev=41122ef#41122eff7d244cb030c9cfb9b5e14fc50b70dd24" +dependencies = [ + "bitflags 2.10.0", + "log", +] + [[package]] name = "fatfs" version = "0.4.0" @@ -2486,9 +2436,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" [[package]] name = "fitimage" @@ -2618,7 +2568,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -2715,9 +2665,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", @@ -2922,7 +2872,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2 0.4.12", + "h2 0.4.13", "http 1.4.0", "http-body 1.0.1", "httparse", @@ -2983,9 +2933,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64 0.22.1", "bytes", @@ -3079,9 +3029,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -3093,9 +3043,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -3173,15 +3123,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" dependencies = [ - "darling 0.20.11", + "darling 0.23.0", "indoc", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -3208,9 +3158,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -3233,15 +3183,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" +checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" dependencies = [ "jiff-static", "log", @@ -3252,13 +3202,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" +checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -3279,7 +3229,7 @@ dependencies = [ "serde_json", "thiserror 2.0.17", "tokio", - "toml 0.9.8", + "toml 0.9.10+spec-1.1.0", "tower", "tower-http", ] @@ -3303,7 +3253,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -3315,14 +3265,14 @@ dependencies = [ "bitflags 2.10.0", "prettyplease", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] name = "kernel_guard" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307e6be468f3d6b6d895e191f63c11602e4e76575ecca68325d8c8dbebe2870e" +checksum = "d10c55bedf6789bc3748e0d8756ee639df1ae25144fd3525ed311044bd9a739f" dependencies = [ "cfg-if", "crate_interface", @@ -3384,19 +3334,19 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.179" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f" [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.10.0", "libc", - "redox_syscall", + "redox_syscall 0.7.0", ] [[package]] @@ -3432,9 +3382,9 @@ dependencies = [ [[package]] name = "linked_list_r4l" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c6e48d7df84e6414be8e53976ead35ba4d47a4ba561eaad5d1e2b1447b4c0a" +checksum = "1730c4ce817dc3edb092739ca5c109fe551018e5ea5a8361a8ddaa13d79ac8ed" [[package]] name = "linkme" @@ -3453,7 +3403,7 @@ checksum = "e5cec0ec4228b4853bb129c84dbf093a27e6c7a20526da046defc334a1b017f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -3491,9 +3441,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "loongArch64" @@ -3622,9 +3572,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", @@ -3666,9 +3616,9 @@ checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" [[package]] name = "network-interface" -version = "2.0.3" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07709a6d4eba90ab10ec170a0530b3aafc81cb8a2d380e4423ae41fc55fe5745" +checksum = "4ddcb8865ad3d9950f22f42ffa0ef0aecbfbf191867b3122413602b0a360b2a6" dependencies = [ "cc", "libc", @@ -3825,7 +3775,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -3857,9 +3807,9 @@ dependencies = [ [[package]] name = "ostool" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c843408786c18af02d88ba7de7d25f4ff94a137b238a43ce1653c7eceb4b7b" +checksum = "1b4a86102814395817cfeb8ec82a6e6cf5c65c09c36c90dc7a609eaa831a7d3e" dependencies = [ "anyhow", "byte-unit", @@ -3878,7 +3828,7 @@ dependencies = [ "object", "ratatui", "regex", - "reqwest 0.12.24", + "reqwest 0.12.28", "schemars", "serde", "serde_json", @@ -3887,7 +3837,7 @@ dependencies = [ "tar", "tftpd", "tokio", - "toml 0.9.8", + "toml 0.9.10+spec-1.1.0", "uboot-shell", "ureq", ] @@ -3907,6 +3857,8 @@ dependencies = [ [[package]] name = "page_table_entry" version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda9891ec368fda90e4b2cc36592b4881073e25a339fe7e3eddd811f0cf6bf18" dependencies = [ "aarch64-cpu", "bitflags 2.10.0", @@ -3917,6 +3869,8 @@ dependencies = [ [[package]] name = "page_table_multiarch" version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa11a21844255e14aa6688ef0eafb058d7be19338633024fb59417f1bfb07f8" dependencies = [ "log", "memory_addr", @@ -3943,7 +3897,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", "windows-link", ] @@ -3987,9 +3941,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "percpu" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e56c0c558952222967b592899f98765b48590e7bd7403bfd7075f73afc6ed6" +checksum = "54ca06381bdd16a5397e23cf61d347b539c765e2c20b2ecc5cb36df88695c1f7" dependencies = [ "cfg-if", "percpu_macros", @@ -3999,13 +3953,14 @@ dependencies = [ [[package]] name = "percpu_macros" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9f4cc54a2e471ff72f1499461ba381ad4eae9cbd60d29c258545b995e406e0" +checksum = "a933d46113c0171aee86623311a9367f2ec3a86dab0a96aba1d5bc627473617e" dependencies = [ + "cfg-if", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -4054,7 +4009,7 @@ dependencies = [ "prettyplease", "quote", "spin 0.10.0", - "syn 2.0.111", + "syn 2.0.113", "thiserror 2.0.17", ] @@ -4067,7 +4022,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -4090,9 +4045,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "portable-atomic-util" @@ -4124,7 +4079,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.28", + "zerocopy 0.8.31", ] [[package]] @@ -4134,7 +4089,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -4143,7 +4098,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.23.7", + "toml_edit 0.23.10+spec-1.0.0", ] [[package]] @@ -4165,14 +4120,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] @@ -4249,14 +4204,14 @@ dependencies = [ "once_cell", "socket2 0.6.1", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -4468,7 +4423,7 @@ checksum = "eab3105c9af32e901a2adc7d920b39ff8b6ee0f6f0b7dfdeaf18f306ec12606f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -4480,6 +4435,15 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "redox_syscall" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +dependencies = [ + "bitflags 2.10.0", +] + [[package]] name = "ref-cast" version = "1.0.25" @@ -4497,7 +4461,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -4596,9 +4560,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.24" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", @@ -4606,7 +4570,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.12", + "h2 0.4.13", "http 1.4.0", "http-body 1.0.1", "http-body-util", @@ -4694,7 +4658,7 @@ checksum = "e8c4aa1ea1af6dcc83a61be12e8189f9b293c3ba5a487778a4cd89fb060fdbbc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -4724,7 +4688,7 @@ version = "0.1.2" dependencies = [ "axaddrspace", "axerrno 0.1.2", - "axvcpu 0.1.2", + "axvcpu 0.1.1", "axvisor_api", "bit_field", "bitflags 2.10.0", @@ -4746,6 +4710,7 @@ dependencies = [ [[package]] name = "riscv_vplic" version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/riscv_vplic.git?rev=8bc5213#8bc5213c77139755e1106b49133248392fcb4f58" dependencies = [ "axaddrspace", "axdevice_base", @@ -4786,20 +4751,19 @@ dependencies = [ [[package]] name = "rk3588-clk" -version = "0.1.0" -source = "git+https://github.com/drivercraft/rk3588-clk#2434106bb1838bc2af7c3f4de3ab6bbc15fdeb81" +version = "0.1.1" +source = "git+https://github.com/drivercraft/rk3588-clk#1476c44483e7800ff3c7cb53cdfaf01e72e320d4" dependencies = [ "bare-test-macros", "log", - "mbarrier", "tock-registers 0.10.1", ] [[package]] name = "rkyv" -version = "0.7.45" +version = "0.7.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" dependencies = [ "bitvec", "bytecheck", @@ -4815,9 +4779,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.45" +version = "0.7.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" dependencies = [ "proc-macro2", "quote", @@ -4849,6 +4813,16 @@ dependencies = [ "tock-registers 0.10.1", ] +[[package]] +name = "rsext4" +version = "0.1.0" +source = "git+https://github.com/Dirinkbottle/rsext4.git?tag=dev-251222#376e253cc6b8767bc14ca5054fbc1ae49f7a8c8d" +dependencies = [ + "bitflags 2.10.0", + "lazy_static", + "log", +] + [[package]] name = "rust_decimal" version = "1.39.0" @@ -4877,6 +4851,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.44" @@ -4892,22 +4875,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.35" +version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "log", "once_cell", @@ -4929,9 +4912,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "web-time", "zeroize", @@ -4967,7 +4950,7 @@ checksum = "a71347da9582cc6b6f3652c7d2c06516c9555690b3738ecdff7e84297f4e17fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -4987,9 +4970,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "sbi-rt" @@ -5017,9 +5000,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" dependencies = [ "dyn-clone", "ref-cast", @@ -5030,24 +5013,24 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301858a4023d78debd2353c7426dc486001bddc91ae31a76fb1f55132f7e2633" +checksum = "4908ad288c5035a8eb12cfdf0d49270def0a268ee162b75eeee0f85d155a7c45" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] name = "scope-local" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "231e441739fbe668e91acf8440a0b1ac83c73faec7a6d984190ee25354c2ec9a" +checksum = "4a7d5ed5013e6436fcd78f2bcd3892a6286ef9ce6c9b61504d4c4a08d6a40eab" dependencies = [ "percpu", - "spin 0.9.8", + "spin 0.10.0", ] [[package]] @@ -5163,7 +5146,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -5174,20 +5157,20 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -5209,7 +5192,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -5223,9 +5206,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ "serde_core", ] @@ -5244,21 +5227,22 @@ dependencies = [ [[package]] name = "serialport" -version = "4.7.3" +version = "4.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acaf3f973e8616d7ceac415f53fc60e190b2a686fbcf8d27d0256c741c5007b" +checksum = "21f60a586160667241d7702c420fc223939fb3c0bb8d3fac84f78768e8970dee" dependencies = [ "bitflags 2.10.0", "cfg-if", - "core-foundation 0.10.1", + "core-foundation 0.10.0", "core-foundation-sys", "io-kit-sys", "libudev", "mach2", "nix", + "quote", "scopeguard", "unescaper", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -5312,18 +5296,19 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.7" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simdutf8" @@ -5411,7 +5396,7 @@ dependencies = [ "serde", "smccc", "spin 0.10.0", - "toml 0.9.8", + "toml 0.9.10+spec-1.1.0", "url", ] @@ -5488,7 +5473,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -5500,7 +5485,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -5535,9 +5520,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.111" +version = "2.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "678faa00651c9eb72dd2020cbdf275d92eccb2400d568e419efdd64838145cb4" dependencies = [ "proc-macro2", "quote", @@ -5567,7 +5552,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -5631,15 +5616,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom 0.3.4", "once_cell", - "rustix 1.1.2", - "windows-sys 0.59.0", + "rustix 1.1.3", + "windows-sys 0.61.2", ] [[package]] @@ -5674,7 +5659,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -5685,7 +5670,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -5772,9 +5757,9 @@ checksum = "8d2d250f87fb3fb6f225c907cf54381509f47b40b74b1d1f12d2dccbc915bdfe" [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "bytes", "libc", @@ -5795,7 +5780,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -5820,9 +5805,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.17" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -5845,14 +5830,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.8" +version = "0.9.10+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.0.3", - "toml_datetime 0.7.3", + "serde_spanned 1.0.4", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", "winnow", @@ -5869,9 +5854,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] @@ -5892,21 +5877,21 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", - "toml_datetime 0.7.3", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "winnow", ] [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] @@ -5919,9 +5904,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.0.4" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tower" @@ -5941,9 +5926,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "bitflags 2.10.0", "bytes", @@ -5981,9 +5966,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -5992,9 +5977,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] @@ -6009,7 +5994,7 @@ dependencies = [ "lenient_semver", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -6059,9 +6044,9 @@ dependencies = [ [[package]] name = "unescaper" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26" +checksum = "4064ed685c487dbc25bd3f0e9548f2e34bab9d18cefc700f9ec2dba74ba1138e" dependencies = [ "thiserror 2.0.17", ] @@ -6156,9 +6141,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -6192,9 +6177,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "js-sys", "wasm-bindgen", @@ -6313,7 +6298,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", "wasm-bindgen-shared", ] @@ -6348,9 +6333,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" dependencies = [ "rustls-pki-types", ] @@ -6398,7 +6383,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -6409,7 +6394,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -6774,7 +6759,7 @@ dependencies = [ "axaddrspace", "axdevice_base", "axerrno 0.1.2", - "axvcpu 0.1.2", + "axvcpu 0.1.1", "axvisor_api", "bit_field", "bitflags 2.10.0", @@ -6816,7 +6801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix 1.1.2", + "rustix 1.1.3", ] [[package]] @@ -6838,15 +6823,14 @@ dependencies = [ "flate2", "jkconfig", "ostool", - "reqwest 0.12.24", + "reqwest 0.12.28", "schemars", "serde", "serde_json", "sha2", "tar", "tokio", - "toml 0.9.8", - "zerocopy 0.8.28", + "toml 0.9.10+spec-1.1.0", ] [[package]] @@ -6868,7 +6852,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", "synstructure", ] @@ -6884,11 +6868,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.28" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ - "zerocopy-derive 0.8.28", + "zerocopy-derive 0.8.31", ] [[package]] @@ -6899,18 +6883,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] name = "zerocopy-derive" -version = "0.8.28" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] [[package]] @@ -6930,7 +6914,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", "synstructure", ] @@ -6970,5 +6954,11 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.113", ] + +[[package]] +name = "zmij" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" diff --git a/Cargo.toml b/Cargo.toml index 83702541c..4e8d655bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,37 +90,23 @@ axplat-riscv64-qemu-virt = {path = "platform/riscv64-qemu-virt"} axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "next"} [patch.crates-io] -axvcpu = {path="crates/axvcpu"} -axvmconfig = {path = "crates/axvmconfig"} -# riscv = {git = "https://github.com/liulog/riscv.git", branch = "master"} -axcpu = {path = "crates/axcpu"} +axvmconfig = { path = "crates/axvmconfig" } axvisor_api = {path = "../axvisor_api"} -page_table_entry = { path = "../page_table_multiarch/page_table_entry" } -page_table_multiarch = { path = "../page_table_multiarch/page_table_multiarch", features = ["stage-2"]} -axaddrspace = {path = "crates/axaddrspace"} +# page_table_entry = { path = "../page_table_multiarch/page_table_entry" } +# page_table_multiarch = { path = "../page_table_multiarch/page_table_multiarch", features = ["stage-2"]} [patch."https://github.com/arceos-hypervisor/axvmconfig.git"] axvmconfig = {path = "crates/axvmconfig"} -[patch."https://github.com/arceos-org/arceos"] -axconfig = {path = "modules/axconfig"} -axruntime = {path = "modules/axruntime"} -axfs = {path = "modules/axfs"} -axhal = {path = "../arceos/modules/axhal"} -axalloc = {path = "../arceos/modules/axalloc"} -axdriver = {path = "../arceos/modules/axdriver"} +[patch."https://github.com/arceos-org/arceos.git"] +axconfig = { path = "modules/axconfig" } +axruntime = { path = "modules/axruntime" } [patch."https://github.com/arceos-hypervisor/axvm.git"] axvm = {path = "crates/axvm"} [patch."https://github.com/liulog/riscv_vcpu.git"] -riscv_vcpu = {path = "crates/riscv_vcpu"} - -[patch."https://github.com/arceos-org/axcpu.git"] -axcpu = {path = "crates/axcpu"} - -[patch."https://github.com/arceos-hypervisor/arm_vcpu.git"] -arm_vcpu = {path = "crates/arm_vcpu"} +riscv_vcpu = { path = "crates/riscv_vcpu" } [patch."https://github.com/arceos-hypervisor/axdevice.git"] axdevice = {git = "https://github.com/liulog/axdevice", branch = "master"} diff --git a/configs/vms/arceos-riscv64-qemu-smp1.toml b/configs/vms/arceos-riscv64-qemu-smp1.toml index 3c68c0d09..3ec1f67c8 100644 --- a/configs/vms/arceos-riscv64-qemu-smp1.toml +++ b/configs/vms/arceos-riscv64-qemu-smp1.toml @@ -20,9 +20,9 @@ phys_cpu_ids = [0] entry_point = 0x8020_0000 # The location of image: "memory" | "fs". # load from memory. -image_location = "memory" +image_location = "fs" # The file path of the kernel image. -kernel_path = "path/arceos-riscv64-smp1.bin" +kernel_path = "/guest/helloworld_riscv64-qemu-virt.bin" # The load address of the kernel image. kernel_load_addr = 0x8020_0000 # The file path of the device tree blob (DTB). diff --git a/configs/vms/linux-riscv64-qemu-smp1.dts b/configs/vms/linux-riscv64-qemu-smp1.dts new file mode 100644 index 000000000..7a09d0979 --- /dev/null +++ b/configs/vms/linux-riscv64-qemu-smp1.dts @@ -0,0 +1,132 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "riscv-virtio"; + model = "riscv-virtio,qemu"; + + memory@90000000 { + device_type = "memory"; + reg = <0x00 0x90000000 0x00 0x40000000>; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x989680>; + + cpu@0 { + phandle = <0x07>; + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,cbop-block-size = <0x40>; + riscv,cboz-block-size = <0x40>; + riscv,cbom-block-size = <0x40>; + riscv,isa-extensions = "i\0m\0a\0f\0d\0c"; + riscv,isa-base = "rv64i"; + riscv,isa = "rv64imafdc"; + mmu-type = "riscv,sv39"; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x08>; + }; + }; + }; + + aliases { + serial0 = "/soc/serial@10000000"; + }; + + chosen { + bootargs = "earlycon=sbi console=ttyS0,115200 init=/init root=/dev/vda rw"; + stdout-path = "/soc/serial@10000000"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "simple-bus"; + ranges; + + serial@10000000 { + interrupts = <0x0a>; + interrupt-parent = <0x09>; + clock-frequency = "\08@"; + reg = <0x00 0x10000000 0x00 0x100>; + compatible = "ns16550a"; + }; + + plic@c000000 { + phandle = <0x09>; + riscv,ndev = <0x5f>; + reg = <0x00 0xc000000 0x00 0x600000>; + interrupts-extended = <0x08 0x0b 0x08 0x09>; + interrupt-controller; + compatible = "sifive,plic-1.0.0\0riscv,plic0"; + #address-cells = <0x00>; + #interrupt-cells = <0x01>; + }; + + virtio_mmio@10008000 { + interrupts = <0x08>; + interrupt-parent = <0x09>; + reg = <0x00 0x10008000 0x00 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10007000 { + interrupts = <0x07>; + interrupt-parent = <0x09>; + reg = <0x00 0x10007000 0x00 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10006000 { + interrupts = <0x06>; + interrupt-parent = <0x09>; + reg = <0x00 0x10006000 0x00 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10005000 { + interrupts = <0x05>; + interrupt-parent = <0x09>; + reg = <0x00 0x10005000 0x00 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10004000 { + interrupts = <0x04>; + interrupt-parent = <0x09>; + reg = <0x00 0x10004000 0x00 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10003000 { + interrupts = <0x03>; + interrupt-parent = <0x09>; + reg = <0x00 0x10003000 0x00 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10002000 { + interrupts = <0x02>; + interrupt-parent = <0x09>; + reg = <0x00 0x10002000 0x00 0x1000>; + compatible = "virtio,mmio"; + }; + + virtio_mmio@10001000 { + interrupts = <0x01>; + interrupt-parent = <0x09>; + reg = <0x00 0x10001000 0x00 0x1000>; + compatible = "virtio,mmio"; + }; + }; +}; diff --git a/configs/vms/linux-riscv64-qemu-smp1.toml b/configs/vms/linux-riscv64-qemu-smp1.toml index e849a54d9..dbc6799b5 100644 --- a/configs/vms/linux-riscv64-qemu-smp1.toml +++ b/configs/vms/linux-riscv64-qemu-smp1.toml @@ -20,14 +20,14 @@ phys_cpu_ids = [0] entry_point = 0x8020_0000 # The location of image: "memory" | "fs". # load from memory. -image_location = "memory" +image_location = "fs" # The file path of the kernel image. # kernel_path = "linux-6.6.62.bin" -kernel_path = "tmp/Image" +kernel_path = "/guest/qemu-riscv64" # The load address of the kernel image. kernel_load_addr = 0x8020_0000 # The file path of the device tree blob (DTB). -#dtb_path = "tmp/linux-aarch64-qemu-smp1.dtb" +dtb_path = "/guest/linux-aarch64-qemu-smp1.dtb" # The load address of the device tree blob (DTB). dtb_load_addr = 0x8000_0000 diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 066feae1a..691e845f6 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -63,7 +63,7 @@ aarch64-cpu-ext = "0.1" arm-gic-driver = {version = "0.15.5", features = ["rdif"]} [target.'cfg(target_arch = "riscv64")'.dependencies] -riscv_vplic = { path="../crates/riscv_vplic" } +riscv_vplic = { git = "https://github.com/arceos-hypervisor/riscv_vplic.git", rev = "8bc5213" } riscv_vcpu = { git = "https://github.com/liulog/riscv_vcpu.git", branch = "master" } [build-dependencies] diff --git a/modules/axconfig/src/lib.rs b/modules/axconfig/src/lib.rs index 82d22f6ac..b9010f32f 100644 --- a/modules/axconfig/src/lib.rs +++ b/modules/axconfig/src/lib.rs @@ -40,7 +40,7 @@ pub mod devices { #[doc = " Timer interrupt num (PPI, physical timer)."] pub const TIMER_IRQ: usize = 0xf0; #[doc = " VirtIO MMIO regions with format (`base_paddr`, `size`)."] - pub const VIRTIO_MMIO_REGIONS: &[(usize, usize)] = &[]; + pub const VIRTIO_MMIO_RANGES: &[(usize, usize)] = &[]; } #[doc = ""] #[doc = " Platform configs"] diff --git a/modules/axruntime/Cargo.toml b/modules/axruntime/Cargo.toml index a7e216b15..6d42a51e2 100644 --- a/modules/axruntime/Cargo.toml +++ b/modules/axruntime/Cargo.toml @@ -30,7 +30,7 @@ axalloc = {workspace = true, optional = true} axconfig = {workspace = true} axdisplay = {workspace = true, optional = true} axdriver = {workspace = true, optional = true} -axerrno = "0.1" +axerrno = {workspace = true} axfs = {workspace = true, optional = true} axhal = {workspace = true} axipi = {workspace = true, optional = true} diff --git a/platform/riscv64-qemu-virt/axconfig.toml b/platform/riscv64-qemu-virt/axconfig.toml index f541767c3..ddcb024ef 100644 --- a/platform/riscv64-qemu-virt/axconfig.toml +++ b/platform/riscv64-qemu-virt/axconfig.toml @@ -13,8 +13,8 @@ package = "axplat-riscv64-qemu-virt" # str cpu-num = 4 # uint # Base address of the whole physical memory. phys-memory-base = 0x8000_0000 # uint -# Size of the whole physical memory. (4GB) -phys-memory-size = 0x1_0000_0000 # uint +# Size of the whole physical memory. (8GB) +phys-memory-size = 0x2_0000_0000 # uint # Base physical address of the kernel image. kernel-base-paddr = 0x8020_0000 # uint # Base virtual address of the kernel image. diff --git a/xtask/src/image.rs b/xtask/src/image.rs index dcde79bfa..4d9eb76bb 100644 --- a/xtask/src/image.rs +++ b/xtask/src/image.rs @@ -32,7 +32,7 @@ use tokio::io::{AsyncWriteExt, BufWriter}; /// Base URL for downloading images const IMAGE_URL_BASE: &str = - "https://github.com/arceos-hypervisor/axvisor-guest/releases/download/v0.0.20/"; + "https://github.com/arceos-hypervisor/axvisor-guest/releases/download/v0.0.22/"; /// Image management command line arguments. #[derive(Parser)] From cce31218ba07d270249e686f8c00c3269e0dcabc Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 7 Jan 2026 15:30:08 +0800 Subject: [PATCH 034/132] Update Cargo.toml and configuration files for ArceOS support; adjust dependencies and paths for modules --- Cargo.lock | 28 ++++++++++++++++------- Cargo.toml | 3 +-- configs/vms/arceos-riscv64-qemu-smp1.toml | 8 +++---- configs/vms/linux-riscv64-qemu-smp1.toml | 25 +++++++++----------- kernel/Cargo.toml | 2 +- modules/axconfig/src/lib.rs | 11 ++++++++- modules/axruntime/src/lib.rs | 1 - platform/riscv64-qemu-virt/axconfig.toml | 3 ++- 8 files changed, 48 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3ab8b489..8d1b4cfea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1118,6 +1118,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "axvcpu" +version = "0.1.1" +dependencies = [ + "axaddrspace", + "axerrno 0.1.2", + "axvisor_api", + "memory_addr", + "percpu", +] + [[package]] name = "axvcpu" version = "0.1.1" @@ -1154,11 +1165,12 @@ dependencies = [ "axconfig", "axdevice 0.1.0 (git+https://github.com/liulog/axdevice?branch=master)", "axdevice_base", + "axdriver", "axerrno 0.2.2", "axhvc", "axruntime", "axstd", - "axvcpu 0.1.1", + "axvcpu 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "axvisor_api", "axvm", "bitflags 2.10.0", @@ -1221,7 +1233,7 @@ dependencies = [ "axdevice 0.1.0 (git+https://github.com/liulog/axdevice?branch=master)", "axdevice_base", "axerrno 0.2.2", - "axvcpu 0.1.1", + "axvcpu 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "axvmconfig", "cfg-if", "cpumask", @@ -2368,7 +2380,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4204,7 +4216,7 @@ dependencies = [ "once_cell", "socket2 0.6.1", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -4688,7 +4700,7 @@ version = "0.1.2" dependencies = [ "axaddrspace", "axerrno 0.1.2", - "axvcpu 0.1.1", + "axvcpu 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "axvisor_api", "bit_field", "bitflags 2.10.0", @@ -4883,7 +4895,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5624,7 +5636,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.3", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6759,7 +6771,7 @@ dependencies = [ "axaddrspace", "axdevice_base", "axerrno 0.1.2", - "axvcpu 0.1.1", + "axvcpu 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "axvisor_api", "bit_field", "bitflags 2.10.0", diff --git a/Cargo.toml b/Cargo.toml index 4e8d655bd..35820b0d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,8 +92,7 @@ axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branc [patch.crates-io] axvmconfig = { path = "crates/axvmconfig" } axvisor_api = {path = "../axvisor_api"} -# page_table_entry = { path = "../page_table_multiarch/page_table_entry" } -# page_table_multiarch = { path = "../page_table_multiarch/page_table_multiarch", features = ["stage-2"]} +# axvcpu = { path = "./crates/axvcpu" } [patch."https://github.com/arceos-hypervisor/axvmconfig.git"] axvmconfig = {path = "crates/axvmconfig"} diff --git a/configs/vms/arceos-riscv64-qemu-smp1.toml b/configs/vms/arceos-riscv64-qemu-smp1.toml index 3ec1f67c8..c80c906d2 100644 --- a/configs/vms/arceos-riscv64-qemu-smp1.toml +++ b/configs/vms/arceos-riscv64-qemu-smp1.toml @@ -20,9 +20,9 @@ phys_cpu_ids = [0] entry_point = 0x8020_0000 # The location of image: "memory" | "fs". # load from memory. -image_location = "fs" +image_location = "memory" # The file path of the kernel image. -kernel_path = "/guest/helloworld_riscv64-qemu-virt.bin" +kernel_path = "/path/tmp/images/helloworld_riscv64-qemu-virt.bin" # The load address of the kernel image. kernel_load_addr = 0x8020_0000 # The file path of the device tree blob (DTB). @@ -40,7 +40,7 @@ dtb_load_addr = 0x8220_0000 # Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). # For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. memory_regions = [ - [0x8000_0000, 0x4000_0000, 0x7, 1], # System RAM 1G MAP_ALLOC + [0x8000_0000, 0x800_0000, 0x7, 0], # System RAM 1G MAP_ALLOC ] # @@ -49,7 +49,6 @@ memory_regions = [ [devices] # Pass-through devices. passthrough_devices = [ - ["/",], ] # Passthrough addresses. @@ -60,7 +59,6 @@ passthrough_addresses = [ # Devices that are not desired to be passed through to the guest excluded_devices = [ - ["/pci@30000000"], ] # Emu_devices. diff --git a/configs/vms/linux-riscv64-qemu-smp1.toml b/configs/vms/linux-riscv64-qemu-smp1.toml index dbc6799b5..cf7ee30a6 100644 --- a/configs/vms/linux-riscv64-qemu-smp1.toml +++ b/configs/vms/linux-riscv64-qemu-smp1.toml @@ -17,24 +17,24 @@ phys_cpu_ids = [0] # [kernel] # The entry point of the kernel image. -entry_point = 0x8020_0000 +entry_point = 0x9000_0000 # The location of image: "memory" | "fs". # load from memory. -image_location = "fs" +image_location = "memory" # The file path of the kernel image. # kernel_path = "linux-6.6.62.bin" -kernel_path = "/guest/qemu-riscv64" +kernel_path = "/path/tmp/images/qemu_riscv64_linux/qemu-riscv64" # The load address of the kernel image. -kernel_load_addr = 0x8020_0000 +kernel_load_addr = 0x9000_0000 # The file path of the device tree blob (DTB). -dtb_path = "/guest/linux-aarch64-qemu-smp1.dtb" +dtb_path = "/path/tmp/configs/linux-riscv64-qemu-smp1.dtb" # The load address of the device tree blob (DTB). -dtb_load_addr = 0x8000_0000 +dtb_load_addr = 0x9200_0000 # Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). # For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL + [0x9000_0000, 0x4000_0000, 0x7, 2], # System RAM 1G MAP_IDENTICAL ] # @@ -44,27 +44,24 @@ memory_regions = [ # Pass-through devices. # Name Base-Ipa Base-Pa Length Alloc-Irq. passthrough_devices = [ - ["/"], - #["/timer"], + # ["/soc/serial@10000000", 0x1000_0000, 0x1000_0000, 0x1000, 33], ] # Passthrough addresses. # Base-GPA Length. passthrough_addresses = [ - #[0x28041000, 0x100_0000] + [0x1000_0000, 0x1000], # uart + [0x1000_1000, 0x8000], # virtio-mmio ] # Devices that are not desired to be passed through to the guest excluded_devices = [ - # ["/gic-v3"], ] # Emu_devices. # Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. emu_devices = [ - # ["gppt-gicd", 0x0800_0000, 0x1_0000, 0, 0x21, []], - # ["gppt-gicr", 0x080a_0000, 0x2_0000, 0, 0x20, [1, 0x2_0000, 0]], # 1 vcpu, stride 0x20000, starts with pcpu 0 - # ["gppt-gits", 0x0808_0000, 0x2_0000, 0, 0x22, [0x0808_0000]], # host_gits_base + ["plic", 0x0c00_0000, 0x60_0000, 0, 0x30, [2]], # [context_num] ] interrupt_mode = "passthrough" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 691e845f6..3a7787201 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -36,6 +36,7 @@ axhvc.workspace = true axruntime = {workspace = true, features = ["alloc", "irq", "paging", "smp", "multitask"]} axvcpu.workspace = true axvm.workspace = true +axdriver.workspace = true # System independent crates provided by ArceOS, these crates could be imported by remote url. axerrno.workspace = true @@ -57,7 +58,6 @@ axdevice_base = "0.1" axvisor_api = "0.1" driver.workspace = true - [target.'cfg(target_arch = "aarch64")'.dependencies] aarch64-cpu-ext = "0.1" arm-gic-driver = {version = "0.15.5", features = ["rdif"]} diff --git a/modules/axconfig/src/lib.rs b/modules/axconfig/src/lib.rs index b9010f32f..554efd0d6 100644 --- a/modules/axconfig/src/lib.rs +++ b/modules/axconfig/src/lib.rs @@ -40,7 +40,16 @@ pub mod devices { #[doc = " Timer interrupt num (PPI, physical timer)."] pub const TIMER_IRQ: usize = 0xf0; #[doc = " VirtIO MMIO regions with format (`base_paddr`, `size`)."] - pub const VIRTIO_MMIO_RANGES: &[(usize, usize)] = &[]; + pub const VIRTIO_MMIO_RANGES: &[(usize, usize)] = &[ + (0x1000_1000, 0x1000), + (0x1000_2000, 0x1000), + (0x1000_3000, 0x1000), + (0x1000_4000, 0x1000), + (0x1000_5000, 0x1000), + (0x1000_6000, 0x1000), + (0x1000_7000, 0x1000), + (0x1000_8000, 0x1000), + ]; } #[doc = ""] #[doc = " Platform configs"] diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index 4d483fe99..41e9a3d3f 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -128,7 +128,6 @@ pub fn rust_main(cpu_id: usize, arg: usize) -> ! { // build_mode = {}\n\ // smp = {}\n\ // ", - // ); #[cfg(feature = "rtc")] ax_println!( diff --git a/platform/riscv64-qemu-virt/axconfig.toml b/platform/riscv64-qemu-virt/axconfig.toml index ddcb024ef..581a4a741 100644 --- a/platform/riscv64-qemu-virt/axconfig.toml +++ b/platform/riscv64-qemu-virt/axconfig.toml @@ -14,7 +14,7 @@ cpu-num = 4 # uint # Base address of the whole physical memory. phys-memory-base = 0x8000_0000 # uint # Size of the whole physical memory. (8GB) -phys-memory-size = 0x2_0000_0000 # uint +phys-memory-size = 0x1_0000_0000 # uint # Base physical address of the kernel image. kernel-base-paddr = 0x8020_0000 # uint # Base virtual address of the kernel image. @@ -44,6 +44,7 @@ mmio-ranges = [ [0x1000_1000, 0x8000], # VirtIO [0x3000_0000, 0x1000_0000], # PCI config space [0x4000_0000, 0x4000_0000], # PCI memory ranges (ranges 1: 32-bit MMIO space) + [0x2_7fe0_0000, 0x1000_0000] ] # [(uint, uint)] # VirtIO MMIO ranges with format (`base_paddr`, `size`). virtio-mmio-ranges = [ From aae42f518007e4df3674da4ab6d95b27a00ab5b1 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Wed, 7 Jan 2026 17:34:47 +0800 Subject: [PATCH 035/132] Migrate to `crate_interface` v0.2, bump to v0.2 (#7) * migrate to `crate_interface` 0.2, bump to 0.2 * fix missing docs * fix documents, add lock for test --- Cargo.toml | 6 +- axvisor_api_proc/Cargo.toml | 2 +- axvisor_api_proc/src/items.rs | 111 ----------- axvisor_api_proc/src/lib.rs | 283 +++++---------------------- examples/example.rs | 42 ++-- src/arch.rs | 25 +++ src/host.rs | 8 + src/lib.rs | 354 +++++++++++----------------------- src/memory.rs | 46 +++++ src/test.rs | 83 ++++---- src/time.rs | 53 +++++ src/vmm.rs | 37 ++++ 12 files changed, 404 insertions(+), 646 deletions(-) delete mode 100644 axvisor_api_proc/src/items.rs create mode 100644 src/arch.rs create mode 100644 src/host.rs create mode 100644 src/memory.rs create mode 100644 src/time.rs create mode 100644 src/vmm.rs diff --git a/Cargo.toml b/Cargo.toml index a30c70c70..b21e2f67c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/arceos-hypervisor/axvisor_api" [package] name = "axvisor_api" description = "Basic API for components of the Hypervisor on ArceOS" -version = "0.1.0" +version = "0.2.0" keywords = ["axvisor", "api", "embedded"] categories = ["embedded", "no-std"] authors.workspace = true @@ -22,8 +22,8 @@ license.workspace = true repository.workspace = true [dependencies] -axvisor_api_proc = { path = "axvisor_api_proc", version = "0.1.0"} +axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0"} -crate_interface = "0.1" +crate_interface = "0.2" memory_addr = "0.4" axaddrspace = "0.1.0" diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml index e01ae4181..f080e7672 100644 --- a/axvisor_api_proc/Cargo.toml +++ b/axvisor_api_proc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "axvisor_api_proc" -version = "0.1.0" +version = "0.2.0" description = "Procedural macros for the `axvisor_api` crate" keywords = ["axvisor", "api", "embedded", "macros"] categories = ["development-tools::procedural-macro-helpers"] diff --git a/axvisor_api_proc/src/items.rs b/axvisor_api_proc/src/items.rs deleted file mode 100644 index 92654cfd8..000000000 --- a/axvisor_api_proc/src/items.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Definitions of custom items used in the `api_mod!` and `api_mod_impl!` macros. -//! -//! `api_mod!` and `api_mod_impl!` have very similar structures. - -use syn::{ - Attribute, Block, Ident, Item, Signature, Token, Visibility, braced, - parse::{Parse, ParseStream}, - token::Brace, -}; - -/// An API function, defined with the `extern fn` syntax. It represents both the definition and the implementation. For -/// the definition, `T` is `Token![;]`, and for the implementation, `T` is `syn::Block`. -pub struct ItemApiFn { - /// Attributes of the function. - pub attrs: Vec, - #[expect(dead_code)] - /// The `extern` keyword. - pub extern_token: Token![extern], - /// The function signature. - pub sig: Signature, - /// The body of the function, or a semicolon. - pub body: T, -} - -impl Parse for ItemApiFn { - fn parse(input: ParseStream) -> syn::Result { - let attrs = input.call(Attribute::parse_outer)?; - let extern_token = input.parse()?; - let sig = input.parse()?; - let body = input.parse()?; - - Ok(Self { - attrs, - extern_token, - sig, - body, - }) - } -} - -/// An item in a [`ItemApiMod`], which can be a regular [`Item`] or an [API function](`ItemApiFn`). As `ItemApiFn`, this -/// enum represents both the definition and the implementation. -pub enum ApiModItem { - Regular(Item), - ApiFn(ItemApiFn), -} - -impl Parse for ApiModItem { - fn parse(input: ParseStream) -> syn::Result { - // Attributes will be parsed twice, but it's not a big deal. - let forked = input.fork(); - let _ = forked.call(Attribute::parse_outer)?; - let is_api_fn = forked.peek(Token![extern]) && forked.peek2(Token![fn]); - drop(forked); - - if is_api_fn { - Ok(Self::ApiFn(input.parse()?)) - } else { - Ok(Self::Regular(input.parse()?)) - } - } -} - -/// A module that contains the definition or implementation of API functions, and marked by `#[api_mod]` or -/// `#[api_mod_impl]`. -pub struct ItemApiMod { - /// Attributes of the module. - pub attrs: Vec, - /// Visibility of the module. - pub vis: Visibility, - /// The `mod` keyword. - pub mod_token: Token![mod], - /// The identifier of the module. - pub ident: Ident, - #[expect(dead_code)] - /// The brace token. - pub brace_token: Brace, - /// The items in the module. - pub items: Vec>, -} - -impl Parse for ItemApiMod { - fn parse(input: ParseStream) -> syn::Result { - let attrs = input.call(Attribute::parse_outer)?; - let vis = input.parse()?; - let mod_token = input.parse()?; - let ident = input.parse()?; - - let content; - let brace_token = braced!(content in input); - let mut items = vec![]; - - while !content.is_empty() { - items.push(content.parse()?); - } - - Ok(Self { - attrs, - vis, - mod_token, - ident, - brace_token, - items, - }) - } -} - -/// A module that contains the definition of API functions, marked by `#[api_mod]`. -pub type ItemApiModDef = ItemApiMod; -/// A module that contains the implementation of API functions, marked by `#[api_mod_impl]`. -pub type ItemApiModImpl = ItemApiMod; diff --git a/axvisor_api_proc/src/lib.rs b/axvisor_api_proc/src/lib.rs index a538a1006..b8f1c9080 100644 --- a/axvisor_api_proc/src/lib.rs +++ b/axvisor_api_proc/src/lib.rs @@ -2,14 +2,10 @@ use proc_macro::TokenStream as TokenStream1; use proc_macro_crate::{FoundCrate, crate_name}; use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned}; -use syn::{FnArg, Ident, Path, Token, spanned::Spanned}; - -mod items; - -use items::{ApiModItem, ItemApiFn, ItemApiModDef, ItemApiModImpl}; +use syn::{Ident, spanned::Spanned}; /// Find the path to the `axvisor_api` crate. -fn find_axvisor_api_crate() -> TokenStream { +fn axvisor_api_crate() -> TokenStream { match crate_name("axvisor_api") { Ok(FoundCrate::Itself) => quote! { crate }, Ok(FoundCrate::Name(name)) => { @@ -20,254 +16,63 @@ fn find_axvisor_api_crate() -> TokenStream { } } -/// Capitalize the first letter of a string. -/// -/// From: `https://stackoverflow.com/questions/38406793/why-is-capitalizing-the-first-letter-of-a-string-so-convoluted-in-rust` -fn capitalize_first_letter(s: &str) -> String { - let mut c = s.chars(); - match c.next() { - None => String::new(), - Some(f) => f.to_uppercase().collect::() + c.as_str(), - } -} - -/// Get the name of the API trait for an API module. -fn get_api_trait_name(module_name: impl AsRef, span: Span) -> Ident { - let module_name = module_name.as_ref(); - let trait_name = format!("Axvisor{}ApiTrait", capitalize_first_letter(module_name)); - Ident::new(&trait_name, span) -} - -/// Get the extra doc comments for an API module definition. -fn get_api_mod_def_extra_doc_comments( - mod_ident: &Ident, - api_fn_items: &Vec<&ItemApiFn>, -) -> TokenStream { - if api_fn_items.is_empty() { - return quote! { - #[doc = ""] - #[doc = "This module does not contain any API functions to be implemented."] - }; - } - - let mod_name = mod_ident.to_string(); - let api_fn_count = api_fn_items.len(); - let api_fn_count_hint = format!( - "This module contains {} API function{} to be implemented:", - api_fn_count, - if api_fn_count == 1 { "" } else { "s" } - ); - let api_fn_list = api_fn_items - .iter() - .map(|f| format!("- [`{0}`]({1}::{0})", f.sig.ident.to_string(), mod_name)); - - quote! { - #[doc = ""] - #[doc = #api_fn_count_hint] - #( - #[doc = #api_fn_list] - )* - } -} - -fn get_api_fn_def_extra_doc_comments() -> TokenStream { - quote! { - #[doc = ""] - #[doc = "This function is an API function and **should be implemented somewhere**."] - } +/// The namespace used for AxVisor APIs when calling `crate_interface` macros. +fn axvisor_api_namespace() -> Ident { + const AXVISOR_API_NS: &str = "AxVisorApi"; + Ident::new(AXVISOR_API_NS, Span::call_site()) } -/// Process an API module definition. -fn process_api_mod_def(module: ItemApiModDef) -> TokenStream { - let attrs = &module.attrs; - let vis = &module.vis; - let mod_token = &module.mod_token; - let mod_ident = &module.ident; - - // Split the items into regular items and API functions - let mut regular_items = vec![]; - let mut api_fn_items = vec![]; - - for item in &module.items { - match item { - ApiModItem::Regular(item) => regular_items.push(item), - ApiModItem::ApiFn(item) => api_fn_items.push(item), - } - } - - let extra_doc_comments = get_api_mod_def_extra_doc_comments(&mod_ident, &api_fn_items); - - if api_fn_items.is_empty() { - return quote! { - #(#attrs)* - #extra_doc_comments - #vis #mod_token #mod_ident { - #(#regular_items)* - } - }; - } - - let axvisor_api_path = find_axvisor_api_crate(); - - // Generate the API trait - let trait_ident = get_api_trait_name(mod_ident.to_string(), mod_token.span()); - let api_fn_attrs = api_fn_items - .iter() - .map(|item| &item.attrs) - .collect::>(); - let api_fn_signatures = api_fn_items - .iter() - .map(|item| &item.sig) - .collect::>(); - - let trait_def = quote! { - #[doc(hidden)] - #[#axvisor_api_path::__priv::crate_interface::def_interface] - #[allow(non_camel_case_types)] - pub trait #trait_ident { - #(#(#api_fn_attrs)* #api_fn_signatures;)* +macro_rules! assert_empty_attr { + ($attr:expr) => { + if !$attr.is_empty() { + return (quote_spanned! { + TokenStream::from($attr).span() => compile_error!("This attribute does not accept any arguments") + }).into(); } }; - - // Generate the API function implementations - let mut api_fn_impls = quote! {}; - for api_fn_item in api_fn_items { - let attrs = &api_fn_item.attrs; - let sig = &api_fn_item.sig; - let fn_name = &sig.ident; - let args = &sig - .inputs - .iter() - .map(|arg| match arg { - FnArg::Receiver(_) => panic!("API functions cannot have self arguments"), - FnArg::Typed(pat) => &pat.pat, - }) - .collect::>(); - - let extra_doc_comments = get_api_fn_def_extra_doc_comments(); - - api_fn_impls.extend(quote! { - #(#attrs)* - #extra_doc_comments - pub #sig { - #axvisor_api_path::__priv::crate_interface::call_interface!( - #trait_ident::#fn_name, #(#args),* - ) - } - }); - } - - quote! { - #(#attrs)* - #extra_doc_comments - #vis #mod_token #mod_ident { - #(#regular_items)* - - #api_fn_impls - - #trait_def - } - } -} - -/// Reuses the path to the module to be implemented, to make sure the `impl` block can find the correct trait. -fn get_implementee_reuse_ident(implementee: &Path) -> Ident { - let mut ident = String::from(if implementee.leading_colon.is_some() { - "__axvisor_api_implementee_abs" - } else { - "__axvisor_api_implementee_rel" - }); - - for seg in implementee.segments.iter() { - ident.push('_'); - ident.push_str(seg.ident.to_string().as_str()); - } - - Ident::new(&ident, implementee.span()) } -/// Process an API module implementation. -fn process_api_mod_impl(implementee: Path, input: ItemApiModImpl) -> TokenStream { - let attrs = &input.attrs; - let vis = &input.vis; - let mod_token = &input.mod_token; - let mod_ident = &input.ident; - - let implementee_name = match implementee.segments.last() { - Some(segment) => segment.ident.to_string(), - None => return quote! { compile_error!("Invalid implementee path") }, - }; - let implementee_trait_ident = get_api_trait_name(&implementee_name, implementee.span()); - // we should reuse the implementee mod path besides the implementing mod, to make sure the `impl` block can find - // the corrent trait. - let implementee_reuse_ident = get_implementee_reuse_ident(&implementee); - - let axvisor_api_path = find_axvisor_api_crate(); - - let mut regular_items = vec![]; - let mut api_fn_items = vec![]; - for item in input.items { - match item { - ApiModItem::Regular(item) => regular_items.push(item), - ApiModItem::ApiFn(item) => api_fn_items.push(item), - } - } - - let mut api_fn_impls = TokenStream::new(); - for api_fn_item in api_fn_items { - let attrs = &api_fn_item.attrs; - let sig = &api_fn_item.sig; - let body = &api_fn_item.body; +/// Define an AxVisor API interface. +/// +/// This macro is applied to a trait definition. It generates the necessary +/// boilerplate code to register the trait as an AxVisor API interface. No +/// arguments are accepted. +/// +/// This macro uses `crate_interface::def_interface` internally. +#[proc_macro_attribute] +pub fn api_def(attr: TokenStream1, input: TokenStream1) -> TokenStream1 { + assert_empty_attr!(attr); - api_fn_impls.extend(quote! { - #(#attrs)* - #sig #body - }); - } + let axvisor_api_path = axvisor_api_crate(); + let ns = axvisor_api_namespace(); + let input: TokenStream = syn::parse_macro_input!(input as TokenStream); quote! { - #[doc(hidden)] - use #implementee as #implementee_reuse_ident; - - #(#attrs)* - #vis #mod_token #mod_ident { - #(#regular_items)* - - #[doc(hidden)] - pub struct __Impl; - #[#axvisor_api_path::__priv::crate_interface::impl_interface] - impl super::#implementee_reuse_ident::#implementee_trait_ident for __Impl { - #api_fn_impls - } - } + #[#axvisor_api_path::__priv::crate_interface::def_interface(gen_caller, namespace = #ns)] + #input } + .into() } -#[proc_macro_attribute] -/// Define a module containing API functions. +/// Implement an AxVisor API interface. /// -/// The module can contain regular items and API functions. API functions are defined with the `extern fn` syntax. +/// This macro is applied to an `impl` block that implements a trait previously +/// defined with `api_def`. It generates the necessary boilerplate code to +/// register the implementation as an AxVisor API implementation. No arguments +/// are accepted. /// -/// **Does not work on outlined modules.** (i.e. `mod foo;` with content in `foo.rs`) -pub fn api_mod(attr: TokenStream1, input: TokenStream1) -> TokenStream1 { - if !attr.is_empty() { - return (quote_spanned! { - TokenStream::from(attr).span() => compile_error!("`api_mod` attribute does not accept any arguments") - }).into(); - } +/// This macro uses `crate_interface::impl_interface` internally. +#[proc_macro_attribute] +pub fn api_impl(attr: TokenStream1, input: TokenStream1) -> TokenStream1 { + assert_empty_attr!(attr); - process_api_mod_def(syn::parse_macro_input!(input as ItemApiModDef)).into() -} + let axvisor_api_path = axvisor_api_crate(); + let ns = axvisor_api_namespace(); + let input: TokenStream = syn::parse_macro_input!(input as TokenStream); -#[proc_macro_attribute] -/// Implement the API functions defined in another module. -/// -/// The module should contain the implementation of the API functions defined in another module. The path to the module -/// defining the APIs should be passed as the argument. -pub fn api_mod_impl(attr: TokenStream1, input: TokenStream1) -> TokenStream1 { - process_api_mod_impl( - syn::parse_macro_input!(attr as Path), - syn::parse_macro_input!(input as ItemApiModImpl), - ) + quote! { + #[#axvisor_api_path::__priv::crate_interface::impl_interface(namespace = #ns)] + #input + } .into() } diff --git a/examples/example.rs b/examples/example.rs index bc6a356f5..ddf889444 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,31 +1,43 @@ extern crate axvisor_api; extern crate memory_addr; -use axvisor_api::{__priv, api_mod, api_mod_impl}; +use axvisor_api::__priv; -#[api_mod] -/// Memory-related API pub mod some_demo { + use memory_addr::MemoryAddr; pub use memory_addr::PhysAddr; - /// Some function - extern fn some_func() -> PhysAddr; - /// Another function - extern fn another_func(addr: PhysAddr); + #[axvisor_api::api_def] + pub trait SomeDemoIf { + /// Some function provided by the implementer + fn some_func() -> PhysAddr; + /// Another function provided by the implementer + fn another_func(addr: PhysAddr); + } + + /// Some function provided by the API definer + pub fn provided_func() -> PhysAddr { + some_func().add(0x1000) + } } -#[api_mod_impl(some_demo)] -mod some_impl { - use memory_addr::{PhysAddr, pa}; +mod some_demo_impl { + use crate::some_demo::SomeDemoIf; - extern fn some_func() -> PhysAddr { - return pa!(0x42); - } + pub struct SomeDemoImpl; + + #[axvisor_api::api_impl] + impl SomeDemoIf for SomeDemoImpl { + fn some_func() -> memory_addr::PhysAddr { + memory_addr::pa!(0x42) + } - extern fn another_func(addr: PhysAddr) { - println!("Wow, the answer is {:?}", addr); + fn another_func(addr: memory_addr::PhysAddr) { + println!("Wow, the answer is {:?}", addr); + } } } fn main() { some_demo::another_func(some_demo::some_func()); + some_demo::another_func(some_demo::provided_func()); } diff --git a/src/arch.rs b/src/arch.rs new file mode 100644 index 000000000..c11b030e7 --- /dev/null +++ b/src/arch.rs @@ -0,0 +1,25 @@ +//! Architecture-specific APIs. + +use super::{memory::PhysAddr, vmm::InterruptVector}; + +/// The API trait for architecture-specific functionalities. +#[crate::api_def] +pub trait ArchIf { + /// Inject a virtual interrupt to the current virtual CPU. + #[cfg(target_arch = "aarch64")] + fn hardware_inject_virtual_interrupt(vector: InterruptVector); + + /// Get the TYPER register of the GIC distributor. Used in virtual GIC initialization. + #[cfg(target_arch = "aarch64")] + fn read_vgicd_typer() -> u32; + /// Get the IIDR register of the GIC distributor. Used in virtual GIC initialization. + #[cfg(target_arch = "aarch64")] + fn read_vgicd_iidr() -> u32; + + /// Get the base address of the GIC distributor in the host system. + #[cfg(target_arch = "aarch64")] + fn get_host_gicd_base() -> PhysAddr; + /// Get the base address of the GIC redistributor in the host system. + #[cfg(target_arch = "aarch64")] + fn get_host_gicr_base() -> PhysAddr; +} diff --git a/src/host.rs b/src/host.rs new file mode 100644 index 000000000..5d27eabc1 --- /dev/null +++ b/src/host.rs @@ -0,0 +1,8 @@ +//! Host system related APIs. + +/// The API trait for host system functionalities. +#[crate::api_def] +pub trait HostIf { + /// Get the total number of cpus in the host system. + fn get_host_cpu_num() -> usize; +} diff --git a/src/lib.rs b/src/lib.rs index 10a25aec5..fc50c8106 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,274 +1,142 @@ -//! `axvisor_api` is a library that provides: -//! - a set of Rust API for all components of the `axvisor` Hypervisor, including: -//! -//! - memory management, -//! - time and timer management, -//! - interrupt management, -//! - ... -//! -//! these APIs are defined here, should be implemented by the axvisor Hypervisor, and can be use by all components. -//! -//! - a standard way to define and implement APIs, including the [`api_mod`] and the [`api_mod_impl`] attributes, which -//! the components can utilize to define and implement their own APIs. +//! `axvisor_api` is the bottom-most crate of the AxVisor Hypervisor project. It +//! provides a standardized set of APIs for the components of the Hypervisor and +//! grants them access to OS-level and Hypervisor-level functionalities, like +//! memory allocation, address conversion, time and timer management, cross-vcpu +//! operations, and so on. +//! +//! `axvisor_api` is designed for two main purposes: +//! - **Replace generic-based API-injection mechanism.** +//! +//! Generic-based API-injection mechanism, for example: +//! +//! ```rust +//! pub trait VCpuHal { +//! /* ... */ +//! } +//! pub struct VCpu { +//! /* ... */ +//! # _marker: core::marker::PhantomData, +//! } +//! ``` +//! +//! has been widely used previously here and in other related projects. It's +//! definitely great, with zero overhead, good readability, and no link-time +//! magic. However, it turns out that passing generic parameters down through +//! multiple layers of modules and components is quite inconvenient, and it's +//! always a pain to decide how to categorize the APIs into different traits +//! properly. +//! +//! Finally, the author decided to just use a big monolithic trait for every +//! component, and use `crate_interface` to eliminate the generic parameter. +//! (Although there are multiple traits in this crate, technically in the +//! current implementation the link-time symbol space is used as a big +//! monolithic trait.) Theoretically, this may slightly increase the +//! difficulty of re-using a single component in a new software project, but +//! the author believes that it will reduce the overall complexity when not +//! one but a few related components are re-used together. +//! +//! - **Enable portability of the Hypervisor across different unikernels.** +//! +//! Technically, the whole AxVisor Hypervisor can be ported to different +//! unikernels by implementing the APIs defined in this crate (although not +//! tested yet). If such porting is successful, this crate can also be used as +//! a tested hardware-and-unikernel abstraction layer for other hypervisor +//! projects. +//! +//! This crate also provides a standard way to define and implement APIs, with +//! the [`api_def`] and [`api_impl`] procedural macros. They are built on top of +//! the `crate_interface` crate, which provides the low-level functionalities +//! of defining and implementing crate-level interfaces. //! //! # How to define and implement APIs //! //! ## Define APIs //! -//! To define APIs, you can use the `api_mod` attribute to define a module containing the API functions. An API -//! function is defined with the `extern fn` syntax. Note that Vanilla Rust does not support defining extern functions -//! in such a way, so the definition of the API functions can easily be distinguished from the regular functions. +//! To define APIs, you can use the `api_def` attribute on a trait defining the +//! API, with each API function defined as a regular function in the trait. It's +//! recommended to pack the trait definition and related definitions (like type +//! aliases) into a module for better organization. //! //! ```rust, standalone_crate -//! # use axvisor_api::{api_mod, __priv}; // some inconviniece brought by proc-macro-name and doctest +//! # // some inconvenience brought by proc-macro-name and doctest +//! # use axvisor_api::__priv; //! # fn main() {} -//! #[api_mod] -//! /// Memory-related API -//! pub mod memory_demo { -//! pub use memory_addr::PhysAddr; +//! mod example { +//! # // some inconvenience brought by proc-macro-name and doctest +//! # use axvisor_api::api_def; +//! /// Example API definition +//! #[api_def] +//! pub trait ExampleIf { +//! /// An example API function +//! fn example_func(arg: usize) -> usize; +//! /// Another example API function +//! fn another_func(); +//! } +//! } //! -//! /// Allocate a frame -//! extern fn alloc_frame() -> Option; -//! /// Deallocate a frame -//! extern fn dealloc_frame(addr: PhysAddr); +//! fn use_example_api() { +//! let result = example::example_func(42); +//! example::another_func(); //! } //! ``` //! -//! Defined APIs can be invoked by other components: +//! `api_def` will generate a caller function for each API function defined in +//! the trait, at the same level as the trait definition. The generated callers +//! can be used to invoke the API functions, as demonstrated above. //! -//! ```rust, no_run, standalone_crate -//! # use axvisor_api::{api_mod, __priv}; // some inconviniece brought by proc-macro-name and doctest -//! # fn main() {} -//! # #[api_mod] -//! # /// Memory-related API -//! # pub mod memory_demo { -//! # pub use memory_addr::PhysAddr; -//! # -//! # /// Allocate a frame -//! # extern fn alloc_frame() -> Option; -//! # /// Deallocate a frame -//! # extern fn dealloc_frame(addr: PhysAddr); -//! # } -//! struct SomeComponent; +//! ## Implement APIs //! -//! impl SomeComponent { -//! fn some_method() { -//! let frame = memory_demo::alloc_frame().unwrap(); -//! // Do something with the frame -//! memory_demo::dealloc_frame(frame); +//! Defined APIs should be implemented somewhere, unless they are not used +//! anywhere. To implement APIs, the implementer should define an empty struct +//! and implement the API trait for the struct, with the `api_impl` attribute on +//! the `impl` block. For example, +//! +//! ```rust, standalone_crate +//! # // some inconvenience brought by proc-macro-name and doctest +//! # use axvisor_api::{api_impl, __priv}; +//! mod example { +//! # // some inconvenience brought by proc-macro-name and doctest +//! # use axvisor_api::{api_def, __priv}; +//! /// Example API definition +//! #[api_def] +//! pub trait ExampleIf { +//! /// An example API function +//! fn example_func(arg: usize) -> usize; +//! /// Another example API function +//! fn another_func(); //! } //! } -//! ``` -//! -//! ## Implement APIs -//! -//! Defined APIs should be implemented by the Hypervisor, or other components that are able and responsible to do so. To -//! implement APIs, you can use the `api_mod_impl` attribute, with the path of the module defining the APIs as the -//! argument, on a module containing the implementation of the API functions. The implementations of the API functions -//! are also defined with the `extern fn` syntax. //! -//! ```rust, no_run, standalone_crate -//! # use axvisor_api::{api_mod, api_mod_impl, __priv}; // some inconviniece brought by proc-macro-name and doctest -//! # fn main() {} -//! # #[api_mod] -//! # /// Memory-related API -//! # pub mod memory_demo { -//! # pub use memory_addr::PhysAddr; -//! # -//! # /// Allocate a frame -//! # extern fn alloc_frame() -> Option; -//! # /// Deallocate a frame -//! # extern fn dealloc_frame(addr: PhysAddr); -//! # } -//! #[api_mod_impl(memory_demo)] -//! mod memory_impl { -//! use memory_addr::PhysAddr; +//! struct ExampleImpl; //! -//! extern fn alloc_frame() -> Option { -//! // Implementation of the `alloc_frame` API -//! todo!() +//! #[api_impl] +//! impl example::ExampleIf for ExampleImpl { +//! fn example_func(arg: usize) -> usize { +//! arg + 1 //! } //! -//! extern fn dealloc_frame(addr: PhysAddr) { -//! // Implementation of the `dealloc_frame` API -//! todo!() +//! fn another_func() { +//! println!("Another function called"); //! } //! } -//! ``` //! -//! ## Tricks behind the macros -//! -//! [`api_mod`] and [`api_mod_impl`] are wrappers around the great [`crate_interface`] crate, with some macro tricks to -//! make the usage more convenient. +//! fn main() { +//! let result = example::example_func(42); +//! assert_eq!(result, 43); +//! example::another_func(); // prints "Another function called" +//! } +//! ``` //! #![no_std] -pub use axvisor_api_proc::{api_mod, api_mod_impl}; - -#[api_mod] -/// Memory-related API. -pub mod memory { - pub use memory_addr::{PhysAddr, VirtAddr}; - - // API interfaces +pub use axvisor_api_proc::{api_def, api_impl}; - /// Allocate a frame. - extern fn alloc_frame() -> Option; - /// Allocate a number of contiguous frames, with a specified alignment. - extern fn alloc_contiguous_frames( - num_frames: usize, - frame_align_pow2: usize, - ) -> Option; - /// Deallocate a frame. - extern fn dealloc_frame(addr: PhysAddr); - /// Deallocate a number of contiguous frames. - extern fn dealloc_contiguous_frames(first_addr: PhysAddr, num_frames: usize); - /// Convert a physical address to a virtual address. - extern fn phys_to_virt(addr: PhysAddr) -> VirtAddr; - /// Convert a virtual address to a physical address. - extern fn virt_to_phys(addr: VirtAddr) -> PhysAddr; - - // Re-exports - // TODO: determine whether it's proper and acceptable to place this definition here in this mod. - /// [`AxMmHal`](axaddrspace::AxMmHal) implementation by axvisor_api. - #[doc(hidden)] - pub struct AxMmHalApiImpl; - - impl axaddrspace::AxMmHal for AxMmHalApiImpl { - fn alloc_frame() -> Option { - alloc_frame() - } - - fn dealloc_frame(addr: PhysAddr) { - dealloc_frame(addr) - } - - fn phys_to_virt(addr: PhysAddr) -> VirtAddr { - phys_to_virt(addr) - } - - fn virt_to_phys(addr: VirtAddr) -> PhysAddr { - virt_to_phys(addr) - } - } - - /// A physical frame which will be automatically deallocated when dropped. - pub type PhysFrame = axaddrspace::PhysFrame; -} - -#[api_mod] -/// Time-and-timer-related API. -pub mod time { - extern crate alloc; - use alloc::boxed::Box; - use core::time::Duration; - - /// Time value. - pub type TimeValue = Duration; - /// Nanoseconds count. - pub type Nanos = u64; - /// Tick count. - pub type Ticks = u64; - /// Cancel token, used to cancel a scheduled timer event. - pub type CancelToken = usize; - - /// Get the current tick count. - extern fn current_ticks() -> Ticks; - /// Get the current time in nanoseconds. - pub fn current_time_nanos() -> Nanos { - ticks_to_nanos(current_ticks()) - } - /// Get the current time. - pub fn current_time() -> TimeValue { - Duration::from_nanos(current_time_nanos()) - } - - /// Convert ticks to nanoseconds. - extern fn ticks_to_nanos(ticks: Ticks) -> Nanos; - /// Convert ticks to time. - pub fn ticks_to_time(ticks: Ticks) -> TimeValue { - Duration::from_nanos(ticks_to_nanos(ticks)) - } - /// Convert nanoseconds to ticks. - extern fn nanos_to_ticks(nanos: Nanos) -> Ticks; - /// Convert time to ticks. - pub fn time_to_ticks(time: TimeValue) -> Ticks { - nanos_to_ticks(time.as_nanos() as Nanos) - } - - /// Register a timer. - extern fn register_timer( - deadline: TimeValue, - callback: Box, - ) -> CancelToken; - /// Cancel a timer. - extern fn cancel_timer(token: CancelToken); -} - -#[api_mod] -/// Virtual machine management API. -pub mod vmm { - /// Virtual machine ID. - pub type VMId = usize; - /// Virtual CPU ID. - pub type VCpuId = usize; - /// Interrupt vector. - pub type InterruptVector = u8; - - /// Get the ID of the current virtual machine. - extern fn current_vm_id() -> VMId; - /// Get the ID of the current virtual CPU. - extern fn current_vcpu_id() -> VCpuId; - /// Get the number of virtual CPUs in a virtual machine. - extern fn vcpu_num(vm_id: VMId) -> Option; - /// Get the mask of active virtual CPUs in a virtual machine. - extern fn active_vcpus(vm_id: VMId) -> Option; - /// Get the number of virtual CPUs in the current virtual machine. - pub fn current_vm_vcpu_num() -> usize { - vcpu_num(current_vm_id()).unwrap() - } - /// Get the mask of active virtual CPUs in the current virtual machine. - pub fn current_vm_active_vcpus() -> usize { - active_vcpus(current_vm_id()).unwrap() - } - - /// Inject an interrupt to a virtual CPU. - extern fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector); - /// Notify that a virtual CPU timer has expired. - /// - /// TODO: determine whether we can skip this function. - extern fn notify_vcpu_timer_expired(vm_id: VMId, vcpu_id: VCpuId); -} - -#[api_mod] -pub mod host { - /// Get the total number of cpus in the host system. - extern fn get_host_cpu_num() -> usize; -} - -#[api_mod] -pub mod arch { - use super::vmm::InterruptVector; - - #[cfg(target_arch = "aarch64")] - /// AArch64-specific API. Inject a virtual interrupt to the current virtual CPU using gich. - extern fn hardware_inject_virtual_interrupt(vector: InterruptVector); - - #[cfg(target_arch = "aarch64")] - /// AArch64-specific API. Get the TYPER register of the GIC distributor. Used in virtual GIC initialization. - extern fn read_vgicd_typer() -> u32; - #[cfg(target_arch = "aarch64")] - /// AArch64-specific API. Get the IIDR register of the GIC distributor. Used in virtual GIC initialization. - extern fn read_vgicd_iidr() -> u32; - - #[cfg(target_arch = "aarch64")] - /// AArch64-specific API. Get the base address of the GIC distributor in the host system. - extern fn get_host_gicd_base() -> crate::memory::PhysAddr; - #[cfg(target_arch = "aarch64")] - /// AArch64-specific API. Get the base address of the GIC redistributor in the host system. - extern fn get_host_gicr_base() -> crate::memory::PhysAddr; -} +pub mod arch; +pub mod host; +pub mod memory; +pub mod time; +pub mod vmm; #[doc(hidden)] pub mod __priv { diff --git a/src/memory.rs b/src/memory.rs new file mode 100644 index 000000000..cc196821f --- /dev/null +++ b/src/memory.rs @@ -0,0 +1,46 @@ +//! Memory allocation and address translation APIs. + +pub use memory_addr::{PhysAddr, VirtAddr}; + +/// The API trait for memory allocation and address translation functionalities. +#[crate::api_def] +pub trait MemoryIf { + /// Allocate a frame. + fn alloc_frame() -> Option; + /// Allocate a number of contiguous frames, with a specified alignment. + fn alloc_contiguous_frames(num_frames: usize, frame_align_pow2: usize) -> Option; + /// Deallocate a frame allocated previously by [`alloc_frame`]. + fn dealloc_frame(addr: PhysAddr); + /// Deallocate a number of contiguous frames allocated previously by + /// [`alloc_contiguous_frames`]. + fn dealloc_contiguous_frames(first_addr: PhysAddr, num_frames: usize); + /// Convert a physical address to a virtual address. + fn phys_to_virt(addr: PhysAddr) -> VirtAddr; + /// Convert a virtual address to a physical address. + fn virt_to_phys(addr: VirtAddr) -> PhysAddr; +} + +/// [`AxMmHal`](axaddrspace::AxMmHal) implementation by axvisor_api. +#[doc(hidden)] +pub struct AxMmHalApiImpl; + +impl axaddrspace::AxMmHal for AxMmHalApiImpl { + fn alloc_frame() -> Option { + alloc_frame() + } + + fn dealloc_frame(addr: PhysAddr) { + dealloc_frame(addr) + } + + fn phys_to_virt(addr: PhysAddr) -> VirtAddr { + phys_to_virt(addr) + } + + fn virt_to_phys(addr: VirtAddr) -> PhysAddr { + virt_to_phys(addr) + } +} + +/// A physical frame which will be automatically deallocated when dropped. +pub type PhysFrame = axaddrspace::PhysFrame; diff --git a/src/test.rs b/src/test.rs index 4aff434df..2dc1eb9b3 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,54 +1,66 @@ use memory_addr::{pa, va}; -/// A demonstration of the `memory` API implementation. -#[crate::api_mod_impl(crate::memory)] mod memory_impl { - use core::sync::atomic::AtomicUsize; + extern crate std; // in test only + use memory_addr::{PhysAddr, VirtAddr, pa, va}; + use std::sync::{ + Mutex, MutexGuard, + atomic::{AtomicUsize, Ordering}, + }; static ALLOCATED: AtomicUsize = AtomicUsize::new(0); static RETURNED_SUM: AtomicUsize = AtomicUsize::new(0); + static LOCK: Mutex<()> = Mutex::new(()); pub const VA_PA_OFFSET: usize = 0x1000; - extern fn alloc_frame() -> Option { - let value = ALLOCATED.fetch_add(1, core::sync::atomic::Ordering::SeqCst); + pub struct MemoryIfImpl; - Some(pa!(value * 0x1000)) - } + #[crate::api_impl] + impl crate::memory::MemoryIf for MemoryIfImpl { + fn alloc_frame() -> Option { + let value = ALLOCATED.fetch_add(1, Ordering::Relaxed); - extern fn alloc_contiguous_frames( - _num_frames: usize, - _frame_align_pow2: usize, - ) -> Option { - unimplemented!(); - } + Some(pa!(value * 0x1000)) + } - extern fn dealloc_frame(addr: PhysAddr) { - RETURNED_SUM.fetch_add(addr.as_usize(), core::sync::atomic::Ordering::SeqCst); - } + fn alloc_contiguous_frames( + _num_frames: usize, + _frame_align_pow2: usize, + ) -> Option { + unimplemented!(); + } + + fn dealloc_frame(addr: PhysAddr) { + RETURNED_SUM.fetch_add(addr.as_usize(), Ordering::Relaxed); + } + + fn dealloc_contiguous_frames(_first_addr: PhysAddr, _num_frames: usize) { + unimplemented!(); + } + + fn phys_to_virt(addr: PhysAddr) -> VirtAddr { + va!(addr.as_usize() + VA_PA_OFFSET) // Example implementation + } - extern fn dealloc_contiguous_frames(_first_addr: PhysAddr, _num_frames: usize) { - unimplemented!(); + fn virt_to_phys(addr: VirtAddr) -> PhysAddr { + pa!(addr.as_usize() - VA_PA_OFFSET) // Example implementation + } } /// Get the sum of all returned physical addresses. /// /// Note that this function demonstrates that non-API functions work well in a module with the `api_mod_impl` attribute. pub fn get_returned_sum() -> usize { - RETURNED_SUM.load(core::sync::atomic::Ordering::SeqCst) + RETURNED_SUM.load(Ordering::Relaxed) } - pub fn clear() { - ALLOCATED.store(0, core::sync::atomic::Ordering::SeqCst); - RETURNED_SUM.store(0, core::sync::atomic::Ordering::SeqCst); - } - - extern fn phys_to_virt(addr: PhysAddr) -> VirtAddr { - va!(addr.as_usize() + VA_PA_OFFSET) // Example implementation - } - - extern fn virt_to_phys(addr: VirtAddr) -> PhysAddr { - pa!(addr.as_usize() - VA_PA_OFFSET) // Example implementation + /// Start a test by acquiring the lock and resetting the internal state. + pub fn enter_test() -> MutexGuard<'static, ()> { + let guard = LOCK.lock().unwrap(); + ALLOCATED.store(0, Ordering::Relaxed); + RETURNED_SUM.store(0, Ordering::Relaxed); + guard } } @@ -56,7 +68,7 @@ mod memory_impl { pub fn test_memory() { use crate::memory; - memory_impl::clear(); + let guard = memory_impl::enter_test(); let frame1 = memory::alloc_frame(); let frame2 = memory::alloc_frame(); @@ -75,14 +87,15 @@ pub fn test_memory() { assert_eq!(memory::phys_to_virt(pa!(0)), va!(memory_impl::VA_PA_OFFSET)); assert_eq!(memory::virt_to_phys(va!(memory_impl::VA_PA_OFFSET)), pa!(0)); + + drop(guard); } #[test] pub fn test_memory_phys_frame() { - use crate::memory; - use crate::memory::PhysFrame; + use crate::memory::{self, PhysFrame}; - memory_impl::clear(); + let guard = memory_impl::enter_test(); let _ = memory::alloc_frame(); let frame1 = PhysFrame::alloc().unwrap(); @@ -99,4 +112,6 @@ pub fn test_memory_phys_frame() { assert_eq!(memory_impl::get_returned_sum(), 0x5000); drop(frame1); assert_eq!(memory_impl::get_returned_sum(), 0x6000); + + drop(guard); } diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 000000000..1e8129fcc --- /dev/null +++ b/src/time.rs @@ -0,0 +1,53 @@ +//! Time and timer APIs. + +extern crate alloc; + +use alloc::boxed::Box; +use core::time::Duration; + +/// Time value. +pub type TimeValue = Duration; +/// Nanoseconds count. +pub type Nanos = u64; +/// Tick count. +pub type Ticks = u64; +/// Cancel token, used to cancel a scheduled timer event. +pub type CancelToken = usize; + +/// The API trait for time and timer functionalities. +#[crate::api_def] +pub trait TimeIf { + /// Get the current tick count. + fn current_ticks() -> Ticks; + /// Convert ticks to nanoseconds. + fn ticks_to_nanos(ticks: Ticks) -> Nanos; + /// Convert nanoseconds to ticks. + fn nanos_to_ticks(nanos: Nanos) -> Ticks; + /// Register a timer. + fn register_timer( + deadline: TimeValue, + callback: Box, + ) -> CancelToken; + /// Cancel a timer. + fn cancel_timer(token: CancelToken); +} + +/// Get the current time in nanoseconds. +pub fn current_time_nanos() -> Nanos { + ticks_to_nanos(current_ticks()) +} + +/// Get the current time. +pub fn current_time() -> TimeValue { + Duration::from_nanos(current_time_nanos()) +} + +/// Convert ticks to time. +pub fn ticks_to_time(ticks: Ticks) -> TimeValue { + Duration::from_nanos(ticks_to_nanos(ticks)) +} + +/// Convert time to ticks. +pub fn time_to_ticks(time: TimeValue) -> Ticks { + nanos_to_ticks(time.as_nanos() as Nanos) +} diff --git a/src/vmm.rs b/src/vmm.rs new file mode 100644 index 000000000..5d3ee2537 --- /dev/null +++ b/src/vmm.rs @@ -0,0 +1,37 @@ +//! Virtual machine management APIs. + +/// Virtual machine ID. +pub type VMId = usize; +/// Virtual CPU ID. +pub type VCpuId = usize; +/// Interrupt vector. +pub type InterruptVector = u8; + +/// The API trait for virtual machine management functionalities. +#[crate::api_def] +pub trait VmmIf { + /// Get the ID of the current virtual machine. + fn current_vm_id() -> VMId; + /// Get the ID of the current virtual CPU. + fn current_vcpu_id() -> VCpuId; + /// Get the number of virtual CPUs in a virtual machine. + fn vcpu_num(vm_id: VMId) -> Option; + /// Get the mask of active virtual CPUs in a virtual machine. + fn active_vcpus(vm_id: VMId) -> Option; + /// Inject an interrupt to a virtual CPU. + fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector); + /// Notify that a virtual CPU timer has expired. + /// + /// TODO: determine whether we can skip this function. + fn notify_vcpu_timer_expired(vm_id: VMId, vcpu_id: VCpuId); +} + +/// Get the number of virtual CPUs in the current virtual machine. +pub fn current_vm_vcpu_num() -> usize { + vcpu_num(current_vm_id()).unwrap() +} + +/// Get the mask of active virtual CPUs in the current virtual machine. +pub fn current_vm_active_vcpus() -> usize { + active_vcpus(current_vm_id()).unwrap() +} From 8406c5c0df9ef0e14ce238fd62c9ab372e09adf8 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Wed, 21 Jan 2026 21:37:45 +0800 Subject: [PATCH 036/132] adopt `aar-master` branch, remove `HalImpl`s --- Cargo.lock | 754 +++--------------- Cargo.toml | 26 +- kernel/Cargo.toml | 6 +- kernel/src/hal/impl_host.rs | 11 + kernel/src/hal/impl_memory.rs | 53 ++ kernel/src/hal/impl_time.rs | 33 + kernel/src/hal/impl_vmm.rs | 75 ++ kernel/src/hal/mod.rs | 195 +---- kernel/src/vmm/mod.rs | 11 +- modules/axconfig/src/lib.rs | 2 +- modules/axfs/src/fs/fatfs.rs | 2 +- modules/axfs/src/lib.rs | 2 +- modules/axfs/src/partition.rs | 4 +- .../driver/src/soc/rockchip/clk/rk3568-clk.rs | 2 +- platform/x86-qemu-q35/Cargo.toml | 2 +- platform/x86-qemu-q35/src/mp.rs | 4 +- xtask/src/cargo.rs | 2 +- xtask/src/image.rs | 18 +- xtask/src/main.rs | 18 +- xtask/src/menuconfig.rs | 5 +- 20 files changed, 382 insertions(+), 843 deletions(-) create mode 100644 kernel/src/hal/impl_host.rs create mode 100644 kernel/src/hal/impl_memory.rs create mode 100644 kernel/src/hal/impl_time.rs create mode 100644 kernel/src/hal/impl_vmm.rs diff --git a/Cargo.lock b/Cargo.lock index 0b7e56a36..2d197ed4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,13 +11,22 @@ dependencies = [ "tock-registers 0.9.0", ] +[[package]] +name = "aarch64-cpu" +version = "11.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44171e22925ec72b63d86747bc3655c7849a5b8d865c980222128839f45ac034" +dependencies = [ + "tock-registers 0.10.1", +] + [[package]] name = "aarch64-cpu-ext" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52dad5cf7342926ce1c375ec680834e56dd3cdbe8b7adf8a6f99b2854cc52c17" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 10.0.0", "tock-registers 0.10.1", ] @@ -27,17 +36,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a2c929f5025d9b8a0f549b187c4d3a39671f44015ff6ccddd0b134c874b3c1a" -[[package]] -name = "abi-singleton" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbdf894742ece5360a74aa8278c42e0f305aa9f7c35d43ebc9cceca105f7e434" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "addr2line" version = "0.25.1" @@ -112,15 +110,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_rgb" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a730095eb14ee842a0f1e68504b85c8d4a19b1ef2ac2a9b4debf0ed982f9b08a" -dependencies = [ - "rgb", -] - [[package]] name = "anstream" version = "0.6.21" @@ -212,32 +201,18 @@ dependencies = [ "axtask", ] -[[package]] -name = "arm-gic-driver" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a886a953642fbf21eb5928c49a05f021fae007219ae8cb2dafbf403dfeba974a" -dependencies = [ - "aarch64-cpu", - "bitflags 2.10.0", - "enum_dispatch", - "log", - "rdif-intc 0.11.0", - "tock-registers 0.9.0", -] - [[package]] name = "arm-gic-driver" version = "0.15.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5487b0a69ebddf2f8affd1e0d32a875fc6213b3a15e3315f9d7beb34b174d457" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 10.0.0", "bitflags 2.10.0", "enum_dispatch", "log", "paste", - "rdif-intc 0.12.1", + "rdif-intc", "tock-registers 0.9.0", ] @@ -262,12 +237,12 @@ dependencies = [ [[package]] name = "arm_vcpu" version = "0.1.1" -source = "git+https://github.com/arceos-hypervisor/arm_vcpu?branch=next#b24cc3635c049302ab8d58d3b54007bb5a053a96" +source = "git+https://github.com/arceos-hypervisor/arm_vcpu?branch=aar-master#3170912a4ff784385691f1ca169e4f87d687e3bf" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 11.2.0", "axaddrspace", "axdevice_base", - "axerrno 0.1.2", + "axerrno 0.2.2", "axvcpu", "axvisor_api", "log", @@ -279,19 +254,18 @@ dependencies = [ [[package]] name = "arm_vgic" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f576b11b486e2ca12373c8205c4a06473a85cf7a664845e5961c47948910c3" +source = "git+https://github.com/arceos-hypervisor/arm_vgic?branch=aar-master#1b145dc767a8d3e0c6f677f26474959de10e6ccf" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 11.2.0", "aarch64_sysreg", "axaddrspace", "axdevice_base", - "axerrno 0.1.2", + "axerrno 0.2.2", "axvisor_api", "bitmaps", "log", - "memory_addr 0.4.1", - "spin 0.9.8", + "memory_addr", + "spin 0.10.0", "tock-registers 0.10.1", ] @@ -332,17 +306,16 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axaddrspace" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06b129114ab36be728ef11dd6540559c30deb6332378157d22bdc0aae6803a63" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/axaddrspace?branch=aar-master#16163ef4a4516fa2d5b51c8b08f5940d1ff31f04" dependencies = [ - "axerrno 0.1.2", + "axerrno 0.2.2", "bit_field", "bitflags 2.10.0", "cfg-if", "lazyinit", "log", - "memory_addr 0.4.1", + "memory_addr", "memory_set", "numeric-enum-macro", "page_table_entry", @@ -360,7 +333,7 @@ dependencies = [ "cfg-if", "kspin", "log", - "memory_addr 0.4.1", + "memory_addr", "strum 0.27.2", ] @@ -409,14 +382,14 @@ name = "axcpu" version = "0.3.0" source = "git+https://github.com/arceos-org/axcpu.git?tag=dev-v03#72ef3859952b7340bae261c9a50c32705e602017" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 10.0.0", "axbacktrace", "cfg-if", "lazyinit", "linkme", "log", "loongArch64", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "page_table_multiarch", "percpu", @@ -430,31 +403,30 @@ dependencies = [ [[package]] name = "axdevice" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axdevice.git#75d9db284fd4c9ee9607c2fd84967461eeaf5b07" +source = "git+https://github.com/arceos-hypervisor/axdevice.git?branch=aar-master#9e35b2f6bb8d80eeff134dc0f9e80a8085e261d8" dependencies = [ "arm_vgic", "axaddrspace", "axdevice_base", - "axerrno 0.1.2", + "axerrno 0.2.2", "axvmconfig", "cfg-if", "log", - "memory_addr 0.4.1", + "memory_addr", "range-alloc", - "spin 0.9.8", + "spin 0.10.0", ] [[package]] name = "axdevice_base" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c43baf33ed4790ffd3365c4ca027a1e3d1c2b6058f4605b67bca04cadf48d5" +source = "git+https://github.com/arceos-hypervisor/axdevice_crates?branch=aar-master#f5d6ebe2a1a65f5872f75799cfbc2fd85fa13b85" dependencies = [ "axaddrspace", - "axerrno 0.1.2", + "axerrno 0.2.2", "axvmconfig", "cfg-if", - "memory_addr 0.4.1", + "memory_addr", "serde", ] @@ -474,7 +446,7 @@ name = "axdriver" version = "0.2.0" source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ - "arm-gic-driver 0.15.9", + "arm-gic-driver", "axalloc", "axconfig", "axdriver_base 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", @@ -488,13 +460,13 @@ dependencies = [ "axklib", "axmm", "cfg-if", - "crate_interface", + "crate_interface 0.1.4", "dma-api 0.5.2", "log", - "memory_addr 0.4.1", - "rdif-block 0.6.2", - "rdif-intc 0.12.1", - "rdrive 0.18.11", + "memory_addr", + "rdif-block", + "rdif-intc", + "rdrive", "smallvec", "spin 0.10.0", ] @@ -615,7 +587,7 @@ dependencies = [ "fatfs", "lazyinit", "log", - "rsext4 0.1.0 (git+https://github.com/Dirinkbottle/rsext4.git?tag=dev-251222)", + "rsext4", "spin 0.9.8", ] @@ -661,7 +633,7 @@ dependencies = [ "lazyinit", "linkme", "log", - "memory_addr 0.4.1", + "memory_addr", "page_table_multiarch", "percpu", ] @@ -703,7 +675,7 @@ version = "0.2.0" source = "git+https://github.com/arceos-hypervisor/axklib.git#7c0fc0588f978f7d75bb94f4e07477776ed37887" dependencies = [ "axerrno 0.1.2", - "memory_addr 0.4.1", + "memory_addr", "trait-ffi", ] @@ -713,7 +685,7 @@ version = "0.2.0" source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" dependencies = [ "cfg-if", - "crate_interface", + "crate_interface 0.1.4", "kspin", "log", ] @@ -730,7 +702,7 @@ dependencies = [ "kspin", "lazyinit", "log", - "memory_addr 0.4.1", + "memory_addr", "memory_set", ] @@ -760,10 +732,10 @@ dependencies = [ "axplat-macros", "bitflags 2.10.0", "const-str", - "crate_interface", + "crate_interface 0.1.4", "handler_table", "kspin", - "memory_addr 0.4.1", + "memory_addr", "percpu", ] @@ -772,10 +744,10 @@ name = "axplat-aarch64-dyn" version = "0.4.0" source = "git+https://github.com/arceos-hypervisor/axplat-aarch64-dyn.git?tag=v0.4.0#05d5acd43d925807496255a9b9e1aa2d272bb591" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 10.0.0", "aarch64-cpu-ext", "any-uart", - "arm-gic-driver 0.15.9", + "arm-gic-driver", "axconfig-macros", "axcpu", "axplat", @@ -783,12 +755,12 @@ dependencies = [ "heapless 0.8.0", "lazyinit", "log", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "paste", "percpu", - "rdif-intc 0.12.1", - "rdrive 0.18.11", + "rdif-intc", + "rdrive", "serde", "somehal", "spin 0.10.0", @@ -800,8 +772,8 @@ name = "axplat-aarch64-peripherals" version = "0.3.0" source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" dependencies = [ - "aarch64-cpu", - "arm-gic-driver 0.15.9", + "aarch64-cpu 10.0.0", + "arm-gic-driver", "arm_pl011", "arm_pl031", "axcpu", @@ -948,7 +920,7 @@ dependencies = [ "axtask", "cfg-if", "chrono", - "crate_interface", + "crate_interface 0.1.4", "ctor_bare", "log", "percpu", @@ -1000,7 +972,7 @@ dependencies = [ "axsched", "cfg-if", "cpumask", - "crate_interface", + "crate_interface 0.1.4", "event-listener", "extern-trait", "futures-util", @@ -1008,7 +980,7 @@ dependencies = [ "kspin", "lazyinit", "log", - "memory_addr 0.4.1", + "memory_addr", "percpu", ] @@ -1067,12 +1039,12 @@ dependencies = [ [[package]] name = "axvcpu" version = "0.1.2" -source = "git+https://github.com/arceos-hypervisor/axvcpu.git?branch=next#343ec3ccf99a86fb9c67a7b0372e9b7a745f0640" +source = "git+https://github.com/arceos-hypervisor/axvcpu?branch=aar-master#62425896e4fb112efd414c0a49290086bfec6b5c" dependencies = [ "axaddrspace", - "axerrno 0.1.2", + "axerrno 0.2.2", "axvisor_api", - "memory_addr 0.4.1", + "memory_addr", "percpu", ] @@ -1082,7 +1054,7 @@ version = "0.0.0" dependencies = [ "aarch64-cpu-ext", "anyhow", - "arm-gic-driver 0.15.9", + "arm-gic-driver", "axaddrspace", "axconfig", "axdevice", @@ -1098,7 +1070,7 @@ dependencies = [ "byte-unit", "cfg-if", "cpumask", - "crate_interface", + "crate_interface 0.2.0", "driver", "extern-trait", "fdt-parser", @@ -1107,15 +1079,15 @@ dependencies = [ "lazy_static", "lazyinit", "log", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "page_table_multiarch", "percpu", "prettyplease", "quote", - "rdif-intc 0.12.1", - "rdrive 0.18.11", - "spin 0.9.8", + "rdif-intc", + "rdrive", + "spin 0.10.0", "syn 2.0.111", "timer_list", "toml 0.9.10+spec-1.1.0", @@ -1124,21 +1096,20 @@ dependencies = [ [[package]] name = "axvisor_api" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7233b2a1338dc06a80e2779b572b4df02007ea128ef7b235b66fc3eeac0ca6" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/axvisor_api?branch=aar-master#657ef629e46738f8b32987cf1de7a5950c1ba904" dependencies = [ "axaddrspace", "axvisor_api_proc", - "crate_interface", - "memory_addr 0.4.1", + "cpumask", + "crate_interface 0.2.0", + "memory_addr", ] [[package]] name = "axvisor_api_proc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a64eb4410ae8357ac8c01c2fb201e57d7aeeb5436ed4d0f5774bfa11cc5902" +version = "0.2.0" +source = "git+https://github.com/arceos-hypervisor/axvisor_api?branch=aar-master#657ef629e46738f8b32987cf1de7a5950c1ba904" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1149,7 +1120,7 @@ dependencies = [ [[package]] name = "axvm" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axvm.git?branch=next#e161233e58c0ef0c6ec115ffa5b0d17dadd298be" +source = "git+https://github.com/arceos-hypervisor/axvm.git?branch=aar-master#7185c2fdd3b372fab96c82f42f7d81b24e0c99f0" dependencies = [ "arm_vcpu", "arm_vgic", @@ -1158,25 +1129,26 @@ dependencies = [ "axdevice_base", "axerrno 0.2.2", "axvcpu", + "axvisor_api", "axvmconfig", "cfg-if", "cpumask", "log", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "page_table_multiarch", "percpu", "riscv_vcpu", - "spin 0.9.8", + "spin 0.10.0", "x86_vcpu", ] [[package]] name = "axvmconfig" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axvmconfig.git?branch=next#5a8b64a47510b17da71e54cabbdf8c999ba2e2c9" +source = "git+https://github.com/arceos-hypervisor/axvmconfig.git?branch=aar-master#494b8d6d56fe9b7e155837801fcc75a4990f06b8" dependencies = [ - "axerrno 0.1.2", + "axerrno 0.2.2", "clap", "enumerable", "env_logger", @@ -1208,19 +1180,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" -[[package]] -name = "bare-test" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7dfcf95987c500af4665d8a18adbc5e7d3177a2537964f48002b88e28fb055e" -dependencies = [ - "bare-test-macros", - "log", - "sparreal-kernel", - "sparreal-macros 0.9.3", - "sparreal-rt", -] - [[package]] name = "bare-test-macros" version = "0.2.0" @@ -1264,26 +1223,6 @@ dependencies = [ "tar", ] -[[package]] -name = "bindgen" -version = "0.71.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" -dependencies = [ - "bitflags 2.10.0", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.111", -] - [[package]] name = "bit" version = "0.1.1" @@ -1367,15 +1306,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "buddy_system_allocator" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0108968a3a2dab95b089c0fc3f1afa7759aa5ebe6f1d86d206d6f7ba726eb" -dependencies = [ - "spin 0.9.8", -] - [[package]] name = "bumpalo" version = "3.19.1" @@ -1541,15 +1471,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -1575,17 +1496,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.5.53" @@ -1768,6 +1678,17 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "crate_interface" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15627e437dc14b22c0a7b8cfc7143fbab68aa6d740df2d695152b1dca58cdf17" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "crc" version = "3.4.0" @@ -1866,16 +1787,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "cstr_core" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd98742e4fdca832d40cab219dc2e3048de17d873248f83f17df47c1bea70956" -dependencies = [ - "cty", - "memchr", -] - [[package]] name = "ctor_bare" version = "0.2.1" @@ -1896,12 +1807,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - [[package]] name = "cursive" version = "0.21.1" @@ -2163,15 +2068,15 @@ version = "0.1.0" dependencies = [ "axklib", "log", - "phytium-mci 0.1.0 (git+https://github.com/YanQD/phytium-mci.git?rev=99c9ee5)", - "rdif-block 0.6.2", + "phytium-mci", + "rdif-block", "rdif-clk", - "rdrive 0.18.11", + "rdrive", "rk3568_clk", "rk3588-clk", "rockchip-pm", "sdmmc", - "spin 0.9.8", + "spin 0.10.0", ] [[package]] @@ -2616,12 +2521,6 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - [[package]] name = "h2" version = "0.3.27" @@ -3150,15 +3049,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -3231,18 +3121,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "kasm-aarch64" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e484b7a4686e2750fae1b4c4b750e14f1522eb303288d9d2723a955c2a41b7d7" -dependencies = [ - "darling 0.20.11", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "kasm-aarch64" version = "0.2.0" @@ -3274,7 +3152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d10c55bedf6789bc3748e0d8756ee639df1ae25144fd3525ed311044bd9a739f" dependencies = [ "cfg-if", - "crate_interface", + "crate_interface 0.1.4", ] [[package]] @@ -3337,16 +3215,6 @@ version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] - [[package]] name = "libredox" version = "0.1.11" @@ -3479,15 +3347,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "lwext4_rust" -version = "0.2.0" -dependencies = [ - "bindgen", - "log", - "printf-compat", -] - [[package]] name = "lzma-rs" version = "0.3.0" @@ -3546,12 +3405,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memory_addr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5438b8df0f13e16e1f46140de247695a95952a5a4479e47197a8711bf1063373" - [[package]] name = "memory_addr" version = "0.4.1" @@ -3565,7 +3418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50a49ecd4114cf87f7e442ec5dd03bd590e7094541f987057310dbb32a6341ad" dependencies = [ "axerrno 0.1.2", - "memory_addr 0.4.1", + "memory_addr", ] [[package]] @@ -3584,12 +3437,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -3667,16 +3514,6 @@ dependencies = [ "libc", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "nop" version = "0.1.0" @@ -3882,28 +3719,6 @@ dependencies = [ "ureq", ] -[[package]] -name = "page-table-arm" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce2c42338660c47a35e7b2940dcccbe6612a4a0aa0485ecdf4e23aa8a2a1158" -dependencies = [ - "aarch64-cpu", - "bitflags 2.10.0", - "log", -] - -[[package]] -name = "page-table-generic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827063f64bbad7b7655092b0f98824ffbe85e89646388eb4dad1e3d797056a2f" -dependencies = [ - "bitflags 2.10.0", - "log", - "thiserror 2.0.17", -] - [[package]] name = "page-table-generic" version = "0.6.1" @@ -3922,9 +3737,9 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dda9891ec368fda90e4b2cc36592b4881073e25a339fe7e3eddd811f0cf6bf18" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 10.0.0", "bitflags 2.10.0", - "memory_addr 0.4.1", + "memory_addr", "x86_64", ] @@ -3935,7 +3750,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa11a21844255e14aa6688ef0eafb058d7be19338633024fb59417f1bfb07f8" dependencies = [ "log", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "riscv", "x86", @@ -3970,12 +3785,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pasts" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efcd36303871fb977a47dabc9af736c75c492bb32a92fa26262b2741531e97ce" - [[package]] name = "pci_types" version = "0.10.0" @@ -3986,19 +3795,6 @@ dependencies = [ "bitflags 2.10.0", ] -[[package]] -name = "pcie" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e45cda4b8ef9f2a8dae7cf5b58c11b41d35fbe62a4d9693fd2d143225fbf44" -dependencies = [ - "bit_field", - "bitflags 2.10.0", - "log", - "pci_types", - "sparreal-macros 0.0.5", -] - [[package]] name = "pcie" version = "0.4.5" @@ -4043,26 +3839,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "phytium-mci" -version = "0.1.0" -dependencies = [ - "bare-test", - "bare-test-macros", - "bitflags 2.10.0", - "byte-unit", - "bytemuck", - "dma-api 0.2.2", - "lazy_static", - "log", - "nb", - "pcie 0.2.7", - "rlsf", - "spin 0.10.0", - "spin_on", - "tock-registers 0.9.0", -] - [[package]] name = "phytium-mci" version = "0.1.0" @@ -4080,33 +3856,6 @@ dependencies = [ "tock-registers 0.9.0", ] -[[package]] -name = "pie-boot" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524d0fc5cd834d2179d8a88cde327b0e168dd6aedf30ce1832467a924a35594f" -dependencies = [ - "aarch64-cpu", - "aarch64-cpu-ext", - "bindeps-simple", - "fdt-parser", - "heapless 0.8.0", - "kasm-aarch64 0.1.3", - "kdef-pgtable", - "pie-boot-if 0.6.0", - "pie-boot-loader-aarch64 0.1.27", - "pie-boot-macros", -] - -[[package]] -name = "pie-boot-if" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00af8d4efee0eee91ead95b34c50c067163dc4c90b874b1cc4caa671eb1d85b" -dependencies = [ - "heapless 0.8.0", -] - [[package]] name = "pie-boot-if" version = "0.8.0" @@ -4116,47 +3865,23 @@ dependencies = [ "heapless 0.8.0", ] -[[package]] -name = "pie-boot-loader-aarch64" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ee18ed1de7f55f318f01803bf7dc353ef492db71a2005fa3af36f116f25d28" -dependencies = [ - "aarch64-cpu", - "aarch64-cpu-ext", - "any-uart", - "bitflags 2.10.0", - "fdt-parser", - "kasm-aarch64 0.1.3", - "kdef-pgtable", - "log", - "num-align", - "page-table-generic 0.6.1", - "pie-boot-if 0.6.0", - "prettyplease", - "quote", - "spin 0.10.0", - "syn 2.0.111", - "thiserror 2.0.17", -] - [[package]] name = "pie-boot-loader-aarch64" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de8836eb8759cd65e70c73dc0f519345d8a734284e8e4cfc5889a6e445af9f09" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 10.0.0", "aarch64-cpu-ext", "any-uart", "bitflags 2.10.0", "fdt-parser", - "kasm-aarch64 0.2.0", + "kasm-aarch64", "kdef-pgtable", "log", "num-align", - "page-table-generic 0.6.1", - "pie-boot-if 0.8.0", + "page-table-generic", + "pie-boot-if", "prettyplease", "quote", "spin 0.10.0", @@ -4243,18 +3968,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "printf-compat" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b002af28ffe3d3d67202ae717810a28125a494d5396debc43de01ee136ac404" -dependencies = [ - "bitflags 1.3.2", - "cstr_core", - "cty", - "itertools 0.9.0", -] - [[package]] name = "proc-macro-crate" version = "3.4.0" @@ -4467,7 +4180,7 @@ dependencies = [ "crossterm 0.28.1", "indoc", "instability", - "itertools 0.13.0", + "itertools", "lru", "paste", "strum 0.26.3", @@ -4494,18 +4207,6 @@ dependencies = [ "bitflags 2.10.0", ] -[[package]] -name = "rdif-base" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6953f438bbbdf58e55513c31e70fa0f85daba2927e8a59130a04608141bb552" -dependencies = [ - "as-any", - "async-trait", - "rdif-def", - "thiserror 2.0.17", -] - [[package]] name = "rdif-base" version = "0.7.0" @@ -4519,16 +4220,6 @@ dependencies = [ "thiserror 2.0.17", ] -[[package]] -name = "rdif-block" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b8e19dc3cb6cd7241085a9560a91d4346edbc525bcbfc3c86e5eeb11559c19" -dependencies = [ - "cfg-if", - "rdif-base 0.6.0", -] - [[package]] name = "rdif-block" version = "0.6.2" @@ -4538,7 +4229,7 @@ dependencies = [ "cfg-if", "dma-api 0.5.2", "futures", - "rdif-base 0.7.0", + "rdif-base", "spin_on", "thiserror 2.0.17", ] @@ -4549,7 +4240,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9af012204e25d45735aa141b475c411b833b4f4bc580924905745d4afbbf606" dependencies = [ - "rdif-base 0.7.0", + "rdif-base", ] [[package]] @@ -4561,17 +4252,6 @@ dependencies = [ "thiserror 2.0.17", ] -[[package]] -name = "rdif-intc" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e7622f78dc9b40958500119553f3c15b9bb9829002d87d0f4b114ebe302a40" -dependencies = [ - "cfg-if", - "rdif-base 0.6.0", - "thiserror 2.0.17", -] - [[package]] name = "rdif-intc" version = "0.12.1" @@ -4579,7 +4259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "170ec813e6cf4d1e5fa53fa8fed0fadc7aaab96683d4f1d44c602a6109931eb4" dependencies = [ "cfg-if", - "rdif-base 0.7.0", + "rdif-base", "thiserror 2.0.17", ] @@ -4590,59 +4270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60c6e8dea6d432b2c03bc3f4238dc59a276aacac6f688a937351e7a313918738" dependencies = [ "pci_types", - "rdif-base 0.7.0", - "thiserror 2.0.17", -] - -[[package]] -name = "rdif-power" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b6eefca0d1b44a5bef1e934d8ab2c8e00e19dd3d9e071855c0933637ee17a0" -dependencies = [ - "rdif-base 0.7.0", -] - -[[package]] -name = "rdif-serial" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673854a0c554806da63f0836c95b34b08956a143ff15e327644cbd07a8e0df31" -dependencies = [ - "futures", - "rdif-base 0.7.0", - "spin_on", - "thiserror 2.0.17", -] - -[[package]] -name = "rdif-systick" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11da4f362ab6cdcdee9d8e795faabd0f15e04167cb17939fd3aca19c2ef3421" -dependencies = [ - "rdif-base 0.7.0", -] - -[[package]] -name = "rdrive" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ce47e5a3d10943dfdb8c31dcca7a91c353ea43f4ac2eb72c92462e83a2baa4" -dependencies = [ - "enum_dispatch", - "fdt-parser", - "log", - "paste", - "rdif-base 0.6.0", - "rdif-block 0.5.0", - "rdif-clk", - "rdif-intc 0.11.0", - "rdif-power", - "rdif-serial", - "rdif-systick", - "rdrive-macros", - "spin 0.10.0", + "rdif-base", "thiserror 2.0.17", ] @@ -4655,25 +4283,14 @@ dependencies = [ "fdt-parser", "log", "paste", - "pcie 0.4.5", - "rdif-base 0.7.0", + "pcie", + "rdif-base", "rdif-pcie", "rdrive-macros", "spin 0.10.0", "thiserror 2.0.17", ] -[[package]] -name = "rdrive-macro-utils" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977fcecf5b5fe8d7189d497d8754d27a4ffaedeac904cce1b7ea7bdfb5280934" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "rdrive-macros" version = "0.4.1" @@ -4863,15 +4480,6 @@ dependencies = [ "webpki-roots", ] -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" -dependencies = [ - "bytemuck", -] - [[package]] name = "ring" version = "0.17.14" @@ -4947,20 +4555,19 @@ dependencies = [ [[package]] name = "riscv_vcpu" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f38f28fe6c02bb3ced43087c9667b23d18adf729becdc5adf1253f7df83904" +source = "git+https://github.com/arceos-hypervisor/riscv_vcpu?branch=aar-master#4cad20373c85d150e509821c3551fefb16084a7a" dependencies = [ "axaddrspace", - "axerrno 0.1.2", + "axerrno 0.2.2", "axvcpu", "axvisor_api", "bit_field", "bitflags 2.10.0", "cfg-if", - "crate_interface", + "crate_interface 0.2.0", "log", "memoffset", - "memory_addr 0.4.1", + "memory_addr", "page_table_entry", "riscv", "riscv-decode", @@ -4968,7 +4575,7 @@ dependencies = [ "rustsbi", "sbi-rt", "sbi-spec", - "tock-registers 0.9.0", + "tock-registers 0.10.1", ] [[package]] @@ -4976,7 +4583,7 @@ name = "rk3568_clk" version = "0.1.0" source = "git+https://github.com/drivercraft/rk3568-clk.git#2b63818f9f9f576d99988fafa70de41112524e00" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 10.0.0", "bare-test-macros", "fdt-parser", "kspin", @@ -5043,19 +4650,10 @@ dependencies = [ "dma-api 0.5.2", "log", "mbarrier", - "rdif-base 0.7.0", + "rdif-base", "tock-registers 0.10.1", ] -[[package]] -name = "rsext4" -version = "0.1.0" -dependencies = [ - "bitflags 2.10.0", - "lazy_static", - "log", -] - [[package]] name = "rsext4" version = "0.1.0" @@ -5277,7 +4875,7 @@ name = "sdmmc" version = "0.1.0" source = "git+https://github.com/drivercraft/sdmmc.git#cc6ae8e4ecb10b69d1e2fee5502f28198a057bba" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 10.0.0", "arm_pl011", "bare-test-macros", "bitflags 2.10.0", @@ -5287,7 +4885,7 @@ dependencies = [ "kspin", "log", "paste", - "smccc 0.2.2", + "smccc", "spin 0.10.0", ] @@ -5560,12 +5158,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "smccc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617d17f088ec733e5a6b86da6ce4cce1414e6e856d6061c16dda51cceae6f68c" - [[package]] name = "smccc" version = "0.2.2" @@ -5615,107 +5207,29 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b5f763b9ab0ce9efd2d8eba9e5b457f93f6426ede68435fe9567cf7681f29d" dependencies = [ - "aarch64-cpu", + "aarch64-cpu 10.0.0", "aarch64-cpu-ext", "any-uart", "bindeps-simple", "fdt-parser", "futures", "heapless 0.8.0", - "kasm-aarch64 0.2.0", + "kasm-aarch64", "kdef-pgtable", "log", "num-align", - "page-table-generic 0.6.1", - "pie-boot-if 0.8.0", - "pie-boot-loader-aarch64 0.3.3", + "page-table-generic", + "pie-boot-if", + "pie-boot-loader-aarch64", "pie-boot-macros", "release-dep", "serde", - "smccc 0.2.2", + "smccc", "spin 0.10.0", "toml 0.9.10+spec-1.1.0", "url", ] -[[package]] -name = "sparreal-kernel" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b182a508314f1560ce8f94090f8c0990640bd849fab49e307ccafe2b51e67da9" -dependencies = [ - "ansi_rgb", - "anyhow", - "arrayvec", - "buddy_system_allocator", - "byte-unit", - "dma-api 0.3.1", - "fdt-parser", - "lazy_static", - "lock_api", - "log", - "memory_addr 0.3.2", - "page-table-generic 0.5.3", - "pasts", - "rdrive 0.15.3", - "rgb", - "sparreal-macros 0.9.3", - "spin 0.9.8", - "thiserror 2.0.17", -] - -[[package]] -name = "sparreal-macros" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f686073b67b2427c9243bddc10ea0a6a5300ab5354a8ee884d9126854b0abab7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "sparreal-macros" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c39b68430130f1c7587eb41f512dd1f6e48bc22a3e1dc11a69dc1b8294cdc90" -dependencies = [ - "abi-singleton", - "darling 0.20.11", - "proc-macro2", - "quote", - "rdrive-macro-utils", - "syn 2.0.111", -] - -[[package]] -name = "sparreal-rt" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9da6adb0285c99f180e9746ebbadcf4886b1b009904f6f9ab5be155ae1325a7" -dependencies = [ - "aarch64-cpu", - "aarch64-cpu-ext", - "ansi_rgb", - "any-uart", - "arm-gic-driver 0.14.9", - "arrayvec", - "buddy_system_allocator", - "fdt-parser", - "log", - "memory_addr 0.3.2", - "numeric-enum-macro", - "page-table-arm", - "page-table-generic 0.5.3", - "pie-boot", - "rgb", - "smccc 0.1.1", - "sparreal-kernel", - "sparreal-macros 0.9.3", - "spin 0.9.8", -] - [[package]] name = "spin" version = "0.9.8" @@ -6391,7 +5905,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" dependencies = [ - "itertools 0.13.0", + "itertools", "unicode-segmentation", "unicode-width 0.1.14", ] @@ -7069,20 +6583,19 @@ dependencies = [ [[package]] name = "x86_vcpu" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873e097d52e94c31be3f0175a9f8d6f2edbc77d7e2f8e6995427df9c08b30a2b" +source = "git+https://github.com/arceos-hypervisor/x86_vcpu?branch=aar-master#24fce09b11af95077f7efc03ff6634bf78ffa187" dependencies = [ "axaddrspace", "axdevice_base", - "axerrno 0.1.2", + "axerrno 0.2.2", "axvcpu", "axvisor_api", "bit_field", "bitflags 2.10.0", "cfg-if", - "crate_interface", + "crate_interface 0.2.0", "log", - "memory_addr 0.4.1", + "memory_addr", "numeric-enum-macro", "page_table_entry", "paste", @@ -7096,16 +6609,15 @@ dependencies = [ [[package]] name = "x86_vlapic" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2556c62649a277ccf1c3c34c740be87bbde5f8dab0b20fcdcf4c2cd7bb6e7302" +source = "git+https://github.com/arceos-hypervisor/x86_vlapic?branch=aar-master#207fb0dfa10ff549b46a77f2d767483fad0c29f6" dependencies = [ "axaddrspace", "axdevice_base", - "axerrno 0.1.2", + "axerrno 0.2.2", "axvisor_api", "bit", "log", - "memory_addr 0.4.1", + "memory_addr", "paste", "tock-registers 0.10.1", ] diff --git a/Cargo.toml b/Cargo.toml index f06c0194b..19092f920 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ kspin = "0.1" lazy_static = {version = "1.5", default-features = false, features = ["spin_no_std"]} lazyinit = "0.2" log = "0.4" -spin = "0.9" +spin = "0.10" timer_list = "0.1" toml = "0.9" @@ -57,18 +57,18 @@ axcpu = {git = "https://github.com/arceos-org/axcpu.git", tag = "dev-v03"} axplat = {git = "https://github.com/arceos-org/axplat_crates.git", tag = "dev-v03"} # System dependent modules provided by ArceOS-Hypervisor. -axaddrspace = "0.1.1" +axaddrspace = "0.2" axhvc = {git = "https://github.com/arceos-hypervisor/axhvc.git"} axklib = {git = "https://github.com/arceos-hypervisor/axklib.git"} axruntime = {path = "modules/axruntime"} axfs = {path = "modules/axfs"} axvcpu = "0.1" -axvm = {git = "https://github.com/arceos-hypervisor/axvm.git", branch = "next"} +axvm = {git = "https://github.com/arceos-hypervisor/axvm.git", branch = "aar-master"} # System independent crates provided by ArceOS, these crates could be imported by remote url. axerrno = "0.2" byte-unit = {version = "5", default-features = false, features = ["byte"]} -crate_interface = "0.1" +crate_interface = "0.2" fdt-parser = "0.4" memory_addr = "0.4" page_table_entry = {version = "0.5", features = ["arm-el2"]} @@ -79,18 +79,26 @@ rdrive = "0.18" vm-fdt = {git = "https://github.com/bullhh/vm-fdt.git", default-features = false, features = ["alloc"]} -axdevice = {git = "https://github.com/arceos-hypervisor/axdevice.git"} +axdevice = {git = "https://github.com/arceos-hypervisor/axdevice.git",branch = "aar-master"} axdevice_base = "0.1" -axvisor_api = "0.1" +axvisor_api = "0.2" driver = {path = "modules/driver"} # platform axplat-x86-qemu-q35 = {path = "platform/x86-qemu-q35"} -axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "next"} +axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "aar-master"} [patch.crates-io] -axvcpu = {git = "https://github.com/arceos-hypervisor/axvcpu.git", branch = "next"} -axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "next"} +axvcpu = { git = "https://github.com/arceos-hypervisor/axvcpu", branch = "aar-master" } +axvisor_api = { git = "https://github.com/arceos-hypervisor/axvisor_api", branch = "aar-master" } +axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace", branch = "aar-master" } +axdevice_base = { git = "https://github.com/arceos-hypervisor/axdevice_crates", branch = "aar-master"} +x86_vlapic = { git = "https://github.com/arceos-hypervisor/x86_vlapic", branch = "aar-master"} +arm_vgic = { git = "https://github.com/arceos-hypervisor/arm_vgic", branch = "aar-master"} +x86_vcpu = { git = "https://github.com/arceos-hypervisor/x86_vcpu", branch = "aar-master"} +arm_vcpu = { git = "https://github.com/arceos-hypervisor/arm_vcpu", branch = "aar-master"} +riscv_vcpu = { git = "https://github.com/arceos-hypervisor/riscv_vcpu", branch = "aar-master"} +axvmconfig = { git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "aar-master" } [patch."https://github.com/arceos-org/arceos"] axconfig = {path = "modules/axconfig"} diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index ea99ff32e..bd55b7a69 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -5,7 +5,7 @@ license.workspace = true name = "axvisor" [features] -ept-level-4 = ["axaddrspace/4-level-ept"] +ept-level-4 = [] fs = ["axstd/fs", "axruntime/fs"] dyn-plat = ["axstd/myplat", "axstd/driver-dyn", "axruntime/driver-dyn"] @@ -18,7 +18,7 @@ kspin = "0.1" lazy_static = {version = "1.5", default-features = false, features = ["spin_no_std"]} lazyinit = "0.2" log = "0.4" -spin = "0.9" +spin = "0.10" timer_list = "0.1.0" # System dependent modules provided by ArceOS. @@ -54,7 +54,7 @@ vm-fdt = {workspace = true, default-features = false, features = ["alloc"]} axdevice.workspace = true axdevice_base = "0.1" -axvisor_api = "0.1" +axvisor_api = "0.2" driver.workspace = true diff --git a/kernel/src/hal/impl_host.rs b/kernel/src/hal/impl_host.rs new file mode 100644 index 000000000..0643e3ad8 --- /dev/null +++ b/kernel/src/hal/impl_host.rs @@ -0,0 +1,11 @@ +use axvisor_api::host::HostIf; + +struct HostImpl; + +#[axvisor_api::api_impl] +impl HostIf for HostImpl { + fn get_host_cpu_num() -> usize { + // std::os::arceos::modules::axconfig::plat::CPU_NUM + axruntime::cpu_count() + } +} diff --git a/kernel/src/hal/impl_memory.rs b/kernel/src/hal/impl_memory.rs new file mode 100644 index 000000000..1479255f2 --- /dev/null +++ b/kernel/src/hal/impl_memory.rs @@ -0,0 +1,53 @@ +use core::{alloc::Layout, ptr::NonNull}; + +use std::os::arceos; + +use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; +use axvisor_api::memory::MemoryIf; +use memory_addr::PAGE_SIZE_4K; + +use crate::hal::AxMmHalImpl; + +struct MemoryImpl; + +#[axvisor_api::api_impl] +impl MemoryIf for MemoryImpl { + fn alloc_frame() -> Option { + ::alloc_frame() + } + + fn alloc_contiguous_frames(num_frames: usize, frame_align_pow2: usize) -> Option { + arceos::modules::axalloc::global_allocator() + .alloc( + Layout::from_size_align( + num_frames * PAGE_SIZE_4K, + PAGE_SIZE_4K << frame_align_pow2, + ) + .unwrap(), + ) + // .alloc_pages(num_frames, PAGE_SIZE_4K << frame_align_pow2) + // .map(|vaddr| ::virt_to_phys(vaddr.into())) + .map(|vaddr| HostPhysAddr::from(vaddr.as_ptr() as usize)) + .ok() + } + + fn dealloc_frame(paddr: HostPhysAddr) { + ::dealloc_frame(paddr) + } + + fn dealloc_contiguous_frames(paddr: HostPhysAddr, num_frames: usize) { + // arceos::modules::axalloc::global_allocator().dealloc_pages(paddr.as_usize(), num_frames); + arceos::modules::axalloc::global_allocator().dealloc( + unsafe { NonNull::new_unchecked(paddr.as_usize() as _) }, + Layout::from_size_align(num_frames * PAGE_SIZE_4K, PAGE_SIZE_4K).unwrap(), + ); + } + + fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr { + ::phys_to_virt(paddr) + } + + fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr { + ::virt_to_phys(vaddr) + } +} diff --git a/kernel/src/hal/impl_time.rs b/kernel/src/hal/impl_time.rs new file mode 100644 index 000000000..2c2758992 --- /dev/null +++ b/kernel/src/hal/impl_time.rs @@ -0,0 +1,33 @@ +use std::os::arceos::modules::axhal; + +use axvisor_api::time::{CancelToken, Nanos, Ticks, TimeIf, TimeValue}; + +use crate::vmm; + +struct TimeImpl; + +#[axvisor_api::api_impl] +impl TimeIf for TimeImpl { + fn current_ticks() -> Ticks { + axhal::time::current_ticks() + } + + fn ticks_to_nanos(ticks: Ticks) -> Nanos { + axhal::time::ticks_to_nanos(ticks) + } + + fn nanos_to_ticks(nanos: Nanos) -> Ticks { + axhal::time::nanos_to_ticks(nanos) + } + + fn register_timer( + deadline: TimeValue, + handler: alloc::boxed::Box, + ) -> CancelToken { + vmm::timer::register_timer(deadline.as_nanos() as u64, handler) + } + + fn cancel_timer(token: CancelToken) { + vmm::timer::cancel_timer(token) + } +} diff --git a/kernel/src/hal/impl_vmm.rs b/kernel/src/hal/impl_vmm.rs new file mode 100644 index 000000000..c42462cd9 --- /dev/null +++ b/kernel/src/hal/impl_vmm.rs @@ -0,0 +1,75 @@ +use std::os::arceos::modules::{axhal, axtask}; + +use axaddrspace::{HostPhysAddr, HostVirtAddr}; +use axerrno::{AxResult, ax_err_type}; +use axvisor_api::vmm::{InterruptVector, VCpuId, VCpuSet, VMId, VmmIf}; +use axvm::AxVMHal; + +use crate::{task::AsVCpuTask, vmm}; + +struct VmmImpl; + +fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr { + axhal::mem::virt_to_phys(vaddr) +} + +fn current_time_nanos() -> u64 { + axhal::time::monotonic_time_nanos() +} + +fn current_vm_id() -> usize { + axtask::current().as_vcpu_task().vm().id() +} + +fn current_vcpu_id() -> usize { + axtask::current().as_vcpu_task().vcpu.id() +} + +fn current_pcpu_id() -> usize { + axhal::percpu::this_cpu_id() +} + +fn vcpu_resides_on(vm_id: usize, vcpu_id: usize) -> AxResult { + vmm::with_vcpu_task(vm_id, vcpu_id, |task| task.cpu_id() as usize) + .ok_or_else(|| ax_err_type!(NotFound)) +} + +fn inject_irq_to_vcpu(vm_id: usize, vcpu_id: usize, irq: usize) -> AxResult { + vmm::with_vm_and_vcpu_on_pcpu(vm_id, vcpu_id, move |_, vcpu| { + vcpu.inject_interrupt(irq).unwrap(); + }) +} + +#[axvisor_api::api_impl] +impl VmmIf for VmmImpl { + fn current_vm_id() -> usize { + axtask::current().as_vcpu_task().vm().id() + } + + fn current_vcpu_id() -> usize { + axtask::current().as_vcpu_task().vcpu.id() + } + + fn vcpu_num(vm_id: VMId) -> Option { + vmm::with_vm(vm_id, |vm| vm.vcpu_num()) + } + + fn active_vcpus(_vm_id: VMId) -> Option { + todo!("active_vcpus") + } + + fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector) { + let _ = vmm::with_vm_and_vcpu_on_pcpu(vm_id, vcpu_id, move |_, vcpu| { + vcpu.inject_interrupt(vector as usize).unwrap(); + }); + } + + fn inject_interrupt_to_cpus(_vm_id: VMId, _vcpu_set: VCpuSet, _vector: InterruptVector) { + todo!("inject_interrupt_to_cpus") + } + + fn notify_vcpu_timer_expired(_vm_id: VMId, _vcpu_id: VCpuId) { + todo!("notify_vcpu_timer_expired") + // vmm::timer::notify_timer_expired(vm_id, vcpu_id); + } +} diff --git a/kernel/src/hal/mod.rs b/kernel/src/hal/mod.rs index dc058723b..78b229096 100644 --- a/kernel/src/hal/mod.rs +++ b/kernel/src/hal/mod.rs @@ -1,22 +1,15 @@ -use std::os::arceos::{ - self, - modules::{axhal::percpu::this_cpu_id, axtask}, -}; - -use axerrno::{AxResult, ax_err_type}; -use memory_addr::PAGE_SIZE_4K; -use page_table_multiarch::PagingHandler; +use std::os::arceos::{self, modules::axhal::percpu::this_cpu_id}; use arceos::modules::axhal; use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; -use axvcpu::AxVCpuHal; -use axvm::{AxVMHal, AxVMPerCpu}; +use axvm::AxVMPerCpu; +use page_table_multiarch::PagingHandler; #[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/mod.rs")] #[cfg_attr(target_arch = "x86_64", path = "arch/x86_64/mod.rs")] pub mod arch; -use crate::{hal::arch::hardware_check, task::AsVCpuTask, vmm}; +use crate::{hal::arch::hardware_check, vmm}; #[allow(unused)] #[repr(C)] @@ -30,58 +23,20 @@ pub enum CacheOp { CleanAndInvalidate, } -/// Implementation for `AxVMHal` trait. -pub struct AxVMHalImpl; - -impl AxVMHal for AxVMHalImpl { - type PagingHandler = axhal::paging::PagingHandlerImpl; - - fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr { - axhal::mem::virt_to_phys(vaddr) - } - - fn current_time_nanos() -> u64 { - axhal::time::monotonic_time_nanos() - } - - fn current_vm_id() -> usize { - axtask::current().as_vcpu_task().vm().id() - } - - fn current_vcpu_id() -> usize { - axtask::current().as_vcpu_task().vcpu.id() - } - - fn current_pcpu_id() -> usize { - axhal::percpu::this_cpu_id() - } - - fn vcpu_resides_on(vm_id: usize, vcpu_id: usize) -> AxResult { - vmm::with_vcpu_task(vm_id, vcpu_id, |task| task.cpu_id() as usize) - .ok_or_else(|| ax_err_type!(NotFound)) - } - - fn inject_irq_to_vcpu(vm_id: usize, vcpu_id: usize, irq: usize) -> AxResult { - vmm::with_vm_and_vcpu_on_pcpu(vm_id, vcpu_id, move |_, vcpu| { - vcpu.inject_interrupt(irq).unwrap(); - }) - } -} - pub struct AxMmHalImpl; impl AxMmHal for AxMmHalImpl { fn alloc_frame() -> Option { - ::PagingHandler::alloc_frame() + ::alloc_frame() } fn dealloc_frame(paddr: HostPhysAddr) { - ::PagingHandler::dealloc_frame(paddr) + ::dealloc_frame(paddr) } #[inline] fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr { - ::PagingHandler::phys_to_virt(paddr) + ::phys_to_virt(paddr) } fn virt_to_phys(vaddr: axaddrspace::HostVirtAddr) -> axaddrspace::HostPhysAddr { @@ -89,18 +44,18 @@ impl AxMmHal for AxMmHalImpl { } } -pub struct AxVCpuHalImpl; +// pub struct AxVCpuHalImpl; -impl AxVCpuHal for AxVCpuHalImpl { - type MmHal = AxMmHalImpl; +// impl AxVCpuHal for AxVCpuHalImpl { +// type MmHal = AxMmHalImpl; - fn irq_hanlder() { - axhal::irq::irq_handler(0); - } -} +// fn irq_hanlder() { +// axhal::irq::irq_handler(0); +// } +// } #[percpu::def_percpu] -static mut AXVM_PER_CPU: AxVMPerCpu = AxVMPerCpu::::new_uninit(); +static mut AXVM_PER_CPU: AxVMPerCpu = AxVMPerCpu::new_uninit(); /// Init hardware virtualization support in each core. pub(crate) fn enable_virtualization() { @@ -157,119 +112,7 @@ pub(crate) fn enable_virtualization() { info!("All cores have enabled hardware virtualization support."); } -#[axvisor_api::api_mod_impl(axvisor_api::memory)] -mod memory_api_impl { - use core::{alloc::Layout, ptr::NonNull}; - - use super::*; - - extern fn alloc_frame() -> Option { - ::alloc_frame() - } - - extern fn alloc_contiguous_frames( - num_frames: usize, - frame_align_pow2: usize, - ) -> Option { - arceos::modules::axalloc::global_allocator() - .alloc( - Layout::from_size_align( - num_frames * PAGE_SIZE_4K, - PAGE_SIZE_4K << frame_align_pow2, - ) - .unwrap(), - ) - // .alloc_pages(num_frames, PAGE_SIZE_4K << frame_align_pow2) - // .map(|vaddr| ::virt_to_phys(vaddr.into())) - .map(|vaddr| HostPhysAddr::from(vaddr.as_ptr() as usize)) - .ok() - } - - extern fn dealloc_frame(paddr: HostPhysAddr) { - ::dealloc_frame(paddr) - } - - extern fn dealloc_contiguous_frames(paddr: HostPhysAddr, num_frames: usize) { - // arceos::modules::axalloc::global_allocator().dealloc_pages(paddr.as_usize(), num_frames); - arceos::modules::axalloc::global_allocator().dealloc( - unsafe { NonNull::new_unchecked(paddr.as_usize() as _) }, - Layout::from_size_align(num_frames * PAGE_SIZE_4K, PAGE_SIZE_4K).unwrap(), - ); - } - - extern fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr { - ::phys_to_virt(paddr) - } - - extern fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr { - ::virt_to_phys(vaddr) - } -} - -#[axvisor_api::api_mod_impl(axvisor_api::time)] -mod time_api_impl { - use super::*; - use axvisor_api::time::{CancelToken, Nanos, Ticks, TimeValue}; - - extern fn current_ticks() -> Ticks { - axhal::time::current_ticks() - } - - extern fn ticks_to_nanos(ticks: Ticks) -> Nanos { - axhal::time::ticks_to_nanos(ticks) - } - - extern fn nanos_to_ticks(nanos: Nanos) -> Ticks { - axhal::time::nanos_to_ticks(nanos) - } - - extern fn register_timer( - deadline: TimeValue, - handler: alloc::boxed::Box, - ) -> CancelToken { - vmm::timer::register_timer(deadline.as_nanos() as u64, handler) - } - - extern fn cancel_timer(token: CancelToken) { - vmm::timer::cancel_timer(token) - } -} - -#[axvisor_api::api_mod_impl(axvisor_api::vmm)] -mod vmm_api_impl { - use super::*; - use axvisor_api::vmm::{InterruptVector, VCpuId, VMId}; - - extern fn current_vm_id() -> usize { - ::current_vm_id() - } - - extern fn current_vcpu_id() -> usize { - ::current_vcpu_id() - } - - extern fn vcpu_num(vm_id: VMId) -> Option { - vmm::with_vm(vm_id, |vm| vm.vcpu_num()) - } - - extern fn active_vcpus(_vm_id: VMId) -> Option { - todo!("active_vcpus") - } - - extern fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector) { - ::inject_irq_to_vcpu(vm_id, vcpu_id, vector as usize).unwrap(); - } - - extern fn notify_vcpu_timer_expired(_vm_id: VMId, _vcpu_id: VCpuId) { - todo!("notify_vcpu_timer_expired") - // vmm::timer::notify_timer_expired(vm_id, vcpu_id); - } -} - -#[axvisor_api::api_mod_impl(axvisor_api::host)] -mod host_api_impl { - extern fn get_host_cpu_num() -> usize { - // std::os::arceos::modules::axconfig::plat::CPU_NUM - axruntime::cpu_count() - } -} +mod impl_host; +mod impl_memory; +mod impl_time; +mod impl_vmm; diff --git a/kernel/src/vmm/mod.rs b/kernel/src/vmm/mod.rs index 5beecbce1..f1c53615f 100644 --- a/kernel/src/vmm/mod.rs +++ b/kernel/src/vmm/mod.rs @@ -18,18 +18,15 @@ use std::os::arceos::{ use axerrno::{AxResult, ax_err_type}; -use crate::{ - hal::{AxVCpuHalImpl, AxVMHalImpl}, - task::AsVCpuTask, -}; +use crate::task::AsVCpuTask; pub use timer::init_percpu as init_timer_percpu; /// The instantiated VM type. -pub type VM = axvm::AxVM; +pub type VM = axvm::AxVM; /// The instantiated VM ref type (by `Arc`). -pub type VMRef = axvm::AxVMRef; +pub type VMRef = axvm::AxVMRef; /// The instantiated VCpu ref type (by `Arc`). -pub type VCpuRef = axvm::AxVCpuRef; +pub type VCpuRef = axvm::AxVCpuRef; static VMM: AxWaitQueueHandle = AxWaitQueueHandle::new(); diff --git a/modules/axconfig/src/lib.rs b/modules/axconfig/src/lib.rs index 82d22f6ac..c5e0c7ecd 100644 --- a/modules/axconfig/src/lib.rs +++ b/modules/axconfig/src/lib.rs @@ -47,7 +47,7 @@ pub mod devices { #[doc = ""] pub mod plat { #[doc = " Number of CPUs."] - pub const CPU_NUM: usize = 16; + pub const CPU_NUM: usize = 1; #[doc = " Platform family (deprecated)."] pub const FAMILY: &str = ""; #[doc = " Kernel address space base."] diff --git a/modules/axfs/src/fs/fatfs.rs b/modules/axfs/src/fs/fatfs.rs index da0b6f11d..b62d25cb2 100644 --- a/modules/axfs/src/fs/fatfs.rs +++ b/modules/axfs/src/fs/fatfs.rs @@ -4,8 +4,8 @@ use core::cell::OnceCell; use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult}; use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; -use spin::Mutex; use fatfs::{Dir, File, LossyOemCpConverter, NullTimeProvider, Read, Seek, SeekFrom, Write}; +use spin::Mutex; use crate::dev::{Disk, Partition}; diff --git a/modules/axfs/src/lib.rs b/modules/axfs/src/lib.rs index 11692a023..697b21b5b 100644 --- a/modules/axfs/src/lib.rs +++ b/modules/axfs/src/lib.rs @@ -19,7 +19,7 @@ use alloc::{ sync::Arc, vec::Vec, }; -use axdriver::{prelude::*, AxDeviceContainer}; +use axdriver::{AxDeviceContainer, prelude::*}; /// Initializes filesystems by block devices. pub fn init_filesystems(mut blk_devs: AxDeviceContainer, bootargs: Option<&str>) { diff --git a/modules/axfs/src/partition.rs b/modules/axfs/src/partition.rs index 05f0d3a8c..f6487ea1c 100644 --- a/modules/axfs/src/partition.rs +++ b/modules/axfs/src/partition.rs @@ -225,7 +225,7 @@ fn parse_gpt_partitions(disk: &mut Disk) -> AxResult> { for j in 0..36 { name_utf16[j] = entry.partition_name[j]; } - + // Find the null terminator let mut name_len = 36; for j in 0..36 { @@ -234,7 +234,7 @@ fn parse_gpt_partitions(disk: &mut Disk) -> AxResult> { break; } } - + // Convert only the valid portion let name_slice = &name_utf16[..name_len]; let name_str = String::from_utf16_lossy(name_slice); diff --git a/modules/driver/src/soc/rockchip/clk/rk3568-clk.rs b/modules/driver/src/soc/rockchip/clk/rk3568-clk.rs index 6607dd92c..efa3b1ca0 100644 --- a/modules/driver/src/soc/rockchip/clk/rk3568-clk.rs +++ b/modules/driver/src/soc/rockchip/clk/rk3568-clk.rs @@ -125,4 +125,4 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError plat_dev.register(clk); Ok(()) -} \ No newline at end of file +} diff --git a/platform/x86-qemu-q35/Cargo.toml b/platform/x86-qemu-q35/Cargo.toml index 69c24bada..880e3c181 100644 --- a/platform/x86-qemu-q35/Cargo.toml +++ b/platform/x86-qemu-q35/Cargo.toml @@ -7,7 +7,7 @@ version = "0.1.0" default = [ "irq", "smp", - "reboot-on-system-off", + # "reboot-on-system-off", ] fp-simd = [] irq = ["axplat/irq"] diff --git a/platform/x86-qemu-q35/src/mp.rs b/platform/x86-qemu-q35/src/mp.rs index 14e8e671b..866744440 100644 --- a/platform/x86-qemu-q35/src/mp.rs +++ b/platform/x86-qemu-q35/src/mp.rs @@ -25,11 +25,11 @@ unsafe fn setup_startup_page(stack_top: PhysAddr) { core::ptr::copy_nonoverlapping( ap_start as *const u64, start_page_ptr, - (ap_end as usize - ap_start as usize) / 8, + (ap_end as *const () as usize - ap_start as *const () as usize) / 8, ); } start_page[U64_PER_PAGE - 2] = stack_top.as_usize() as u64; // stack_top - start_page[U64_PER_PAGE - 1] = ap_entry32 as usize as _; // entry + start_page[U64_PER_PAGE - 1] = ap_entry32 as *const () as usize as _; // entry } /// Starts the given secondary CPU with its boot stack. diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index 0fe676f39..a821bdf5a 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -1,5 +1,5 @@ -use std::{fs, path::PathBuf}; use ostool::build::CargoRunnerKind; +use std::{fs, path::PathBuf}; use crate::ctx::Context; diff --git a/xtask/src/image.rs b/xtask/src/image.rs index dcde79bfa..c54e2eb2e 100644 --- a/xtask/src/image.rs +++ b/xtask/src/image.rs @@ -46,25 +46,25 @@ pub struct ImageArgs { pub enum ImageCommands { /// List all available images Ls, - + /// Download the specified image and automatically extract it Download { /// Name of the image to download image_name: String, - + /// Output directory for the downloaded image #[arg(short, long)] output_dir: Option, - + /// Do not extract after download #[arg(long, help = "Do not extract after download")] no_extract: bool, }, - + /// Remove the specified image from temp directory Rm { /// Name of the image to remove - image_name: String + image_name: String, }, } @@ -433,12 +433,16 @@ async fn image_download(image_name: &str, output_dir: Option, extract: b Ok(false) => { // Remove the invalid downloaded file let _ = fs::remove_file(&output_path); - return Err(anyhow!("Download completed but file SHA256 verification failed")); + return Err(anyhow!( + "Download completed but file SHA256 verification failed" + )); } Err(e) => { // Remove the potentially corrupted downloaded file let _ = fs::remove_file(&output_path); - return Err(anyhow!("Download completed but error verifying downloaded file: {e}")); + return Err(anyhow!( + "Download completed but error verifying downloaded file: {e}" + )); } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 68390dc7c..f5fdb5efd 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -55,11 +55,11 @@ struct QemuArgs { /// Path to custom build configuration file (TOML format) #[arg(long)] build_config: Option, - + /// Path to custom QEMU configuration file #[arg(long)] qemu_config: Option, - + /// Comma-separated list of VM configuration files #[arg(long)] vmconfigs: Vec, @@ -73,23 +73,23 @@ struct ClippyArgs { /// Only check specific packages (comma separated) #[arg(long)] packages: Option, - + /// Only check specific targets (comma separated) #[arg(long)] targets: Option, - + /// Continue on error instead of exiting immediately #[arg(long)] continue_on_error: bool, - + /// Dry run - show what would be checked without running clippy #[arg(long)] dry_run: bool, - + /// Automatically fix clippy warnings where possible #[arg(long)] fix: bool, - + /// Allow fixing when the working directory is dirty (has uncommitted changes) #[arg(long)] allow_dirty: bool, @@ -100,11 +100,11 @@ struct UbootArgs { /// Path to custom build configuration file (TOML format) #[arg(long)] build_config: Option, - + /// Path to custom U-Boot configuration file #[arg(long)] uboot_config: Option, - + /// Comma-separated list of VM configuration files #[arg(long)] vmconfigs: Vec, diff --git a/xtask/src/menuconfig.rs b/xtask/src/menuconfig.rs index 199b440f4..0460dc474 100644 --- a/xtask/src/menuconfig.rs +++ b/xtask/src/menuconfig.rs @@ -10,7 +10,10 @@ impl Context { let config_path = self.ctx.paths.workspace.join(".build.toml"); if config_path.exists() { - println!("\nCurrent .build.toml configuration file: {}", config_path.display()); + println!( + "\nCurrent .build.toml configuration file: {}", + config_path.display() + ); } else { println!("\nNo .build.toml configuration file found, will use default configuration"); } From 934cd42dca4359cac4fcbeafe1db87735f135877 Mon Sep 17 00:00:00 2001 From: DeathWish5 Date: Sat, 24 Jan 2026 18:03:06 +0800 Subject: [PATCH 037/132] refactor: resolve clippy warnings and tighten docs Clear clippy lints and add minimal module/README notes needed for publish-ready docs. --- .gitignore | 24 ++ Cargo.lock | 2 +- Cargo.toml | 21 +- README.md | 8 + src/fxmac.rs | 384 +++++++++++++------ src/fxmac_const.rs | 907 +++++++++++++++++++++++---------------------- src/fxmac_dma.rs | 894 +++++++++++++++++++++++++++----------------- src/fxmac_intr.rs | 826 +++++++++++++++++++++++------------------ src/fxmac_phy.rs | 414 ++++++++++++--------- src/lib.rs | 216 ++++++++++- src/utils.rs | 9 +- 11 files changed, 2234 insertions(+), 1471 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..10eceb8e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds +*.pdb + +# IDE / Editor +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# macOS +.DS_Store diff --git a/Cargo.lock b/Cargo.lock index b76982889..af7f2f10e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ dependencies = [ [[package]] name = "fxmac_rs" -version = "0.2.0" +version = "0.2.1" dependencies = [ "aarch64-cpu", "crate_interface", diff --git a/Cargo.toml b/Cargo.toml index 547d89663..341044bef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,16 @@ -cargo-features = ["edition2024"] - [package] name = "fxmac_rs" +version = "0.2.1" +edition = "2021" authors = ["xiaoluoyuan@163.com"] -version = "0.2.0" -edition = "2024" -license = "GPL-2.0" -description = "fxmac ethernet driver in Rust on PhytiumPi board." -homepage = "https://github.com/elliott10/fxmac_rs.git" -documentation = "https://github.com/elliott10/fxmac_rs/blob/main/README.md" -repository = "https://github.com/elliott10/fxmac_rs.git" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +description = "FXMAC Ethernet driver in Rust for PhytiumPi (Phytium Pi) board, supporting DMA-based packet transmission and reception." +documentation = "https://docs.rs/fxmac_rs" +repository = "https://github.com/elliott10/fxmac_rs" +homepage = "https://github.com/elliott10/fxmac_rs" +readme = "README.md" +license = "GPL-2.0-only" +keywords = ["ethernet", "driver", "phytium", "aarch64", "no_std"] +categories = ["embedded", "hardware-support", "no-std"] [features] debug = [] diff --git a/README.md b/README.md index 17cc8d9f0..37f781266 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,14 @@ let recv_packets = FXmacRecvHandler(fxmac_device); cargo build --target=aarch64-unknown-none-softfloat ``` +## Testing (建议) + +由于该驱动强依赖硬件与 DMA,建议分层补充测试并按能力选择运行环境: + +- 单元测试/文档测试(主机可跑):`cargo test --all-features`、`cargo test --doc` +- 功能测试(mock/模拟):对 BD ring、地址转换与错误分支做行为验证 +- 系统测试(真实板子):在 PhytiumPi 上验证链路协商、收发路径与中断处理 + ## About ethernet PHY: Motorcomm YT8521 diff --git a/src/fxmac.rs b/src/fxmac.rs index eaec7432f..6f4266762 100644 --- a/src/fxmac.rs +++ b/src/fxmac.rs @@ -1,3 +1,8 @@ +//! Core FXMAC Ethernet controller functionality. +//! +//! This module provides the main data structures and functions for controlling +//! the FXMAC Ethernet MAC controller. + use core::sync::atomic::Ordering; use crate::fxmac_const::*; @@ -7,106 +12,248 @@ use crate::fxmac_phy::*; use crate::utils::*; use alloc::boxed::Box; -pub const FXMAC_HANDLER_DMASEND: u32 = 1; /* 发送中断 */ -pub const FXMAC_HANDLER_DMARECV: u32 = 2; /* 接收中断 */ -pub const FXMAC_HANDLER_ERROR: u32 = 3; /* 异常中断 */ -pub const FXMAC_HANDLER_LINKCHANGE: u32 = 4; /* 连接状态 */ -pub const FXMAC_HANDLER_RESTART: u32 = 5; /* 发送描述符队列发生异常 */ - +/// Handler type for DMA send (TX) interrupts. +pub const FXMAC_HANDLER_DMASEND: u32 = 1; +/// Handler type for DMA receive (RX) interrupts. +pub const FXMAC_HANDLER_DMARECV: u32 = 2; +/// Handler type for error interrupts. +pub const FXMAC_HANDLER_ERROR: u32 = 3; +/// Handler type for link status change interrupts. +pub const FXMAC_HANDLER_LINKCHANGE: u32 = 4; +/// Handler type for TX descriptor queue restart. +pub const FXMAC_HANDLER_RESTART: u32 = 5; + +/// Link status: down. pub const FXMAC_LINKDOWN: u32 = 0; +/// Link status: up. pub const FXMAC_LINKUP: u32 = 1; +/// Link status: negotiating. pub const FXMAC_NEGOTIATING: u32 = 2; +/// FXMAC0 peripheral clock frequency in Hz. pub const FXMAC0_PCLK: u32 = 50000000; +/// FXMAC0 hotplug IRQ number. pub const FXMAC0_HOTPLUG_IRQ_NUM: u32 = 53 + 30; -pub const FXMAC_QUEUE_MAX_NUM: u32 = 4; // 16 +/// Maximum number of hardware queues supported. +pub const FXMAC_QUEUE_MAX_NUM: u32 = 4; +/// Mask for upper 32 bits of 64-bit address. pub const ULONG64_HI_MASK: u64 = 0xFFFFFFFF00000000; +/// Mask for lower 32 bits of 64-bit address. pub const ULONG64_LO_MASK: u64 = !ULONG64_HI_MASK; +/// Component is initialized and ready. pub const FT_COMPONENT_IS_READY: u32 = 0x11111111; +/// Component is started. pub const FT_COMPONENT_IS_STARTED: u32 = 0x22222222; +/// Memory page size in bytes. pub const PAGE_SIZE: usize = 4096; -pub(crate) const FXMAC_IOBASE: u64 = 0x3200c000; // FXMAC0_BASE_ADDR +/// Base address of FXMAC0 controller. +pub(crate) const FXMAC_IOBASE: u64 = 0x3200c000; +/// Main FXMAC Ethernet controller instance. +/// +/// This structure holds all state information for an FXMAC controller instance, +/// including configuration, DMA queues, and runtime status. +/// +/// # Thread Safety +/// +/// This structure implements `Send` and `Sync` for use across threads, but +/// external synchronization is required for concurrent access to mutable state. +/// +/// # Example +/// +/// ```ignore +/// let hwaddr: [u8; 6] = [0x55, 0x44, 0x33, 0x22, 0x11, 0x00]; +/// let fxmac: &'static mut FXmac = xmac_init(&hwaddr); +/// +/// // Check link status +/// if fxmac.link_status == FXMAC_LINKUP { +/// println!("Network link is up!"); +/// } +/// ``` pub struct FXmac { + /// Hardware configuration settings. pub config: FXmacConfig, - pub is_ready: u32, /* Device is ininitialized and ready*/ + /// Device initialization state (FT_COMPONENT_IS_READY when initialized). + pub is_ready: u32, + /// Device running state (FT_COMPONENT_IS_STARTED when active). pub is_started: u32, - pub link_status: u32, /* indicates link status ,FXMAC_LINKUP is link up ,FXMAC_LINKDOWN is link down,FXMAC_NEGOTIATING is need to negotiating*/ + /// Current link status (FXMAC_LINKUP, FXMAC_LINKDOWN, or FXMAC_NEGOTIATING). + pub link_status: u32, + /// Currently enabled MAC options. pub options: u32, - pub mask: u32, /* indicates intr mask */ - pub caps: u32, /* Capability mask bits */ - + /// Interrupt mask for enabled interrupts. + pub mask: u32, + /// Capability mask bits. + pub caps: u32, + /// Network buffer management (lwIP port compatibility). pub lwipport: FXmacLwipPort, - - pub tx_bd_queue: FXmacQueue, /* Transmit Queue */ - pub rx_bd_queue: FXmacQueue, /* Receive Queue */ - - pub moudle_id: u32, /* Module identification number */ + /// Transmit buffer descriptor queue. + pub tx_bd_queue: FXmacQueue, + /// Receive buffer descriptor queue. + pub rx_bd_queue: FXmacQueue, + /// Hardware module identification number. + pub moudle_id: u32, + /// Maximum transmission unit size. pub max_mtu_size: u32, + /// Maximum frame size including headers. pub max_frame_size: u32, - - pub phy_address: u32, /* phy address */ - pub rxbuf_mask: u32, /* 1000,100,10 */ + /// PHY address on the MDIO bus. + pub phy_address: u32, + /// Receive buffer mask for speed settings. + pub rxbuf_mask: u32, } +// SAFETY: FXmac can be sent between threads as long as proper synchronization +// is used for concurrent access. unsafe impl Send for FXmac {} +// SAFETY: FXmac can be shared between threads with external synchronization. unsafe impl Sync for FXmac {} - +/// Hardware configuration for the FXMAC controller. +/// +/// This structure contains all hardware-level configuration parameters +/// required to initialize and operate the FXMAC Ethernet controller. pub struct FXmacConfig { - pub instance_id: u32, /* Id of device*/ + /// Instance identifier for multi-controller setups. + pub instance_id: u32, + /// Base address of the MAC controller registers. pub base_address: u64, + /// Base address for extended mode configuration. pub extral_mode_base: u64, + /// Base address for loopback configuration. pub extral_loopback_base: u64, + /// PHY interface type (SGMII, RGMII, etc.). pub interface: FXmacPhyInterface, - pub speed: u32, /* FXMAC_SPEED_XXX */ - pub duplex: u32, /* 1 is full-duplex , 0 is half-duplex */ - pub auto_neg: u32, /* Enable auto-negotiation - when set active high, autonegotiation operation is enabled. */ + /// Link speed in Mbps (10, 100, 1000, etc.). + pub speed: u32, + /// Duplex mode: 1 for full-duplex, 0 for half-duplex. + pub duplex: u32, + /// Auto-negotiation enable: 1 to enable, 0 to disable. + pub auto_neg: u32, + /// Peripheral clock frequency in Hz. pub pclk_hz: u32, - pub max_queue_num: u32, /* Number of Xmac Controller Queues */ - pub tx_queue_id: u32, /* 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number */ - pub rx_queue_id: u32, /* 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number */ + /// Maximum number of hardware queues. + pub max_queue_num: u32, + /// TX queue index (0 to FXMAC_QUEUE_MAX_NUM-1). + pub tx_queue_id: u32, + /// RX queue index (0 to FXMAC_QUEUE_MAX_NUM-1). + pub rx_queue_id: u32, + /// Hotplug IRQ number. pub hotplug_irq_num: u32, - pub dma_brust_length: u32, /* burst length */ + /// DMA burst length setting. + pub dma_brust_length: u32, + /// Default network configuration options. pub network_default_config: u32, - pub queue_irq_num: [u32; FXMAC_QUEUE_MAX_NUM as usize], /* mac0 8个 ,其他的 4个 */ - pub caps: u32, /* used to configure tail ptr feature */ + /// IRQ numbers for each hardware queue. + pub queue_irq_num: [u32; FXMAC_QUEUE_MAX_NUM as usize], + /// Capability flags (e.g., tail pointer support). + pub caps: u32, + /// MAC address (6 bytes). pub mac: [u8; 6], } +/// Hardware queue structure for TX/RX operations. pub struct FXmacQueue { + /// Queue identifier. pub queue_id: u32, + /// Buffer descriptor ring for this queue. pub bdring: FXmacBdRing, } -/// Interface Mode definitions +/// PHY interface mode definitions. +/// +/// Specifies the physical layer interface type used for communication +/// between the MAC controller and the PHY chip. #[derive(Debug, Clone, Copy, PartialEq)] pub enum FXmacPhyInterface { + /// SGMII (Serial Gigabit Media Independent Interface). FXMAC_PHY_INTERFACE_MODE_SGMII = 0, + /// RMII (Reduced Media Independent Interface). FXMAC_PHY_INTERFACE_MODE_RMII = 1, + /// RGMII (Reduced Gigabit Media Independent Interface). FXMAC_PHY_INTERFACE_MODE_RGMII = 2, + /// XGMII (10 Gigabit Media Independent Interface). FXMAC_PHY_INTERFACE_MODE_XGMII = 3, + /// USXGMII (Universal Serial 10 Gigabit Media Independent Interface). FXMAC_PHY_INTERFACE_MODE_USXGMII = 4, + /// 5GBASE-R interface mode. FXMAC_PHY_INTERFACE_MODE_5GBASER = 5, + /// 2500BASE-X interface mode. FXMAC_PHY_INTERFACE_MODE_2500BASEX = 6, } +/// Reads a memory-mapped register via a physical address. +/// +/// The address is translated using the platform's [`KernelFunc::phys_to_virt`] +/// implementation before a volatile read is performed. pub fn read_reg(src: *const T) -> T { - unsafe { core::ptr::read_volatile(crate_interface::call_interface!(crate::KernelFunc::phys_to_virt(src as usize)) as *const T) } + unsafe { + core::ptr::read_volatile( + crate_interface::call_interface!(crate::KernelFunc::phys_to_virt(src as usize)) + as *const T, + ) + } } +/// Writes a value to a memory-mapped register via a physical address. +/// +/// The address is translated using the platform's [`KernelFunc::phys_to_virt`] +/// implementation before a volatile write is performed. pub fn write_reg(dst: *mut T, value: T) { unsafe { - core::ptr::write_volatile(crate_interface::call_interface!(crate::KernelFunc::phys_to_virt(dst as usize)) as *mut T, value); + core::ptr::write_volatile( + crate_interface::call_interface!(crate::KernelFunc::phys_to_virt(dst as usize)) + as *mut T, + value, + ); } } -pub fn xmac_init(hwaddr: &[u8; 6]) -> &'static mut FXmac { // i32 - //let mut hwaddr: [u8; 6] = [0x98, 0x0e, 0x24, 0x00, 0x11, 0x0]; - +/// Initializes the FXMAC Ethernet controller. +/// +/// This function performs complete hardware initialization of the FXMAC controller, +/// including: +/// - Hardware reset and configuration +/// - PHY initialization and link establishment +/// - DMA buffer descriptor ring setup +/// - Interrupt handler registration +/// - MAC address configuration +/// +/// # Arguments +/// +/// * `hwaddr` - A 6-byte MAC address to assign to the controller. +/// +/// # Returns +/// +/// A static mutable reference to the initialized [`FXmac`] instance. +/// +/// # Panics +/// +/// This function may panic if: +/// - PHY initialization fails +/// - DMA memory allocation fails +/// +/// # Example +/// +/// ```ignore +/// // Define the MAC address +/// let hwaddr: [u8; 6] = [0x55, 0x44, 0x33, 0x22, 0x11, 0x00]; +/// +/// // Initialize the controller +/// let fxmac = xmac_init(&hwaddr); +/// +/// // The controller is now ready for packet transmission and reception +/// assert_eq!(fxmac.is_started, FT_COMPONENT_IS_STARTED); +/// ``` +/// +/// # Note +/// +/// The returned reference has `'static` lifetime and is stored in a global +/// atomic pointer. Only one instance should be active at a time. +pub fn xmac_init(hwaddr: &[u8; 6]) -> &'static mut FXmac { /* FXmacConfig mac_config: mac_config.instance_id=0, @@ -241,7 +388,7 @@ pub fn xmac_init(hwaddr: &[u8; 6]) -> &'static mut FXmac { // i32 // initialize dma let mut dmacrreg: u32 = read_reg((xmac.config.base_address + FXMAC_DMACR_OFFSET) as *const u32); dmacrreg &= !(FXMAC_DMACR_BLENGTH_MASK); - dmacrreg = dmacrreg | FXMAC_DMACR_INCR16_AHB_AXI_BURST; /* Attempt to use bursts of up to 16. */ + dmacrreg |= FXMAC_DMACR_INCR16_AHB_AXI_BURST; /* Attempt to use bursts of up to 16. */ write_reg( (xmac.config.base_address + FXMAC_DMACR_OFFSET) as *mut u32, dmacrreg, @@ -257,7 +404,7 @@ pub fn xmac_init(hwaddr: &[u8; 6]) -> &'static mut FXmac { // i32 if (xmac.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_UNICAST_ADDRESS_FILITER) != 0 { debug!("Set unicast hash table"); - FXmac_SetHash(&mut xmac, &hwaddr); + FXmac_SetHash(&mut xmac, hwaddr); } /* 注册了 lwip_port->ops: @@ -285,12 +432,14 @@ pub fn xmac_init(hwaddr: &[u8; 6]) -> &'static mut FXmac { // i32 xmac_ref } -/// FXmacStart(): Start mac -/// Start the Ethernet controller as follows: -/// - Enable transmitter if FXMAC_TRANSMIT_ENABLE_OPTION is set -/// - Enable receiver if FXMAC_RECEIVER_ENABLE_OPTION is set -/// - Start the SG DMA send and receive channels and enable the device -/// interrupt +/// Starts the Ethernet controller. +/// +/// This enables TX/RX paths based on configured options, starts DMA channels, +/// and enables the device interrupt mask. +/// +/// # Panics +/// +/// Panics if the instance is not in the ready state. pub fn FXmacStart(instance_p: &mut FXmac) { assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); @@ -301,13 +450,13 @@ pub fn FXmacStart(instance_p: &mut FXmac) { ); /* Enable transmitter if not already enabled */ - if (instance_p.config.network_default_config & FXMAC_TRANSMITTER_ENABLE_OPTION as u32) != 0 { + if (instance_p.config.network_default_config & FXMAC_TRANSMITTER_ENABLE_OPTION) != 0 { let reg_val = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); if (reg_val & FXMAC_NWCTRL_TXEN_MASK) == 0 { write_reg( (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, - reg_val | FXMAC_NWCTRL_TXEN_MASK as u32, + reg_val | FXMAC_NWCTRL_TXEN_MASK, ); } } @@ -320,7 +469,7 @@ pub fn FXmacStart(instance_p: &mut FXmac) { if (reg_val & FXMAC_NWCTRL_RXEN_MASK) == 0 { write_reg( (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, - reg_val | FXMAC_NWCTRL_RXEN_MASK as u32, + reg_val | FXMAC_NWCTRL_RXEN_MASK, ); } } @@ -343,10 +492,13 @@ pub fn FXmacStart(instance_p: &mut FXmac) { instance_p.is_started = FT_COMPONENT_IS_STARTED; } -/// Gracefully stop the Ethernet MAC as follows: -/// - Disable all interrupts from this device -/// - Stop DMA channels -/// - Disable the tansmitter and receiver +/// Gracefully stops the Ethernet MAC. +/// +/// This disables interrupts, stops DMA channels, and shuts down TX/RX paths. +/// +/// # Panics +/// +/// Panics if the instance is not in the ready state. pub fn FXmacStop(instance_p: &mut FXmac) { assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); // Disable all interrupts @@ -358,8 +510,8 @@ pub fn FXmacStop(instance_p: &mut FXmac) { /* Disable the receiver & transmitter */ let mut reg_val: u32 = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); - reg_val &= !(FXMAC_NWCTRL_RXEN_MASK as u32); - reg_val &= !(FXMAC_NWCTRL_TXEN_MASK as u32); + reg_val &= !FXMAC_NWCTRL_RXEN_MASK; + reg_val &= !FXMAC_NWCTRL_TXEN_MASK; write_reg( (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_val, @@ -408,8 +560,8 @@ fn FXmacReset(instance_p: &mut FXmac) { instance_p.config.pclk_hz = FXMAC0_PCLK; // 50000000 - let netctrl = (FXMAC_NWCTRL_STATCLR_MASK & !(FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK as u32)) - | FXMAC_NWCTRL_MDEN_MASK; + let netctrl = + (FXMAC_NWCTRL_STATCLR_MASK & !FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK) | FXMAC_NWCTRL_MDEN_MASK; write_reg((FXMAC_IOBASE + FXMAC_NWCTRL_OFFSET) as *mut u32, netctrl); FXmacConfigureCaps(instance_p); @@ -461,7 +613,7 @@ fn FXmacReset(instance_p: &mut FXmac) { * FXMAC_TRANSMITTER_ENABLE_OPTION and FXMAC_RECEIVER_ENABLE_OPTION are set. */ let options = instance_p.config.network_default_config - & !((FXMAC_TRANSMITTER_ENABLE_OPTION | FXMAC_RECEIVER_ENABLE_OPTION) as u32); + & !(FXMAC_TRANSMITTER_ENABLE_OPTION | FXMAC_RECEIVER_ENABLE_OPTION); FXmacSetOptions(instance_p, options, 0); let options = !instance_p.config.network_default_config; FXmacClearOptions(instance_p, options, 0); @@ -547,7 +699,7 @@ fn FXmacDmaWidth(moudle_id: u32) -> u32 { return FXMAC_NWCFG_BUS_WIDTH_32_MASK; } - let read_regs = read_reg((FXMAC_IOBASE + FXMAC_DESIGNCFG_DEBUG1_OFFSET as u64) as *const u32); + let read_regs = read_reg((FXMAC_IOBASE + FXMAC_DESIGNCFG_DEBUG1_OFFSET) as *const u32); match ((read_regs & FXMAC_DESIGNCFG_DEBUG1_BUS_WIDTH_MASK) >> 25) { 4 => { info!("bus width is 128"); @@ -595,18 +747,17 @@ fn FxmacFeatureSetOptions(feature: u32, xmac_p: &mut FXmac) { FXmacSetOptions(xmac_p, options, 0); } -/** - * This function sets the start address of the transmit/receive buffer queue. - * - * @param instance_p is a pointer to the instance to be worked on. - * @param queue_p is the address of the Queue to be written - * @param queue_num is the Buffer Queue Index - * @param direction indicates Transmit/Receive - * - * @note - * The buffer queue addresses has to be set before starting the transfer, so - * this function has to be called in prior to FXmacStart() - */ +/// Sets the start address of the transmit/receive buffer queue. +/// +/// # Arguments +/// +/// * `queue_p` - Physical base address of the queue ring. +/// * `queue_num` - Queue index to configure. +/// * `direction` - [`FXMAC_SEND`] or [`FXMAC_RECV`]. +/// +/// # Note +/// +/// The buffer queue address must be configured before calling [`FXmacStart`]. pub fn FXmacSetQueuePtr(queue_p: u64, queue_num: u8, direction: u32) { //assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); // If already started, then just return @@ -629,22 +780,18 @@ pub fn FXmacSetQueuePtr(queue_p: u64, queue_num: u8, direction: u32) { ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32, ); } + } else if direction == FXMAC_SEND { + write_reg( + (FXMAC_IOBASE + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, queue_num as u64)) + as *mut u32, + ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32, + ); } else { - if direction == FXMAC_SEND { - write_reg( - (FXMAC_IOBASE - + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, queue_num as u64)) - as *mut u32, - ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32, - ); - } else { - write_reg( - (FXMAC_IOBASE - + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_RXQ1BASE_OFFSET, queue_num as u64)) - as *mut u32, - ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32, - ); - } + write_reg( + (FXMAC_IOBASE + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_RXQ1BASE_OFFSET, queue_num as u64)) + as *mut u32, + ((queue_p & ULONG64_LO_MASK) | flag_queue_p) as u32, + ); } if direction == FXMAC_SEND @@ -770,7 +917,7 @@ fn FXmacSetOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u32 /* Allow broadcast address reception */ if (options & FXMAC_BROADCAST_OPTION) != 0 { - reg_new_netcfg &= !(FXMAC_NWCFG_BCASTDI_MASK as u32); + reg_new_netcfg &= !FXMAC_NWCFG_BCASTDI_MASK; } /* Allow multicast address filtering */ @@ -923,32 +1070,32 @@ fn FXmacClearOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u3 * It is configured in two different length, up to 1536 and 10240 bytes */ if (options & FXMAC_FRAME1536_OPTION) != 0 { - reg_new_net_cfg &= !(FXMAC_NWCFG_1536RXEN_MASK as u32); + reg_new_net_cfg &= !FXMAC_NWCFG_1536RXEN_MASK; } /* Turn off VLAN packet only */ if (options & FXMAC_VLAN_OPTION) != 0 { - reg_new_net_cfg &= !(FXMAC_NWCFG_NVLANDISC_MASK as u32); + reg_new_net_cfg &= !FXMAC_NWCFG_NVLANDISC_MASK; } /* Turn off FCS stripping on receive packets */ if (options & FXMAC_FCS_STRIP_OPTION) != 0 { - reg_new_net_cfg &= !(FXMAC_NWCFG_FCS_REMOVE_MASK as u32); + reg_new_net_cfg &= !FXMAC_NWCFG_FCS_REMOVE_MASK; } /* Turn off length/type field checking on receive packets */ if (options & FXMAC_LENTYPE_ERR_OPTION) != 0 { - reg_new_net_cfg &= !(FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK as u32); + reg_new_net_cfg &= !FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK; } /* Turn off flow control */ if (options & FXMAC_FLOW_CONTROL_OPTION) != 0 { - reg_new_net_cfg &= !(FXMAC_NWCFG_PAUSE_ENABLE_MASK as u32); + reg_new_net_cfg &= !FXMAC_NWCFG_PAUSE_ENABLE_MASK; } /* Turn off promiscuous frame filtering (all frames are received) */ if (options & FXMAC_PROMISC_OPTION) != 0 { - reg_new_net_cfg &= !(FXMAC_NWCFG_COPYALLEN_MASK as u32); + reg_new_net_cfg &= !FXMAC_NWCFG_COPYALLEN_MASK; } /* Disallow broadcast address filtering => broadcast reception */ @@ -958,12 +1105,12 @@ fn FXmacClearOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u3 /* Disallow unicast address filtering */ if (options & FXMAC_UNICAST_OPTION) != 0 { - reg_new_net_cfg &= !(FXMAC_NWCFG_UCASTHASHEN_MASK as u32); + reg_new_net_cfg &= !FXMAC_NWCFG_UCASTHASHEN_MASK; } /* Disallow multicast address filtering */ if (options & FXMAC_MULTICAST_OPTION) != 0 { - reg_new_net_cfg &= !(FXMAC_NWCFG_MCASTHASHEN_MASK as u32); + reg_new_net_cfg &= !FXMAC_NWCFG_MCASTHASHEN_MASK; } if (options & FXMAC_TAIL_PTR_OPTION) != 0 { @@ -972,7 +1119,7 @@ fn FXmacClearOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u3 /* Disable RX checksum offload */ if (options & FXMAC_RX_CHKSUM_ENABLE_OPTION) != 0 { - reg_new_net_cfg &= !(FXMAC_NWCFG_RXCHKSUMEN_MASK as u32); + reg_new_net_cfg &= !FXMAC_NWCFG_RXCHKSUMEN_MASK; } /* Disable jumbo frames */ @@ -982,7 +1129,7 @@ fn FXmacClearOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u3 instance_p.max_mtu_size = FXMAC_MTU; instance_p.max_frame_size = FXMAC_MAX_FRAME_SIZE; - reg_new_net_cfg &= !(FXMAC_NWCFG_JUMBO_MASK as u32); + reg_new_net_cfg &= !FXMAC_NWCFG_JUMBO_MASK; reg = read_reg((FXMAC_IOBASE + FXMAC_DMACR_OFFSET) as *const u32); @@ -1021,8 +1168,7 @@ fn FXmacClearOptions(instance_p: &mut FXmac, options: u32, queue_num: u32) -> u3 } if (options & FXMAC_SGMII_ENABLE_OPTION) != 0 { - reg_new_net_cfg &= - !((FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK | FXMAC_NWCFG_PCSSEL_MASK) as u32); + reg_new_net_cfg &= !(FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK | FXMAC_NWCFG_PCSSEL_MASK); } if (options & FXMAC_LOOPBACK_NO_MII_OPTION) != 0 { @@ -1090,8 +1236,18 @@ fn FXmacClearHash() { write_reg((FXMAC_IOBASE + FXMAC_HASHH_OFFSET) as *mut u32, 0); } -/// Set the MAC address for this driver/device. The address is a 48-bit value. +/// Sets the MAC address for the specified address slot. +/// /// The device must be stopped before calling this function. +/// +/// # Arguments +/// +/// * `address_ptr` - 6-byte MAC address. +/// * `index` - Address slot index (0..FXMAC_MAX_MAC_ADDR). +/// +/// # Panics +/// +/// Panics if `index` is out of range. pub fn FXmacSetMacAddress(address_ptr: &[u8; 6], index: u8) -> u32 { let mut mac_addr: u32 = 0; let aptr = address_ptr; @@ -1105,7 +1261,7 @@ pub fn FXmacSetMacAddress(address_ptr: &[u8; 6], index: u8) -> u32 { let is_started = 0; /* Be sure device has been stopped */ - if is_started == FT_COMPONENT_IS_STARTED as u32 { + if is_started == FT_COMPONENT_IS_STARTED { //status = FXMAC_ERR_MAC_IS_PROCESSING; status = 9; error!("FXMAC is processing when calling FXmacSetMacAddress function"); @@ -1139,14 +1295,16 @@ pub fn FXmacSetMacAddress(address_ptr: &[u8; 6], index: u8) -> u32 { status } -/** - * @name: FXmacGetMacAddress - * @msg: Set the MAC address according to index - * @param {FXmac} *mac is a pointer to the instance to be worked on. - * @param {void} *address_ptr is an output parameter, and is a pointer to a buffer into - * which the current MAC address will be copied. - * @param {u8} index is a index to which MAC (0-3) address. - */ +/// Reads a MAC address from the specified address slot. +/// +/// # Arguments +/// +/// * `address_ptr` - Output buffer for the MAC address. +/// * `index` - Address slot index (0..FXMAC_MAX_MAC_ADDR). +/// +/// # Panics +/// +/// Panics if `index` is out of range. pub fn FXmacGetMacAddress(address_ptr: &mut [u8; 6], index: u8) { assert!((index as u32) < FXMAC_MAX_MAC_ADDR); @@ -1162,9 +1320,15 @@ pub fn FXmacGetMacAddress(address_ptr: &mut [u8; 6], index: u8) { address_ptr[5] = (reg_value >> 8) as u8; } -/// Set 48-bit MAC addresses in hash table. +/// Sets a 48-bit MAC address entry in the hash table. +/// /// The device must be stopped before calling this function. /// +/// # Arguments +/// +/// * `intance_p` - Mutable reference to the FXMAC instance. +/// * `mac_address` - The MAC address to hash. +/// /// The hash address register is 64 bits long and takes up two locations in /// the memory map. The least significant bits are stored in hash register /// bottom and the most significant bits in hash register top. diff --git a/src/fxmac_const.rs b/src/fxmac_const.rs index e6910875e..46d21451c 100644 --- a/src/fxmac_const.rs +++ b/src/fxmac_const.rs @@ -1,310 +1,315 @@ +//! FXMAC hardware register offsets and bit definitions. +//! +//! This module mirrors the low-level register layout from the FXMAC hardware +//! specification and is primarily intended for internal driver use. //////////////////// // fxmac_hw.h -pub(crate) const FXMAC_RX_BUF_UNIT:u32 = 64; /* Number of receive buffer bytes as a unit, this is HW setup */ +pub(crate) const FXMAC_RX_BUF_UNIT: u32 = 64; /* Number of receive buffer bytes as a unit, this is HW setup */ -pub(crate) const FXMAC_MAX_RXBD:u32 = 128;/* Size of RX buffer descriptor queues */ -pub(crate) const FXMAC_MAX_TXBD:u32 = 128;/* Size of TX buffer descriptor queues */ +pub(crate) const FXMAC_MAX_RXBD: u32 = 128; /* Size of RX buffer descriptor queues */ +pub(crate) const FXMAC_MAX_TXBD: u32 = 128; /* Size of TX buffer descriptor queues */ -pub(crate) const FXMAC_MAX_HASH_BITS:u32 = 64;/* Maximum value for hash bits. 2**6 */ +pub(crate) const FXMAC_MAX_HASH_BITS: u32 = 64; /* Maximum value for hash bits. 2**6 */ /************************** Constant Definitions *****************************/ -pub(crate) const FXMAC_MAX_MAC_ADDR:u32 = 4;/* Maxmum number of mac address supported */ -pub(crate) const FXMAC_MAX_TYPE_ID:u32 = 4; /* Maxmum number of type id supported */ +pub(crate) const FXMAC_MAX_MAC_ADDR: u32 = 4; /* Maxmum number of mac address supported */ +pub(crate) const FXMAC_MAX_TYPE_ID: u32 = 4; /* Maxmum number of type id supported */ /// for aarch64 -pub(crate) const FXMAC_BD_ALIGNMENT:u32 = 64;/* Minimum buffer descriptor alignment on the local bus */ - - -pub(crate) const FXMAC_RX_BUF_ALIGNMENT:u32 = 4;/* Minimum buffer alignment when using options that impose - alignment restrictions on the buffer data on the local bus */ - -pub(crate) const FXMAC_NWCTRL_OFFSET:u64 = 0x00000000; /* Network Control reg */ -pub(crate) const FXMAC_NWCFG_OFFSET:u64 = 0x00000004; /* Network Config reg */ -pub(crate) const FXMAC_NWSR_OFFSET:u64 = 0x00000008; /* Network Status reg */ -pub(crate) const FXMAC_DMACR_OFFSET:u64 = 0x00000010; /* DMA Control reg */ -pub(crate) const FXMAC_TXSR_OFFSET:u64 = 0x00000014; /* TX Status reg */ -pub(crate) const FXMAC_RXQBASE_OFFSET:u64 = 0x00000018;/* RX Q Base address reg */ -pub(crate) const FXMAC_TXQBASE_OFFSET:u64 = 0x0000001C;/* TX Q Base address reg */ -pub(crate) const FXMAC_RXSR_OFFSET:u64 = 0x00000020; /* RX Status reg */ - -pub(crate) const FXMAC_ISR_OFFSET:u64 = 0x00000024;/* Interrupt Status reg */ -pub(crate) const FXMAC_IER_OFFSET:u64 = 0x00000028;/* Interrupt Enable reg */ -pub(crate) const FXMAC_IDR_OFFSET:u64 = 0x0000002C;/* Interrupt Disable reg */ -pub(crate) const FXMAC_IMR_OFFSET:u64 = 0x00000030;/* Interrupt Mask reg */ - -pub(crate) const FXMAC_PHYMNTNC_OFFSET:u64 = 0x00000034;/* Phy Maintaince reg */ -pub(crate) const FXMAC_RXPAUSE_OFFSET:u64 = 0x00000038; /* RX Pause Time reg */ -pub(crate) const FXMAC_TXPAUSE_OFFSET:u64 = 0x0000003C; /* TX Pause Time reg */ - -pub(crate) const FXMAC_JUMBOMAXLEN_OFFSET:u64 = 0x00000048;/* Jumbo max length reg */ -pub(crate) const FXMAC_GEM_HSMAC:u32 = 0x0050; /* Hs mac config register*/ -pub(crate) const FXMAC_RXWATERMARK_OFFSET:u64 = 0x0000007C;/* RX watermark reg */ - -pub(crate) const FXMAC_HASHL_OFFSET:u64 = 0x00000080;/* Hash Low address reg */ -pub(crate) const FXMAC_HASHH_OFFSET:u64 = 0x00000084;/* Hash High address reg */ - -pub(crate) const FXMAC_GEM_SA1B:u32 = 0x0088; /* Specific1 Bottom */ -pub(crate) const FXMAC_GEM_SA1T:u32 = 0x008C; /* Specific1 Top */ -pub(crate) const FXMAC_GEM_SA2B:u32 = 0x0090; /* Specific2 Bottom */ -pub(crate) const FXMAC_GEM_SA2T:u32 = 0x0094; /* Specific2 Top */ -pub(crate) const FXMAC_GEM_SA3B:u32 = 0x0098; /* Specific3 Bottom */ -pub(crate) const FXMAC_GEM_SA3T:u32 = 0x009C; /* Specific3 Top */ -pub(crate) const FXMAC_GEM_SA4B:u32 = 0x00A0; /* Specific4 Bottom */ -pub(crate) const FXMAC_GEM_SA4T:u32 = 0x00A4; /* Specific4 Top */ - -pub(crate) const FXMAC_MATCH1_OFFSET:u64 = 0x000000A8;/* Type ID1 Match reg */ -pub(crate) const FXMAC_MATCH2_OFFSET:u64 = 0x000000AC;/* Type ID2 Match reg */ -pub(crate) const FXMAC_MATCH3_OFFSET:u64 = 0x000000B0;/* Type ID3 Match reg */ -pub(crate) const FXMAC_MATCH4_OFFSET:u64 = 0x000000B4;/* Type ID4 Match reg */ - -pub(crate) const FXMAC_STRETCH_OFFSET:u64 = 0x000000BC; /* IPG Stretch reg */ -pub(crate) const FXMAC_REVISION_REG_OFFSET:u64 = 0x000000FC;/* identification number and module revision */ - -pub(crate) const FXMAC_OCTTXL_OFFSET:u64 = 0x00000100;/* Octects transmitted Low reg */ -pub(crate) const FXMAC_OCTTXH_OFFSET:u64 = 0x00000104;/* Octects transmitted High reg */ - -pub(crate) const FXMAC_TXCNT_OFFSET:u64 = 0x00000108; /* Error-free Frmaes transmitted counter */ -pub(crate) const FXMAC_TXBCCNT_OFFSET:u64 = 0x0000010C; /* Error-free Broadcast Frames counter*/ -pub(crate) const FXMAC_TXMCCNT_OFFSET:u64 = 0x00000110; /* Error-free Multicast Frame counter */ -pub(crate) const FXMAC_TXPAUSECNT_OFFSET:u64 = 0x00000114;/* Pause Frames Transmitted Counter */ -pub(crate) const FXMAC_TX64CNT_OFFSET:u64 = 0x00000118; /* Error-free 64 byte Frames Transmitted counter */ -pub(crate) const FXMAC_TX65CNT_OFFSET:u64 = 0x0000011C; /* Error-free 65-127 byte Frames Transmitted counter */ -pub(crate) const FXMAC_TX128CNT_OFFSET:u64 = 0x00000120; /* Error-free 128-255 byte Frames Transmitted counter*/ -pub(crate) const FXMAC_TX256CNT_OFFSET:u64 = 0x00000124; /* Error-free 256-511 byte Frames transmitted counter */ -pub(crate) const FXMAC_TX512CNT_OFFSET:u64 = 0x00000128; /* Error-free 512-1023 byte Frames transmitted counter */ -pub(crate) const FXMAC_TX1024CNT_OFFSET:u64 = 0x0000012C; /* Error-free 1024-1518 byte Frames transmitted counter */ -pub(crate) const FXMAC_TX1519CNT_OFFSET:u64 = 0x00000130; /* Error-free larger than 1519 byte Frames transmitted counter */ -pub(crate) const FXMAC_TXURUNCNT_OFFSET:u64 = 0x00000134; /* TX under run error counter */ - -pub(crate) const FXMAC_SNGLCOLLCNT_OFFSET:u64 = 0x00000138; /* Single Collision Frame Counter */ -pub(crate) const FXMAC_MULTICOLLCNT_OFFSET:u64 = 0x0000013C; /* Multiple Collision Frame Counter */ -pub(crate) const FXMAC_EXCESSCOLLCNT_OFFSET:u64 = 0x00000140;/* Excessive Collision Frame Counter */ -pub(crate) const FXMAC_LATECOLLCNT_OFFSET:u64 = 0x00000144; /* Late Collision Frame Counter */ -pub(crate) const FXMAC_TXDEFERCNT_OFFSET:u64 = 0x00000148; /* Deferred Transmission Frame Counter */ -pub(crate) const FXMAC_TXCSENSECNT_OFFSET:u64 = 0x0000014C; /* Transmit Carrier Sense Error Counter */ - -pub(crate) const FXMAC_OCTRXL_OFFSET:u64 = 0x00000150;/* Octects Received register Low */ -pub(crate) const FXMAC_OCTRXH_OFFSET:u64 = 0x00000154;/* Octects Received register High */ - -pub(crate) const FXMAC_RXCNT_OFFSET:u64 = 0x00000158; /* Error-free Frames Received Counter */ -pub(crate) const FXMAC_RXBROADCNT_OFFSET:u64 = 0x0000015C; /* Error-free Broadcast Frames Received Counter */ -pub(crate) const FXMAC_RXMULTICNT_OFFSET:u64 = 0x00000160; /* Error-free Multicast Frames Received Counter */ -pub(crate) const FXMAC_RXPAUSECNT_OFFSET:u64 = 0x00000164; /* Pause Frames Received Counter */ -pub(crate) const FXMAC_RX64CNT_OFFSET:u64 = 0x00000168; /* Error-free 64 byte Frames Received Counter */ -pub(crate) const FXMAC_RX65CNT_OFFSET:u64 = 0x0000016C; /* Error-free 65-127 byte Frames Received Counter */ -pub(crate) const FXMAC_RX128CNT_OFFSET:u64 = 0x00000170; /* Error-free 128-255 byte Frames Received Counter */ -pub(crate) const FXMAC_RX256CNT_OFFSET:u64 = 0x00000174; /* Error-free 256-512 byte Frames Received Counter */ -pub(crate) const FXMAC_RX512CNT_OFFSET:u64 = 0x00000178; /* Error-free 512-1023 byte Frames Received Counter */ -pub(crate) const FXMAC_RX1024CNT_OFFSET:u64 = 0x0000017C; /* Error-free 1024-1518 byte Frames Received Counter */ -pub(crate) const FXMAC_RX1519CNT_OFFSET:u64 = 0x00000180; /* Error-free 1519-max byte Frames Received Counter */ -pub(crate) const FXMAC_RXUNDRCNT_OFFSET:u64 = 0x00000184; /* Undersize Frames Received Counter */ -pub(crate) const FXMAC_RXOVRCNT_OFFSET:u64 = 0x00000188; /* Oversize Frames Received Counter */ -pub(crate) const FXMAC_RXJABCNT_OFFSET:u64 = 0x0000018C; /* Jabbers Received Counter */ -pub(crate) const FXMAC_RXFCSCNT_OFFSET:u64 = 0x00000190; /* Frame Check Sequence Error Counter */ -pub(crate) const FXMAC_RXLENGTHCNT_OFFSET:u64 = 0x00000194;/* Length Field Error Counter */ -pub(crate) const FXMAC_RXSYMBCNT_OFFSET:u64 = 0x00000198; /* Symbol Error Counter */ -pub(crate) const FXMAC_RXALIGNCNT_OFFSET:u64 = 0x0000019C; /* Alignment Error Counter */ -pub(crate) const FXMAC_RXRESERRCNT_OFFSET:u64 = 0x000001A0;/* Receive Resource Error Counter */ -pub(crate) const FXMAC_RXORCNT_OFFSET:u64 = 0x000001A4; /* Receive Overrun Counter */ -pub(crate) const FXMAC_RXIPCCNT_OFFSET:u64 = 0x000001A8; /* IP header Checksum Error Counter */ -pub(crate) const FXMAC_RXTCPCCNT_OFFSET:u64 = 0x000001AC; /* TCP Checksum Error Counter */ -pub(crate) const FXMAC_RXUDPCCNT_OFFSET:u64 = 0x000001B0; /* UDP Checksum Error Counter */ -pub(crate) const FXMAC_LAST_OFFSET:u64 = 0x000001B4; /* Last statistic counter offset, for clearing */ - -pub(crate) const FXMAC_1588_SEC_OFFSET:u64 = 0x000001D0; /* 1588 second counter */ -pub(crate) const FXMAC_1588_NANOSEC_OFFSET:u64 = 0x000001D4; /* 1588 nanosecond counter */ -pub(crate) const FXMAC_1588_ADJ_OFFSET:u64 = 0x000001D8; /* 1588 nanosecond adjustment counter */ -pub(crate) const FXMAC_1588_INC_OFFSET:u64 = 0x000001DC; /* 1588 nanosecond increment counter */ -pub(crate) const FXMAC_PTP_TXSEC_OFFSET:u64 = 0x000001E0; /* 1588 PTP transmit second counter */ -pub(crate) const FXMAC_PTP_TXNANOSEC_OFFSET:u64 = 0x000001E4; /* 1588 PTP transmit nanosecond counter */ -pub(crate) const FXMAC_PTP_RXSEC_OFFSET:u64 = 0x000001E8; /* 1588 PTP receive second counter */ -pub(crate) const FXMAC_PTP_RXNANOSEC_OFFSET:u64 = 0x000001EC; /* 1588 PTP receive nanosecond counter */ -pub(crate) const FXMAC_PTPP_TXSEC_OFFSET:u64 = 0x000001F0; /* 1588 PTP peer transmit second counter */ -pub(crate) const FXMAC_PTPP_TXNANOSEC_OFFSET:u64 = 0x000001F4;/* 1588 PTP peer transmit nanosecond counter */ -pub(crate) const FXMAC_PTPP_RXSEC_OFFSET:u64 = 0x000001F8; /* 1588 PTP peer receive second counter */ -pub(crate) const FXMAC_PTPP_RXNANOSEC_OFFSET:u64 = 0x000001FC;/* 1588 PTP peer receive nanosecond counter */ - -pub(crate) const FXMAC_PCS_CONTROL_OFFSET:u64 = 0x00000200;/* All PCS registers */ - -pub(crate) const FXMAC_PCS_STATUS_OFFSET:u64 = 0x00000204;/* All PCS status */ - -pub(crate) const FXMAC_PCS_AN_LP_OFFSET:u64 = 0x00000214; /* All PCS link partner's base page */ - -pub(crate) const FXMAC_DESIGNCFG_DEBUG1_OFFSET:u64 = 0x00000280;/* Design Configuration Register 1 */ - -pub(crate) const FXMAC_DESIGNCFG_DEBUG2_OFFSET:u64 = 0x00000284;/* Design Configuration Register 2 */ - -pub(crate) const FXMAC_INTQ1_STS_OFFSET:u64 = 0x00000400;/* Interrupt Q1 Status reg */ - -pub(crate) const FXMAC_TXQ1BASE_OFFSET:u64 = 0x00000440;/* TX Q1 Base address reg */ -pub(crate) const FXMAC_RXQ1BASE_OFFSET:u64 = 0x00000480;/* RX Q1 Base address reg */ - -pub(crate) const FXMAC_RXBUFQ1_SIZE_OFFSET:u64 = 0x000004a0;/* Receive Buffer Size */ +pub(crate) const FXMAC_BD_ALIGNMENT: u32 = 64; /* Minimum buffer descriptor alignment on the local bus */ + +pub(crate) const FXMAC_RX_BUF_ALIGNMENT: u32 = 4; /* Minimum buffer alignment when using options that impose + alignment restrictions on the buffer data on the local bus */ + +pub(crate) const FXMAC_NWCTRL_OFFSET: u64 = 0x00000000; /* Network Control reg */ +pub(crate) const FXMAC_NWCFG_OFFSET: u64 = 0x00000004; /* Network Config reg */ +pub(crate) const FXMAC_NWSR_OFFSET: u64 = 0x00000008; /* Network Status reg */ +pub(crate) const FXMAC_DMACR_OFFSET: u64 = 0x00000010; /* DMA Control reg */ +pub(crate) const FXMAC_TXSR_OFFSET: u64 = 0x00000014; /* TX Status reg */ +pub(crate) const FXMAC_RXQBASE_OFFSET: u64 = 0x00000018; /* RX Q Base address reg */ +pub(crate) const FXMAC_TXQBASE_OFFSET: u64 = 0x0000001C; /* TX Q Base address reg */ +pub(crate) const FXMAC_RXSR_OFFSET: u64 = 0x00000020; /* RX Status reg */ + +pub(crate) const FXMAC_ISR_OFFSET: u64 = 0x00000024; /* Interrupt Status reg */ +pub(crate) const FXMAC_IER_OFFSET: u64 = 0x00000028; /* Interrupt Enable reg */ +pub(crate) const FXMAC_IDR_OFFSET: u64 = 0x0000002C; /* Interrupt Disable reg */ +pub(crate) const FXMAC_IMR_OFFSET: u64 = 0x00000030; /* Interrupt Mask reg */ + +pub(crate) const FXMAC_PHYMNTNC_OFFSET: u64 = 0x00000034; /* Phy Maintaince reg */ +pub(crate) const FXMAC_RXPAUSE_OFFSET: u64 = 0x00000038; /* RX Pause Time reg */ +pub(crate) const FXMAC_TXPAUSE_OFFSET: u64 = 0x0000003C; /* TX Pause Time reg */ + +pub(crate) const FXMAC_JUMBOMAXLEN_OFFSET: u64 = 0x00000048; /* Jumbo max length reg */ +pub(crate) const FXMAC_GEM_HSMAC: u32 = 0x0050; /* Hs mac config register*/ +pub(crate) const FXMAC_RXWATERMARK_OFFSET: u64 = 0x0000007C; /* RX watermark reg */ + +pub(crate) const FXMAC_HASHL_OFFSET: u64 = 0x00000080; /* Hash Low address reg */ +pub(crate) const FXMAC_HASHH_OFFSET: u64 = 0x00000084; /* Hash High address reg */ + +pub(crate) const FXMAC_GEM_SA1B: u32 = 0x0088; /* Specific1 Bottom */ +pub(crate) const FXMAC_GEM_SA1T: u32 = 0x008C; /* Specific1 Top */ +pub(crate) const FXMAC_GEM_SA2B: u32 = 0x0090; /* Specific2 Bottom */ +pub(crate) const FXMAC_GEM_SA2T: u32 = 0x0094; /* Specific2 Top */ +pub(crate) const FXMAC_GEM_SA3B: u32 = 0x0098; /* Specific3 Bottom */ +pub(crate) const FXMAC_GEM_SA3T: u32 = 0x009C; /* Specific3 Top */ +pub(crate) const FXMAC_GEM_SA4B: u32 = 0x00A0; /* Specific4 Bottom */ +pub(crate) const FXMAC_GEM_SA4T: u32 = 0x00A4; /* Specific4 Top */ + +pub(crate) const FXMAC_MATCH1_OFFSET: u64 = 0x000000A8; /* Type ID1 Match reg */ +pub(crate) const FXMAC_MATCH2_OFFSET: u64 = 0x000000AC; /* Type ID2 Match reg */ +pub(crate) const FXMAC_MATCH3_OFFSET: u64 = 0x000000B0; /* Type ID3 Match reg */ +pub(crate) const FXMAC_MATCH4_OFFSET: u64 = 0x000000B4; /* Type ID4 Match reg */ + +pub(crate) const FXMAC_STRETCH_OFFSET: u64 = 0x000000BC; /* IPG Stretch reg */ +pub(crate) const FXMAC_REVISION_REG_OFFSET: u64 = 0x000000FC; /* identification number and module revision */ + +pub(crate) const FXMAC_OCTTXL_OFFSET: u64 = 0x00000100; /* Octects transmitted Low reg */ +pub(crate) const FXMAC_OCTTXH_OFFSET: u64 = 0x00000104; /* Octects transmitted High reg */ + +pub(crate) const FXMAC_TXCNT_OFFSET: u64 = 0x00000108; /* Error-free Frmaes transmitted counter */ +pub(crate) const FXMAC_TXBCCNT_OFFSET: u64 = 0x0000010C; /* Error-free Broadcast Frames counter*/ +pub(crate) const FXMAC_TXMCCNT_OFFSET: u64 = 0x00000110; /* Error-free Multicast Frame counter */ +pub(crate) const FXMAC_TXPAUSECNT_OFFSET: u64 = 0x00000114; /* Pause Frames Transmitted Counter */ +pub(crate) const FXMAC_TX64CNT_OFFSET: u64 = 0x00000118; /* Error-free 64 byte Frames Transmitted counter */ +pub(crate) const FXMAC_TX65CNT_OFFSET: u64 = 0x0000011C; /* Error-free 65-127 byte Frames Transmitted counter */ +pub(crate) const FXMAC_TX128CNT_OFFSET: u64 = 0x00000120; /* Error-free 128-255 byte Frames Transmitted counter*/ +pub(crate) const FXMAC_TX256CNT_OFFSET: u64 = 0x00000124; /* Error-free 256-511 byte Frames transmitted counter */ +pub(crate) const FXMAC_TX512CNT_OFFSET: u64 = 0x00000128; /* Error-free 512-1023 byte Frames transmitted counter */ +pub(crate) const FXMAC_TX1024CNT_OFFSET: u64 = 0x0000012C; /* Error-free 1024-1518 byte Frames transmitted counter */ +pub(crate) const FXMAC_TX1519CNT_OFFSET: u64 = 0x00000130; /* Error-free larger than 1519 byte Frames transmitted counter */ +pub(crate) const FXMAC_TXURUNCNT_OFFSET: u64 = 0x00000134; /* TX under run error counter */ + +pub(crate) const FXMAC_SNGLCOLLCNT_OFFSET: u64 = 0x00000138; /* Single Collision Frame Counter */ +pub(crate) const FXMAC_MULTICOLLCNT_OFFSET: u64 = 0x0000013C; /* Multiple Collision Frame Counter */ +pub(crate) const FXMAC_EXCESSCOLLCNT_OFFSET: u64 = 0x00000140; /* Excessive Collision Frame Counter */ +pub(crate) const FXMAC_LATECOLLCNT_OFFSET: u64 = 0x00000144; /* Late Collision Frame Counter */ +pub(crate) const FXMAC_TXDEFERCNT_OFFSET: u64 = 0x00000148; /* Deferred Transmission Frame Counter */ +pub(crate) const FXMAC_TXCSENSECNT_OFFSET: u64 = 0x0000014C; /* Transmit Carrier Sense Error Counter */ + +pub(crate) const FXMAC_OCTRXL_OFFSET: u64 = 0x00000150; /* Octects Received register Low */ +pub(crate) const FXMAC_OCTRXH_OFFSET: u64 = 0x00000154; /* Octects Received register High */ + +pub(crate) const FXMAC_RXCNT_OFFSET: u64 = 0x00000158; /* Error-free Frames Received Counter */ +pub(crate) const FXMAC_RXBROADCNT_OFFSET: u64 = 0x0000015C; /* Error-free Broadcast Frames Received Counter */ +pub(crate) const FXMAC_RXMULTICNT_OFFSET: u64 = 0x00000160; /* Error-free Multicast Frames Received Counter */ +pub(crate) const FXMAC_RXPAUSECNT_OFFSET: u64 = 0x00000164; /* Pause Frames Received Counter */ +pub(crate) const FXMAC_RX64CNT_OFFSET: u64 = 0x00000168; /* Error-free 64 byte Frames Received Counter */ +pub(crate) const FXMAC_RX65CNT_OFFSET: u64 = 0x0000016C; /* Error-free 65-127 byte Frames Received Counter */ +pub(crate) const FXMAC_RX128CNT_OFFSET: u64 = 0x00000170; /* Error-free 128-255 byte Frames Received Counter */ +pub(crate) const FXMAC_RX256CNT_OFFSET: u64 = 0x00000174; /* Error-free 256-512 byte Frames Received Counter */ +pub(crate) const FXMAC_RX512CNT_OFFSET: u64 = 0x00000178; /* Error-free 512-1023 byte Frames Received Counter */ +pub(crate) const FXMAC_RX1024CNT_OFFSET: u64 = 0x0000017C; /* Error-free 1024-1518 byte Frames Received Counter */ +pub(crate) const FXMAC_RX1519CNT_OFFSET: u64 = 0x00000180; /* Error-free 1519-max byte Frames Received Counter */ +pub(crate) const FXMAC_RXUNDRCNT_OFFSET: u64 = 0x00000184; /* Undersize Frames Received Counter */ +pub(crate) const FXMAC_RXOVRCNT_OFFSET: u64 = 0x00000188; /* Oversize Frames Received Counter */ +pub(crate) const FXMAC_RXJABCNT_OFFSET: u64 = 0x0000018C; /* Jabbers Received Counter */ +pub(crate) const FXMAC_RXFCSCNT_OFFSET: u64 = 0x00000190; /* Frame Check Sequence Error Counter */ +pub(crate) const FXMAC_RXLENGTHCNT_OFFSET: u64 = 0x00000194; /* Length Field Error Counter */ +pub(crate) const FXMAC_RXSYMBCNT_OFFSET: u64 = 0x00000198; /* Symbol Error Counter */ +pub(crate) const FXMAC_RXALIGNCNT_OFFSET: u64 = 0x0000019C; /* Alignment Error Counter */ +pub(crate) const FXMAC_RXRESERRCNT_OFFSET: u64 = 0x000001A0; /* Receive Resource Error Counter */ +pub(crate) const FXMAC_RXORCNT_OFFSET: u64 = 0x000001A4; /* Receive Overrun Counter */ +pub(crate) const FXMAC_RXIPCCNT_OFFSET: u64 = 0x000001A8; /* IP header Checksum Error Counter */ +pub(crate) const FXMAC_RXTCPCCNT_OFFSET: u64 = 0x000001AC; /* TCP Checksum Error Counter */ +pub(crate) const FXMAC_RXUDPCCNT_OFFSET: u64 = 0x000001B0; /* UDP Checksum Error Counter */ +pub(crate) const FXMAC_LAST_OFFSET: u64 = 0x000001B4; /* Last statistic counter offset, for clearing */ + +pub(crate) const FXMAC_1588_SEC_OFFSET: u64 = 0x000001D0; /* 1588 second counter */ +pub(crate) const FXMAC_1588_NANOSEC_OFFSET: u64 = 0x000001D4; /* 1588 nanosecond counter */ +pub(crate) const FXMAC_1588_ADJ_OFFSET: u64 = 0x000001D8; /* 1588 nanosecond adjustment counter */ +pub(crate) const FXMAC_1588_INC_OFFSET: u64 = 0x000001DC; /* 1588 nanosecond increment counter */ +pub(crate) const FXMAC_PTP_TXSEC_OFFSET: u64 = 0x000001E0; /* 1588 PTP transmit second counter */ +pub(crate) const FXMAC_PTP_TXNANOSEC_OFFSET: u64 = 0x000001E4; /* 1588 PTP transmit nanosecond counter */ +pub(crate) const FXMAC_PTP_RXSEC_OFFSET: u64 = 0x000001E8; /* 1588 PTP receive second counter */ +pub(crate) const FXMAC_PTP_RXNANOSEC_OFFSET: u64 = 0x000001EC; /* 1588 PTP receive nanosecond counter */ +pub(crate) const FXMAC_PTPP_TXSEC_OFFSET: u64 = 0x000001F0; /* 1588 PTP peer transmit second counter */ +pub(crate) const FXMAC_PTPP_TXNANOSEC_OFFSET: u64 = 0x000001F4; /* 1588 PTP peer transmit nanosecond counter */ +pub(crate) const FXMAC_PTPP_RXSEC_OFFSET: u64 = 0x000001F8; /* 1588 PTP peer receive second counter */ +pub(crate) const FXMAC_PTPP_RXNANOSEC_OFFSET: u64 = 0x000001FC; /* 1588 PTP peer receive nanosecond counter */ + +pub(crate) const FXMAC_PCS_CONTROL_OFFSET: u64 = 0x00000200; /* All PCS registers */ + +pub(crate) const FXMAC_PCS_STATUS_OFFSET: u64 = 0x00000204; /* All PCS status */ + +pub(crate) const FXMAC_PCS_AN_LP_OFFSET: u64 = 0x00000214; /* All PCS link partner's base page */ + +pub(crate) const FXMAC_DESIGNCFG_DEBUG1_OFFSET: u64 = 0x00000280; /* Design Configuration Register 1 */ + +pub(crate) const FXMAC_DESIGNCFG_DEBUG2_OFFSET: u64 = 0x00000284; /* Design Configuration Register 2 */ + +pub(crate) const FXMAC_INTQ1_STS_OFFSET: u64 = 0x00000400; /* Interrupt Q1 Status reg */ + +pub(crate) const FXMAC_TXQ1BASE_OFFSET: u64 = 0x00000440; /* TX Q1 Base address reg */ +pub(crate) const FXMAC_RXQ1BASE_OFFSET: u64 = 0x00000480; /* RX Q1 Base address reg */ + +pub(crate) const FXMAC_RXBUFQ1_SIZE_OFFSET: u64 = 0x000004a0; /* Receive Buffer Size */ // FXMAC_RXBUFQX_SIZE_OFFSET(x) (FXMAC_RXBUFQ1_SIZE_OFFSET + (x << 2)) pub const fn FXMAC_RXBUFQX_SIZE_OFFSET(value: u64) -> u64 { FXMAC_RXBUFQ1_SIZE_OFFSET + (value << 2) } -pub(crate) const FXMAC_RXBUFQX_SIZE_MASK:u32 = GENMASK(7, 0); +pub(crate) const FXMAC_RXBUFQX_SIZE_MASK: u32 = GENMASK(7, 0); -pub(crate) const FXMAC_MSBBUF_TXQBASE_OFFSET:u64 = 0x000004C8;/* MSB Buffer TX Q Base reg */ -pub(crate) const FXMAC_MSBBUF_RXQBASE_OFFSET:u64 = 0x000004D4;/* MSB Buffer RX Q Base reg */ -pub(crate) const FXMAC_TXQSEGALLOC_QLOWER_OFFSET:u64 = 0x000005A0;/* Transmit SRAM segment distribution */ -pub(crate) const FXMAC_INTQ1_IER_OFFSET:u64 = 0x00000600; /* Interrupt Q1 Enable reg */ +pub(crate) const FXMAC_MSBBUF_TXQBASE_OFFSET: u64 = 0x000004C8; /* MSB Buffer TX Q Base reg */ +pub(crate) const FXMAC_MSBBUF_RXQBASE_OFFSET: u64 = 0x000004D4; /* MSB Buffer RX Q Base reg */ +pub(crate) const FXMAC_TXQSEGALLOC_QLOWER_OFFSET: u64 = 0x000005A0; /* Transmit SRAM segment distribution */ +pub(crate) const FXMAC_INTQ1_IER_OFFSET: u64 = 0x00000600; /* Interrupt Q1 Enable reg */ pub const fn FXMAC_INTQX_IER_SIZE_OFFSET(x: u64) -> u64 { FXMAC_INTQ1_IER_OFFSET + (x << 2) } -pub(crate) const FXMAC_INTQ1_IDR_OFFSET:u64 = 0x00000620;/* Interrupt Q1 Disable reg */ +pub(crate) const FXMAC_INTQ1_IDR_OFFSET: u64 = 0x00000620; /* Interrupt Q1 Disable reg */ pub const fn FXMAC_INTQX_IDR_SIZE_OFFSET(x: u64) -> u64 { FXMAC_INTQ1_IDR_OFFSET + (x << 2) } -pub(crate) const FXMAC_INTQ1_IMR_OFFSET:u64 = 0x00000640;/* Interrupt Q1 Mask reg */ - -pub(crate) const FXMAC_GEM_USX_CONTROL_OFFSET:u64 = 0x0A80; /* High speed PCS control register */ -pub(crate) const FXMAC_TEST_CONTROL_OFFSET:u64 = 0x0A84; /* USXGMII Test Control Register */ -pub(crate) const FXMAC_GEM_USX_STATUS_OFFSET:u64 = 0x0A88; /* USXGMII Status Register */ - -pub(crate) const FXMAC_GEM_SRC_SEL_LN:u32 = 0x1C04; -pub(crate) const FXMAC_GEM_DIV_SEL0_LN:u32 = 0x1C08; -pub(crate) const FXMAC_GEM_DIV_SEL1_LN:u32 = 0x1C0C; -pub(crate) const FXMAC_GEM_PMA_XCVR_POWER_STATE:u32 = 0x1C10; -pub(crate) const FXMAC_GEM_SPEED_MODE:u32 = 0x1C14; -pub(crate) const FXMAC_GEM_MII_SELECT:u32 = 0x1C18; -pub(crate) const FXMAC_GEM_SEL_MII_ON_RGMII:u32 = 0x1C1C; -pub(crate) const FXMAC_GEM_TX_CLK_SEL0:u32 = 0x1C20; -pub(crate) const FXMAC_GEM_TX_CLK_SEL1:u32 = 0x1C24; -pub(crate) const FXMAC_GEM_TX_CLK_SEL2:u32 = 0x1C28; -pub(crate) const FXMAC_GEM_TX_CLK_SEL3:u32 = 0x1C2C; -pub(crate) const FXMAC_GEM_RX_CLK_SEL0:u32 = 0x1C30; -pub(crate) const FXMAC_GEM_RX_CLK_SEL1:u32 = 0x1C34; -pub(crate) const FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL:u32 = 0x1C38; -pub(crate) const FXMAC_GEM_TX_CLK_SEL5:u32 = 0x1C3C; -pub(crate) const FXMAC_GEM_TX_CLK_SEL6:u32 = 0x1C40; -pub(crate) const FXMAC_GEM_RX_CLK_SEL4:u32 = 0x1C44; -pub(crate) const FXMAC_GEM_RX_CLK_SEL5:u32 = 0x1C48; -pub(crate) const FXMAC_GEM_TX_CLK_SEL3_0:u32 = 0x1C70; -pub(crate) const FXMAC_GEM_TX_CLK_SEL4_0:u32 = 0x1C74; -pub(crate) const FXMAC_GEM_RX_CLK_SEL3_0:u32 = 0x1C78; -pub(crate) const FXMAC_GEM_RX_CLK_SEL4_0:u32 = 0x1C7C; -pub(crate) const FXMAC_GEM_RGMII_TX_CLK_SEL0:u32 = 0x1C80; -pub(crate) const FXMAC_GEM_RGMII_TX_CLK_SEL1:u32 = 0x1C84; -pub(crate) const FXMAC_GEM_MODE_SEL_OFFSET:u64 = 0xDC00; -pub(crate) const FXMAC_LOOPBACK_SEL_OFFSET:u64 = 0xDC04; - -pub(crate) const FXMAC_TAIL_ENABLE:u64 = 0xe7c; /*Enable tail Register*/ +pub(crate) const FXMAC_INTQ1_IMR_OFFSET: u64 = 0x00000640; /* Interrupt Q1 Mask reg */ + +pub(crate) const FXMAC_GEM_USX_CONTROL_OFFSET: u64 = 0x0A80; /* High speed PCS control register */ +pub(crate) const FXMAC_TEST_CONTROL_OFFSET: u64 = 0x0A84; /* USXGMII Test Control Register */ +pub(crate) const FXMAC_GEM_USX_STATUS_OFFSET: u64 = 0x0A88; /* USXGMII Status Register */ + +pub(crate) const FXMAC_GEM_SRC_SEL_LN: u32 = 0x1C04; +pub(crate) const FXMAC_GEM_DIV_SEL0_LN: u32 = 0x1C08; +pub(crate) const FXMAC_GEM_DIV_SEL1_LN: u32 = 0x1C0C; +pub(crate) const FXMAC_GEM_PMA_XCVR_POWER_STATE: u32 = 0x1C10; +pub(crate) const FXMAC_GEM_SPEED_MODE: u32 = 0x1C14; +pub(crate) const FXMAC_GEM_MII_SELECT: u32 = 0x1C18; +pub(crate) const FXMAC_GEM_SEL_MII_ON_RGMII: u32 = 0x1C1C; +pub(crate) const FXMAC_GEM_TX_CLK_SEL0: u32 = 0x1C20; +pub(crate) const FXMAC_GEM_TX_CLK_SEL1: u32 = 0x1C24; +pub(crate) const FXMAC_GEM_TX_CLK_SEL2: u32 = 0x1C28; +pub(crate) const FXMAC_GEM_TX_CLK_SEL3: u32 = 0x1C2C; +pub(crate) const FXMAC_GEM_RX_CLK_SEL0: u32 = 0x1C30; +pub(crate) const FXMAC_GEM_RX_CLK_SEL1: u32 = 0x1C34; +pub(crate) const FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL: u32 = 0x1C38; +pub(crate) const FXMAC_GEM_TX_CLK_SEL5: u32 = 0x1C3C; +pub(crate) const FXMAC_GEM_TX_CLK_SEL6: u32 = 0x1C40; +pub(crate) const FXMAC_GEM_RX_CLK_SEL4: u32 = 0x1C44; +pub(crate) const FXMAC_GEM_RX_CLK_SEL5: u32 = 0x1C48; +pub(crate) const FXMAC_GEM_TX_CLK_SEL3_0: u32 = 0x1C70; +pub(crate) const FXMAC_GEM_TX_CLK_SEL4_0: u32 = 0x1C74; +pub(crate) const FXMAC_GEM_RX_CLK_SEL3_0: u32 = 0x1C78; +pub(crate) const FXMAC_GEM_RX_CLK_SEL4_0: u32 = 0x1C7C; +pub(crate) const FXMAC_GEM_RGMII_TX_CLK_SEL0: u32 = 0x1C80; +pub(crate) const FXMAC_GEM_RGMII_TX_CLK_SEL1: u32 = 0x1C84; +pub(crate) const FXMAC_GEM_MODE_SEL_OFFSET: u64 = 0xDC00; +pub(crate) const FXMAC_LOOPBACK_SEL_OFFSET: u64 = 0xDC04; + +pub(crate) const FXMAC_TAIL_ENABLE: u64 = 0xe7c; /*Enable tail Register*/ // FXMAC_TAIL_QUEUE(queue) (0x0e80 + ((queue) << 2)) - /** * @name interrupts bit definitions * Bits definitions are same in FXMAC_ISR_OFFSET, * FXMAC_IER_OFFSET, FXMAC_IDR_OFFSET, and FXMAC_IMR_OFFSET * @{ */ -pub(crate) const FXMAC_IXR_PTPPSTX_MASK:u32 = BIT(25); /* PTP Pdelay_resp TXed */ -pub(crate) const FXMAC_IXR_PTPPDRTX_MASK:u32 = BIT(24); /* PTP Pdelay_req TXed */ -pub(crate) const FXMAC_IXR_PTPPSRX_MASK:u32 = BIT(23); /* PTP Pdelay_resp RXed */ -pub(crate) const FXMAC_IXR_PTPPDRRX_MASK:u32 = BIT(22); /* PTP Pdelay_req RXed */ - -pub(crate) const FXMAC_IXR_PTPSTX_MASK:u32 = BIT(21); /* PTP Sync TXed */ -pub(crate) const FXMAC_IXR_PTPDRTX_MASK:u32 = BIT(20); /* PTP Delay_req TXed */ -pub(crate) const FXMAC_IXR_PTPSRX_MASK:u32 = BIT(19); /* PTP Sync RXed */ -pub(crate) const FXMAC_IXR_PTPDRRX_MASK:u32 = BIT(18); /* PTP Delay_req RXed */ - -pub(crate) const FXMAC_IXR_PAUSETX_MASK:u32 = BIT(14); /* Pause frame transmitted */ -pub(crate) const FXMAC_IXR_PAUSEZERO_MASK:u32 = BIT(13); /* Pause time has reached zero */ -pub(crate) const FXMAC_IXR_PAUSENZERO_MASK:u32 = BIT(12); /* Pause frame received */ -pub(crate) const FXMAC_IXR_HRESPNOK_MASK:u32 = BIT(11); /* hresp not ok */ -pub(crate) const FXMAC_IXR_RXOVR_MASK:u32 = BIT(10); /* Receive overrun occurred */ -pub(crate) const FXMAC_IXR_LINKCHANGE_MASK:u32 = BIT(9); /* link status change */ -pub(crate) const FXMAC_IXR_TXCOMPL_MASK:u32 = BIT(7); /* Frame transmitted ok */ -pub(crate) const FXMAC_IXR_TXEXH_MASK:u32 = BIT(6); /* Transmit err occurred or no buffers*/ -pub(crate) const FXMAC_IXR_RETRY_MASK:u32 = BIT(5); /* Retry limit exceeded */ -pub(crate) const FXMAC_IXR_URUN_MASK:u32 = BIT(4); /* Transmit underrun */ -pub(crate) const FXMAC_IXR_TXUSED_MASK:u32 = BIT(3); /* Tx buffer used bit read */ -pub(crate) const FXMAC_IXR_RXUSED_MASK:u32 = BIT(2); /* Rx buffer used bit read */ -pub(crate) const FXMAC_IXR_RXCOMPL_MASK:u32 = BIT(1); /* Frame received ok */ -pub(crate) const FXMAC_IXR_MGMNT_MASK:u32 = BIT(0); /* PHY management complete */ -pub(crate) const FXMAC_IXR_ALL_MASK:u32 = GENMASK(31, 0); /* Everything! */ - -pub(crate) const FXMAC_IXR_TX_ERR_MASK:u32 = (FXMAC_IXR_TXEXH_MASK | FXMAC_IXR_RETRY_MASK | FXMAC_IXR_URUN_MASK); - -pub(crate) const FXMAC_IXR_RX_ERR_MASK:u32 = (FXMAC_IXR_HRESPNOK_MASK | FXMAC_IXR_RXUSED_MASK | FXMAC_IXR_RXOVR_MASK); - -pub(crate) const FXMAC_INTR_MASK:u32 = (FXMAC_IXR_LINKCHANGE_MASK | FXMAC_IXR_TX_ERR_MASK | FXMAC_IXR_RX_ERR_MASK | FXMAC_IXR_RXCOMPL_MASK | FXMAC_IXR_TXCOMPL_MASK); +pub(crate) const FXMAC_IXR_PTPPSTX_MASK: u32 = BIT(25); /* PTP Pdelay_resp TXed */ +pub(crate) const FXMAC_IXR_PTPPDRTX_MASK: u32 = BIT(24); /* PTP Pdelay_req TXed */ +pub(crate) const FXMAC_IXR_PTPPSRX_MASK: u32 = BIT(23); /* PTP Pdelay_resp RXed */ +pub(crate) const FXMAC_IXR_PTPPDRRX_MASK: u32 = BIT(22); /* PTP Pdelay_req RXed */ + +pub(crate) const FXMAC_IXR_PTPSTX_MASK: u32 = BIT(21); /* PTP Sync TXed */ +pub(crate) const FXMAC_IXR_PTPDRTX_MASK: u32 = BIT(20); /* PTP Delay_req TXed */ +pub(crate) const FXMAC_IXR_PTPSRX_MASK: u32 = BIT(19); /* PTP Sync RXed */ +pub(crate) const FXMAC_IXR_PTPDRRX_MASK: u32 = BIT(18); /* PTP Delay_req RXed */ + +pub(crate) const FXMAC_IXR_PAUSETX_MASK: u32 = BIT(14); /* Pause frame transmitted */ +pub(crate) const FXMAC_IXR_PAUSEZERO_MASK: u32 = BIT(13); /* Pause time has reached zero */ +pub(crate) const FXMAC_IXR_PAUSENZERO_MASK: u32 = BIT(12); /* Pause frame received */ +pub(crate) const FXMAC_IXR_HRESPNOK_MASK: u32 = BIT(11); /* hresp not ok */ +pub(crate) const FXMAC_IXR_RXOVR_MASK: u32 = BIT(10); /* Receive overrun occurred */ +pub(crate) const FXMAC_IXR_LINKCHANGE_MASK: u32 = BIT(9); /* link status change */ +pub(crate) const FXMAC_IXR_TXCOMPL_MASK: u32 = BIT(7); /* Frame transmitted ok */ +pub(crate) const FXMAC_IXR_TXEXH_MASK: u32 = BIT(6); /* Transmit err occurred or no buffers*/ +pub(crate) const FXMAC_IXR_RETRY_MASK: u32 = BIT(5); /* Retry limit exceeded */ +pub(crate) const FXMAC_IXR_URUN_MASK: u32 = BIT(4); /* Transmit underrun */ +pub(crate) const FXMAC_IXR_TXUSED_MASK: u32 = BIT(3); /* Tx buffer used bit read */ +pub(crate) const FXMAC_IXR_RXUSED_MASK: u32 = BIT(2); /* Rx buffer used bit read */ +pub(crate) const FXMAC_IXR_RXCOMPL_MASK: u32 = BIT(1); /* Frame received ok */ +pub(crate) const FXMAC_IXR_MGMNT_MASK: u32 = BIT(0); /* PHY management complete */ +pub(crate) const FXMAC_IXR_ALL_MASK: u32 = GENMASK(31, 0); /* Everything! */ + +pub(crate) const FXMAC_IXR_TX_ERR_MASK: u32 = + (FXMAC_IXR_TXEXH_MASK | FXMAC_IXR_RETRY_MASK | FXMAC_IXR_URUN_MASK); + +pub(crate) const FXMAC_IXR_RX_ERR_MASK: u32 = + (FXMAC_IXR_HRESPNOK_MASK | FXMAC_IXR_RXUSED_MASK | FXMAC_IXR_RXOVR_MASK); + +pub(crate) const FXMAC_INTR_MASK: u32 = (FXMAC_IXR_LINKCHANGE_MASK + | FXMAC_IXR_TX_ERR_MASK + | FXMAC_IXR_RX_ERR_MASK + | FXMAC_IXR_RXCOMPL_MASK + | FXMAC_IXR_TXCOMPL_MASK); /** @name network control register bit definitions * @{ */ -pub(crate) const FXMAC_NWCTRL_ENABLE_HS_MAC_MASK:u32 = BIT(31); - -pub(crate) const FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK:u32 = BIT(29); /* 2.5G operation selected */ +pub(crate) const FXMAC_NWCTRL_ENABLE_HS_MAC_MASK: u32 = BIT(31); -pub(crate) const FXMAC_NWCTRL_FLUSH_DPRAM_MASK:u32 = BIT(18); /* Flush a packet from Rx SRAM */ -pub(crate) const FXMAC_NWCTRL_ZEROPAUSETX_MASK:u32 = BIT(11); /* Transmit zero quantum pause frame */ -pub(crate) const FXMAC_NWCTRL_PAUSETX_MASK:u32 = BIT(11); /* Transmit pause frame */ -pub(crate) const FXMAC_NWCTRL_HALTTX_MASK:u32 = BIT(10); /* Halt transmission after current frame */ -pub(crate) const FXMAC_NWCTRL_STARTTX_MASK:u32 = BIT(9); /* Start tx (tx_go) */ - -pub(crate) const FXMAC_NWCTRL_STATWEN_MASK:u32 = BIT(7); /* Enable writing to stat counters */ -pub(crate) const FXMAC_NWCTRL_STATINC_MASK:u32 = BIT(6); /* Increment statistic registers */ -pub(crate) const FXMAC_NWCTRL_STATCLR_MASK:u32 = BIT(5); /* Clear statistic registers */ -pub(crate) const FXMAC_NWCTRL_MDEN_MASK:u32 = BIT(4); /* Enable MDIO port */ -pub(crate) const FXMAC_NWCTRL_TXEN_MASK:u32 = BIT(3); /* Enable transmit */ -pub(crate) const FXMAC_NWCTRL_RXEN_MASK:u32 = BIT(2); /* Enable receive */ -pub(crate) const FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK:u32 = BIT(1); /* Loopback local */ +pub(crate) const FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK: u32 = BIT(29); /* 2.5G operation selected */ +pub(crate) const FXMAC_NWCTRL_FLUSH_DPRAM_MASK: u32 = BIT(18); /* Flush a packet from Rx SRAM */ +pub(crate) const FXMAC_NWCTRL_ZEROPAUSETX_MASK: u32 = BIT(11); /* Transmit zero quantum pause frame */ +pub(crate) const FXMAC_NWCTRL_PAUSETX_MASK: u32 = BIT(11); /* Transmit pause frame */ +pub(crate) const FXMAC_NWCTRL_HALTTX_MASK: u32 = BIT(10); /* Halt transmission after current frame */ +pub(crate) const FXMAC_NWCTRL_STARTTX_MASK: u32 = BIT(9); /* Start tx (tx_go) */ +pub(crate) const FXMAC_NWCTRL_STATWEN_MASK: u32 = BIT(7); /* Enable writing to stat counters */ +pub(crate) const FXMAC_NWCTRL_STATINC_MASK: u32 = BIT(6); /* Increment statistic registers */ +pub(crate) const FXMAC_NWCTRL_STATCLR_MASK: u32 = BIT(5); /* Clear statistic registers */ +pub(crate) const FXMAC_NWCTRL_MDEN_MASK: u32 = BIT(4); /* Enable MDIO port */ +pub(crate) const FXMAC_NWCTRL_TXEN_MASK: u32 = BIT(3); /* Enable transmit */ +pub(crate) const FXMAC_NWCTRL_RXEN_MASK: u32 = BIT(2); /* Enable receive */ +pub(crate) const FXMAC_NWCTRL_LOOPBACK_LOCAL_MASK: u32 = BIT(1); /* Loopback local */ /** @name network configuration register bit definitions FXMAC_NWCFG_OFFSET * @{ */ -pub(crate) const FXMAC_NWCFG_BADPREAMBEN_MASK:u32 = BIT(29); /* disable rejection of non-standard preamble */ -pub(crate) const FXMAC_NWCFG_IPDSTRETCH_MASK:u32 = BIT(28); /* enable transmit IPG */ -pub(crate) const FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK:u32 = BIT(27); /* SGMII mode enable */ -pub(crate) const FXMAC_NWCFG_FCSIGNORE_MASK:u32 = BIT(26); /* disable rejection of FCS error */ -pub(crate) const FXMAC_NWCFG_HDRXEN_MASK:u32 = BIT(25); /* RX half duplex */ -pub(crate) const FXMAC_NWCFG_RXCHKSUMEN_MASK:u32 = BIT(24); /* enable RX checksum offload */ -pub(crate) const FXMAC_NWCFG_PAUSECOPYDI_MASK:u32 = BIT(23); /* Do not copy pause Frames to memory */ - -pub(crate) const FXMAC_NWCFG_DWIDTH_64_MASK:u32 = BIT(21); /* 64 bit Data bus width */ -pub(crate) const FXMAC_NWCFG_BUS_WIDTH_32_MASK:u32 = (0 << 21); -pub(crate) const FXMAC_NWCFG_BUS_WIDTH_64_MASK:u32 = (1 << 21); -pub(crate) const FXMAC_NWCFG_BUS_WIDTH_128_MASK:u32 = (2 << 21); - -pub(crate) const FXMAC_NWCFG_CLOCK_DIV224_MASK:u32 = (7 << 18); -pub(crate) const FXMAC_NWCFG_CLOCK_DIV128_MASK:u32 = (6 << 18); -pub(crate) const FXMAC_NWCFG_CLOCK_DIV96_MASK:u32 = (5 << 18); -pub(crate) const FXMAC_NWCFG_CLOCK_DIV64_MASK:u32 = (4 << 18); -pub(crate) const FXMAC_NWCFG_CLOCK_DIV48_MASK:u32 = (3 << 18); -pub(crate) const FXMAC_NWCFG_CLOCK_DIV32_MASK:u32 = (2 << 18); -pub(crate) const FXMAC_NWCFG_CLOCK_DIV16_MASK:u32 = (1 << 18); -pub(crate) const FXMAC_NWCFG_CLOCK_DIV8_MASK:u32 = (0 << 18); -pub(crate) const FXMAC_NWCFG_RESET_MASK:u32 = BIT(19); /* reset value of mdc_clock_division*/ -pub(crate) const FXMAC_NWCFG_MDC_SHIFT_MASK:u32 = 18; /* shift bits for MDC */ -pub(crate) const FXMAC_NWCFG_MDCCLKDIV_MASK:u32 = GENMASK(20, 18); /* MDC Mask PCLK divisor */ - -pub(crate) const FXMAC_NWCFG_FCS_REMOVE_MASK:u32 = BIT(17); /* FCS remove - setting this bit will cause received frames to be written to memory without their frame check sequence (last 4 bytes). */ -pub(crate) const FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK:u32 = BIT(16); /* RX length error discard */ +pub(crate) const FXMAC_NWCFG_BADPREAMBEN_MASK: u32 = BIT(29); /* disable rejection of non-standard preamble */ +pub(crate) const FXMAC_NWCFG_IPDSTRETCH_MASK: u32 = BIT(28); /* enable transmit IPG */ +pub(crate) const FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK: u32 = BIT(27); /* SGMII mode enable */ +pub(crate) const FXMAC_NWCFG_FCSIGNORE_MASK: u32 = BIT(26); /* disable rejection of FCS error */ +pub(crate) const FXMAC_NWCFG_HDRXEN_MASK: u32 = BIT(25); /* RX half duplex */ +pub(crate) const FXMAC_NWCFG_RXCHKSUMEN_MASK: u32 = BIT(24); /* enable RX checksum offload */ +pub(crate) const FXMAC_NWCFG_PAUSECOPYDI_MASK: u32 = BIT(23); /* Do not copy pause Frames to memory */ + +pub(crate) const FXMAC_NWCFG_DWIDTH_64_MASK: u32 = BIT(21); /* 64 bit Data bus width */ +pub(crate) const FXMAC_NWCFG_BUS_WIDTH_32_MASK: u32 = (0 << 21); +pub(crate) const FXMAC_NWCFG_BUS_WIDTH_64_MASK: u32 = (1 << 21); +pub(crate) const FXMAC_NWCFG_BUS_WIDTH_128_MASK: u32 = (2 << 21); + +pub(crate) const FXMAC_NWCFG_CLOCK_DIV224_MASK: u32 = (7 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV128_MASK: u32 = (6 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV96_MASK: u32 = (5 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV64_MASK: u32 = (4 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV48_MASK: u32 = (3 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV32_MASK: u32 = (2 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV16_MASK: u32 = (1 << 18); +pub(crate) const FXMAC_NWCFG_CLOCK_DIV8_MASK: u32 = (0 << 18); +pub(crate) const FXMAC_NWCFG_RESET_MASK: u32 = BIT(19); /* reset value of mdc_clock_division*/ +pub(crate) const FXMAC_NWCFG_MDC_SHIFT_MASK: u32 = 18; /* shift bits for MDC */ +pub(crate) const FXMAC_NWCFG_MDCCLKDIV_MASK: u32 = GENMASK(20, 18); /* MDC Mask PCLK divisor */ + +pub(crate) const FXMAC_NWCFG_FCS_REMOVE_MASK: u32 = BIT(17); /* FCS remove - setting this bit will cause received frames to be written to memory without their frame check sequence (last 4 bytes). */ +pub(crate) const FXMAC_NWCFG_LENGTH_FIELD_ERROR_FRAME_DISCARD_MASK: u32 = BIT(16); /* RX length error discard */ // FXMAC_NWCFG_RXOFFS_MASK:u32 = GENMASK(15); /* RX buffer offset */ -pub(crate) const FXMAC_NWCFG_PAUSE_ENABLE_MASK:u32 = BIT(13); /* Pause enable - when set, transmission will pause if a non-zero 802.3 classic pause frame is received and PFC has not been negotiated. */ -pub(crate) const FXMAC_NWCFG_RETRYTESTEN_MASK:u32 = BIT(12); /* Retry test */ -pub(crate) const FXMAC_NWCFG_PCSSEL_MASK:u32 = BIT(11); /* PCS Select */ -pub(crate) const FXMAC_NWCFG_1000_MASK:u32 = BIT(10); /* Gigabit mode enable */ -pub(crate) const FXMAC_NWCFG_XTADDMACHEN_MASK:u32 = BIT(9); /* External address match enable */ -pub(crate) const FXMAC_NWCFG_1536RXEN_MASK:u32 = BIT(8); /* Enable 1536 byte frames reception */ -pub(crate) const FXMAC_NWCFG_UCASTHASHEN_MASK:u32 = BIT(7); /* Receive unicast hash frames */ -pub(crate) const FXMAC_NWCFG_MCASTHASHEN_MASK:u32 = BIT(6); /* Receive multicast hash frames */ -pub(crate) const FXMAC_NWCFG_BCASTDI_MASK:u32 = BIT(5); /* Do not receive broadcast frames */ -pub(crate) const FXMAC_NWCFG_COPYALLEN_MASK:u32 = BIT(4); /* Copy all frames */ -pub(crate) const FXMAC_NWCFG_JUMBO_MASK:u32 = BIT(3); /* Jumbo frames */ -pub(crate) const FXMAC_NWCFG_NVLANDISC_MASK:u32 = BIT(2); /* Receive only VLAN frames */ -pub(crate) const FXMAC_NWCFG_FDEN_MASK:u32 = BIT(1); /* full duplex */ -pub(crate) const FXMAC_NWCFG_100_MASK:u32 = BIT(0); /* 100 Mbps */ - +pub(crate) const FXMAC_NWCFG_PAUSE_ENABLE_MASK: u32 = BIT(13); /* Pause enable - when set, transmission will pause if a non-zero 802.3 classic pause frame is received and PFC has not been negotiated. */ +pub(crate) const FXMAC_NWCFG_RETRYTESTEN_MASK: u32 = BIT(12); /* Retry test */ +pub(crate) const FXMAC_NWCFG_PCSSEL_MASK: u32 = BIT(11); /* PCS Select */ +pub(crate) const FXMAC_NWCFG_1000_MASK: u32 = BIT(10); /* Gigabit mode enable */ +pub(crate) const FXMAC_NWCFG_XTADDMACHEN_MASK: u32 = BIT(9); /* External address match enable */ +pub(crate) const FXMAC_NWCFG_1536RXEN_MASK: u32 = BIT(8); /* Enable 1536 byte frames reception */ +pub(crate) const FXMAC_NWCFG_UCASTHASHEN_MASK: u32 = BIT(7); /* Receive unicast hash frames */ +pub(crate) const FXMAC_NWCFG_MCASTHASHEN_MASK: u32 = BIT(6); /* Receive multicast hash frames */ +pub(crate) const FXMAC_NWCFG_BCASTDI_MASK: u32 = BIT(5); /* Do not receive broadcast frames */ +pub(crate) const FXMAC_NWCFG_COPYALLEN_MASK: u32 = BIT(4); /* Copy all frames */ +pub(crate) const FXMAC_NWCFG_JUMBO_MASK: u32 = BIT(3); /* Jumbo frames */ +pub(crate) const FXMAC_NWCFG_NVLANDISC_MASK: u32 = BIT(2); /* Receive only VLAN frames */ +pub(crate) const FXMAC_NWCFG_FDEN_MASK: u32 = BIT(1); /* full duplex */ +pub(crate) const FXMAC_NWCFG_100_MASK: u32 = BIT(0); /* 100 Mbps */ /* Receive buffer descriptor status words bit positions. * Receive buffer descriptor consists of two 32-bit registers, @@ -317,28 +322,28 @@ pub(crate) const FXMAC_NWCFG_100_MASK:u32 = BIT(0); /* 100 Mbps */ * useful info. * @{ */ -pub(crate) const FXMAC_RXBUF_BCAST_MASK:u32 = BIT(31); /* Broadcast frame */ -pub(crate) const FXMAC_RXBUF_HASH_MASK:u32 = GENMASK(30, 29); -pub(crate) const FXMAC_RXBUF_MULTIHASH_MASK:u32 = BIT(30); /* Multicast hashed frame */ -pub(crate) const FXMAC_RXBUF_UNIHASH_MASK:u32 = BIT(29); /* Unicast hashed frame */ -pub(crate) const FXMAC_RXBUF_EXH_MASK:u32 = BIT(27); /* buffer exhausted */ -pub(crate) const FXMAC_RXBUF_AMATCH_MASK:u32 = GENMASK(26, 25); /* Specific address \ -matched */ -pub(crate) const FXMAC_RXBUF_IDFOUND_MASK:u32 = BIT(24); /* Type ID matched */ -pub(crate) const FXMAC_RXBUF_IDMATCH_MASK:u32 = GENMASK(23, 22); /* ID matched mask */ -pub(crate) const FXMAC_RXBUF_VLAN_MASK:u32 = BIT(21); /* VLAN tagged */ -pub(crate) const FXMAC_RXBUF_PRI_MASK:u32 = BIT(20); /* Priority tagged */ -pub(crate) const FXMAC_RXBUF_VPRI_MASK:u32 = GENMASK(19, 17); /* Vlan priority */ -pub(crate) const FXMAC_RXBUF_CFI_MASK:u32 = BIT(16); /* CFI frame */ -pub(crate) const FXMAC_RXBUF_EOF_MASK:u32 = BIT(15); /* End of frame. */ -pub(crate) const FXMAC_RXBUF_SOF_MASK:u32 = BIT(14); /* Start of frame. */ -pub(crate) const FXMAC_RXBUF_FCS_STATUS_MASK:u32 = BIT(13); /* Status of fcs. */ -pub(crate) const FXMAC_RXBUF_LEN_MASK:u32 = GENMASK(12, 0); /* Mask for length field */ -pub(crate) const FXMAC_RXBUF_LEN_JUMBO_MASK:u32 = GENMASK(13, 0); /* Mask for jumbo length */ - -pub(crate) const FXMAC_RXBUF_WRAP_MASK:u32 = BIT(1); /* Wrap bit, last BD */ -pub(crate) const FXMAC_RXBUF_NEW_MASK:u32 = BIT(0); /* Used bit.. */ -pub(crate) const FXMAC_RXBUF_ADD_MASK:u32 = GENMASK(31, 2); /* Mask for address */ +pub(crate) const FXMAC_RXBUF_BCAST_MASK: u32 = BIT(31); /* Broadcast frame */ +pub(crate) const FXMAC_RXBUF_HASH_MASK: u32 = GENMASK(30, 29); +pub(crate) const FXMAC_RXBUF_MULTIHASH_MASK: u32 = BIT(30); /* Multicast hashed frame */ +pub(crate) const FXMAC_RXBUF_UNIHASH_MASK: u32 = BIT(29); /* Unicast hashed frame */ +pub(crate) const FXMAC_RXBUF_EXH_MASK: u32 = BIT(27); /* buffer exhausted */ +pub(crate) const FXMAC_RXBUF_AMATCH_MASK: u32 = GENMASK(26, 25); /* Specific address \ + matched */ +pub(crate) const FXMAC_RXBUF_IDFOUND_MASK: u32 = BIT(24); /* Type ID matched */ +pub(crate) const FXMAC_RXBUF_IDMATCH_MASK: u32 = GENMASK(23, 22); /* ID matched mask */ +pub(crate) const FXMAC_RXBUF_VLAN_MASK: u32 = BIT(21); /* VLAN tagged */ +pub(crate) const FXMAC_RXBUF_PRI_MASK: u32 = BIT(20); /* Priority tagged */ +pub(crate) const FXMAC_RXBUF_VPRI_MASK: u32 = GENMASK(19, 17); /* Vlan priority */ +pub(crate) const FXMAC_RXBUF_CFI_MASK: u32 = BIT(16); /* CFI frame */ +pub(crate) const FXMAC_RXBUF_EOF_MASK: u32 = BIT(15); /* End of frame. */ +pub(crate) const FXMAC_RXBUF_SOF_MASK: u32 = BIT(14); /* Start of frame. */ +pub(crate) const FXMAC_RXBUF_FCS_STATUS_MASK: u32 = BIT(13); /* Status of fcs. */ +pub(crate) const FXMAC_RXBUF_LEN_MASK: u32 = GENMASK(12, 0); /* Mask for length field */ +pub(crate) const FXMAC_RXBUF_LEN_JUMBO_MASK: u32 = GENMASK(13, 0); /* Mask for jumbo length */ + +pub(crate) const FXMAC_RXBUF_WRAP_MASK: u32 = BIT(1); /* Wrap bit, last BD */ +pub(crate) const FXMAC_RXBUF_NEW_MASK: u32 = BIT(0); /* Used bit.. */ +pub(crate) const FXMAC_RXBUF_ADD_MASK: u32 = GENMASK(31, 2); /* Mask for address */ /* * @} @@ -353,119 +358,121 @@ pub(crate) const FXMAC_RXBUF_ADD_MASK:u32 = GENMASK(31, 2); /* Mask for address * information, whether the frame was transmitted OK or why it had failed. * @{ */ -pub(crate) const FXMAC_TXBUF_USED_MASK:u32 = BIT(31); /* Used bit. */ -pub(crate) const FXMAC_TXBUF_WRAP_MASK:u32 = BIT(30); /* Wrap bit, last descriptor */ -pub(crate) const FXMAC_TXBUF_RETRY_MASK:u32 = BIT(29); /* Retry limit exceeded */ -pub(crate) const FXMAC_TXBUF_URUN_MASK:u32 = BIT(28); /* Transmit underrun occurred */ -pub(crate) const FXMAC_TXBUF_EXH_MASK:u32 = BIT(27); /* Buffers exhausted */ -pub(crate) const FXMAC_TXBUF_TCP_MASK:u32 = BIT(26); /* Late collision. */ -pub(crate) const FXMAC_TXBUF_NOCRC_MASK:u32 = BIT(16); /* No CRC */ -pub(crate) const FXMAC_TXBUF_LAST_MASK:u32 = BIT(15); /* Last buffer */ -pub(crate) const FXMAC_TXBUF_LEN_MASK:u32 = GENMASK(13, 0); /* Mask for length field */ +pub(crate) const FXMAC_TXBUF_USED_MASK: u32 = BIT(31); /* Used bit. */ +pub(crate) const FXMAC_TXBUF_WRAP_MASK: u32 = BIT(30); /* Wrap bit, last descriptor */ +pub(crate) const FXMAC_TXBUF_RETRY_MASK: u32 = BIT(29); /* Retry limit exceeded */ +pub(crate) const FXMAC_TXBUF_URUN_MASK: u32 = BIT(28); /* Transmit underrun occurred */ +pub(crate) const FXMAC_TXBUF_EXH_MASK: u32 = BIT(27); /* Buffers exhausted */ +pub(crate) const FXMAC_TXBUF_TCP_MASK: u32 = BIT(26); /* Late collision. */ +pub(crate) const FXMAC_TXBUF_NOCRC_MASK: u32 = BIT(16); /* No CRC */ +pub(crate) const FXMAC_TXBUF_LAST_MASK: u32 = BIT(15); /* Last buffer */ +pub(crate) const FXMAC_TXBUF_LEN_MASK: u32 = GENMASK(13, 0); /* Mask for length field */ /* * @} */ - /** * @name receive status register bit definitions * @{ */ -pub(crate) const FXMAC_RXSR_HRESPNOK_MASK:u32 = BIT(3); /* Receive hresp not OK */ -pub(crate) const FXMAC_RXSR_RXOVR_MASK:u32 = BIT(2); /* Receive overrun */ -pub(crate) const FXMAC_RXSR_FRAMERX_MASK:u32 = BIT(1); /* Frame received OK */ -pub(crate) const FXMAC_RXSR_BUFFNA_MASK:u32 = BIT(0); /* RX buffer used bit set */ +pub(crate) const FXMAC_RXSR_HRESPNOK_MASK: u32 = BIT(3); /* Receive hresp not OK */ +pub(crate) const FXMAC_RXSR_RXOVR_MASK: u32 = BIT(2); /* Receive overrun */ +pub(crate) const FXMAC_RXSR_FRAMERX_MASK: u32 = BIT(1); /* Frame received OK */ +pub(crate) const FXMAC_RXSR_BUFFNA_MASK: u32 = BIT(0); /* RX buffer used bit set */ -pub(crate) const FXMAC_RXSR_ERROR_MASK:u32 = (FXMAC_RXSR_HRESPNOK_MASK | FXMAC_RXSR_RXOVR_MASK | FXMAC_RXSR_BUFFNA_MASK); +pub(crate) const FXMAC_RXSR_ERROR_MASK: u32 = + (FXMAC_RXSR_HRESPNOK_MASK | FXMAC_RXSR_RXOVR_MASK | FXMAC_RXSR_BUFFNA_MASK); -pub(crate) const FXMAC_SR_ALL_MASK:u32 = GENMASK(31, 0); /* Mask for full register */ +pub(crate) const FXMAC_SR_ALL_MASK: u32 = GENMASK(31, 0); /* Mask for full register */ /** @name DMA control register bit definitions * @{ */ -pub(crate) const FXMAC_DMACR_ADDR_WIDTH_64:u32 = BIT(30); /* 64 bit address bus */ -pub(crate) const FXMAC_DMACR_TXEXTEND_MASK:u32 = BIT(29); /* Tx Extended desc mode */ -pub(crate) const FXMAC_DMACR_RXEXTEND_MASK:u32 = BIT(28); /* Rx Extended desc mode */ -pub(crate) const FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK:u32 = BIT(24); /* Auto Discard RX frames during lack of resource. */ -pub(crate) const FXMAC_DMACR_RXBUF_MASK:u32 = GENMASK(23, 16); /* Mask bit for RX buffer size */ -pub(crate) const FXMAC_DMACR_RXBUF_SHIFT:u32 = 16; /* Shift bit for RX buffer size */ -pub(crate) const FXMAC_DMACR_TCPCKSUM_MASK:u32 = BIT(11); /* enable/disable TX checksum offload */ -pub(crate) const FXMAC_DMACR_TXSIZE_MASK:u32 = BIT(10); /* TX buffer memory size bit[10] */ -pub(crate) const FXMAC_DMACR_RXSIZE_MASK:u32 = GENMASK(9, 8); /* RX buffer memory size bit[9:8] */ -pub(crate) const FXMAC_DMACR_ENDIAN_MASK:u32 = BIT(7); /* endian configuration */ -pub(crate) const FXMAC_DMACR_SWAP_MANAGEMENT_MASK:u32 = BIT(6); /* When clear, selects little endian mode */ -pub(crate) const FXMAC_DMACR_BLENGTH_MASK:u32 = GENMASK(4, 0); /* buffer burst length */ -pub(crate) const FXMAC_DMACR_SINGLE_AHB_AXI_BURST:u32 = BIT(0); /* single AHB_AXI bursts */ -pub(crate) const FXMAC_DMACR_INCR4_AHB_AXI_BURST:u32 = BIT(2); /* 4 bytes AHB_AXI bursts */ -pub(crate) const FXMAC_DMACR_INCR8_AHB_AXI_BURST:u32 = BIT(3); /* 8 bytes AHB_AXI bursts */ -pub(crate) const FXMAC_DMACR_INCR16_AHB_AXI_BURST:u32 = BIT(4); /* 16 bytes AHB_AXI bursts */ +pub(crate) const FXMAC_DMACR_ADDR_WIDTH_64: u32 = BIT(30); /* 64 bit address bus */ +pub(crate) const FXMAC_DMACR_TXEXTEND_MASK: u32 = BIT(29); /* Tx Extended desc mode */ +pub(crate) const FXMAC_DMACR_RXEXTEND_MASK: u32 = BIT(28); /* Rx Extended desc mode */ +pub(crate) const FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK: u32 = BIT(24); /* Auto Discard RX frames during lack of resource. */ +pub(crate) const FXMAC_DMACR_RXBUF_MASK: u32 = GENMASK(23, 16); /* Mask bit for RX buffer size */ +pub(crate) const FXMAC_DMACR_RXBUF_SHIFT: u32 = 16; /* Shift bit for RX buffer size */ +pub(crate) const FXMAC_DMACR_TCPCKSUM_MASK: u32 = BIT(11); /* enable/disable TX checksum offload */ +pub(crate) const FXMAC_DMACR_TXSIZE_MASK: u32 = BIT(10); /* TX buffer memory size bit[10] */ +pub(crate) const FXMAC_DMACR_RXSIZE_MASK: u32 = GENMASK(9, 8); /* RX buffer memory size bit[9:8] */ +pub(crate) const FXMAC_DMACR_ENDIAN_MASK: u32 = BIT(7); /* endian configuration */ +pub(crate) const FXMAC_DMACR_SWAP_MANAGEMENT_MASK: u32 = BIT(6); /* When clear, selects little endian mode */ +pub(crate) const FXMAC_DMACR_BLENGTH_MASK: u32 = GENMASK(4, 0); /* buffer burst length */ +pub(crate) const FXMAC_DMACR_SINGLE_AHB_AXI_BURST: u32 = BIT(0); /* single AHB_AXI bursts */ +pub(crate) const FXMAC_DMACR_INCR4_AHB_AXI_BURST: u32 = BIT(2); /* 4 bytes AHB_AXI bursts */ +pub(crate) const FXMAC_DMACR_INCR8_AHB_AXI_BURST: u32 = BIT(3); /* 8 bytes AHB_AXI bursts */ +pub(crate) const FXMAC_DMACR_INCR16_AHB_AXI_BURST: u32 = BIT(4); /* 16 bytes AHB_AXI bursts */ /* This register indicates module identification number and module revision. */ -pub(crate) const FXMAC_REVISION_MODULE_MASK:u32 = GENMASK(15, 0); /* Module revision */ -pub(crate) const FXMAC_IDENTIFICATION_MASK:u32 = GENMASK(27, 16); /* Module identification number */ -pub(crate) const FXMAC_FIX_NUM_MASK:u32 = GENMASK(31, 28); /* Fix number - incremented for fix releases */ +pub(crate) const FXMAC_REVISION_MODULE_MASK: u32 = GENMASK(15, 0); /* Module revision */ +pub(crate) const FXMAC_IDENTIFICATION_MASK: u32 = GENMASK(27, 16); /* Module identification number */ +pub(crate) const FXMAC_FIX_NUM_MASK: u32 = GENMASK(31, 28); /* Fix number - incremented for fix releases */ /** @name network status register bit definitaions * @{ */ -pub(crate) const FXMAC_NWSR_MDIOIDLE_MASK:u32 = BIT(2); /* PHY management idle */ -pub(crate) const FXMAC_NWSR_MDIO_MASK:u32 = BIT(1); /* Status of mdio_in */ -pub(crate) const FXMAC_NWSR_PCS_LINK_STATE_MASK:u32 = BIT(0); +pub(crate) const FXMAC_NWSR_MDIOIDLE_MASK: u32 = BIT(2); /* PHY management idle */ +pub(crate) const FXMAC_NWSR_MDIO_MASK: u32 = BIT(1); /* Status of mdio_in */ +pub(crate) const FXMAC_NWSR_PCS_LINK_STATE_MASK: u32 = BIT(0); /** @name PHY Maintenance bit definitions * @{ */ -pub(crate) const FXMAC_PHYMNTNC_OP_MASK:u32 = (BIT(17) | BIT(30)); /* operation mask bits */ -pub(crate) const FXMAC_PHYMNTNC_OP_R_MASK:u32 = BIT(29); /* read operation */ -pub(crate) const FXMAC_PHYMNTNC_OP_W_MASK:u32 = BIT(28); /* write operation */ -pub(crate) const FXMAC_PHYMNTNC_ADDR_MASK:u32 = GENMASK(27, 23); /* Address bits */ -pub(crate) const FXMAC_PHYMNTNC_REG_MASK:u32 = GENMASK(22, 18); /* register bits */ -pub(crate) const FXMAC_PHYMNTNC_DATA_MASK:u32 = GENMASK(11, 0); /* data bits */ -pub(crate) const FXMAC_PHYMNTNC_PHAD_SHFT_MSK:u32 = 23; /* Shift bits for PHYAD */ -pub(crate) const FXMAC_PHYMNTNC_PREG_SHFT_MSK:u32 = 18; /* Shift bits for PHREG */ +pub(crate) const FXMAC_PHYMNTNC_OP_MASK: u32 = (BIT(17) | BIT(30)); /* operation mask bits */ +pub(crate) const FXMAC_PHYMNTNC_OP_R_MASK: u32 = BIT(29); /* read operation */ +pub(crate) const FXMAC_PHYMNTNC_OP_W_MASK: u32 = BIT(28); /* write operation */ +pub(crate) const FXMAC_PHYMNTNC_ADDR_MASK: u32 = GENMASK(27, 23); /* Address bits */ +pub(crate) const FXMAC_PHYMNTNC_REG_MASK: u32 = GENMASK(22, 18); /* register bits */ +pub(crate) const FXMAC_PHYMNTNC_DATA_MASK: u32 = GENMASK(11, 0); /* data bits */ +pub(crate) const FXMAC_PHYMNTNC_PHAD_SHFT_MSK: u32 = 23; /* Shift bits for PHYAD */ +pub(crate) const FXMAC_PHYMNTNC_PREG_SHFT_MSK: u32 = 18; /* Shift bits for PHREG */ /** @name transmit status register bit definitions * @{ */ -pub(crate) const FXMAC_TXSR_HRESPNOK_MASK:u32 = BIT(8); /* Transmit hresp not OK */ -pub(crate) const FXMAC_TXSR_URUN_MASK:u32 = BIT(6); /* Transmit underrun */ -pub(crate) const FXMAC_TXSR_TXCOMPL_MASK:u32 = BIT(5); /* Transmit completed OK */ -pub(crate) const FXMAC_TXSR_BUFEXH_MASK:u32 = BIT(4); /* Transmit buffs exhausted mid frame */ -pub(crate) const FXMAC_TXSR_TXGO_MASK:u32 = BIT(3); /* Status of go flag */ -pub(crate) const FXMAC_TXSR_RXOVR_MASK:u32 = BIT(2); /* Retry limit exceeded */ -pub(crate) const FXMAC_TXSR_FRAMERX_MASK:u32 = BIT(1); /* Collision tx frame */ -pub(crate) const FXMAC_TXSR_USEDREAD_MASK:u32 = BIT(0); /* TX buffer used bit set */ - -pub(crate) const FXMAC_TXSR_ERROR_MASK:u32 = (FXMAC_TXSR_HRESPNOK_MASK | - FXMAC_TXSR_URUN_MASK | - FXMAC_TXSR_BUFEXH_MASK | - FXMAC_TXSR_RXOVR_MASK | - FXMAC_TXSR_FRAMERX_MASK | - FXMAC_TXSR_USEDREAD_MASK); +pub(crate) const FXMAC_TXSR_HRESPNOK_MASK: u32 = BIT(8); /* Transmit hresp not OK */ +pub(crate) const FXMAC_TXSR_URUN_MASK: u32 = BIT(6); /* Transmit underrun */ +pub(crate) const FXMAC_TXSR_TXCOMPL_MASK: u32 = BIT(5); /* Transmit completed OK */ +pub(crate) const FXMAC_TXSR_BUFEXH_MASK: u32 = BIT(4); /* Transmit buffs exhausted mid frame */ +pub(crate) const FXMAC_TXSR_TXGO_MASK: u32 = BIT(3); /* Status of go flag */ +pub(crate) const FXMAC_TXSR_RXOVR_MASK: u32 = BIT(2); /* Retry limit exceeded */ +pub(crate) const FXMAC_TXSR_FRAMERX_MASK: u32 = BIT(1); /* Collision tx frame */ +pub(crate) const FXMAC_TXSR_USEDREAD_MASK: u32 = BIT(0); /* TX buffer used bit set */ + +pub(crate) const FXMAC_TXSR_ERROR_MASK: u32 = (FXMAC_TXSR_HRESPNOK_MASK + | FXMAC_TXSR_URUN_MASK + | FXMAC_TXSR_BUFEXH_MASK + | FXMAC_TXSR_RXOVR_MASK + | FXMAC_TXSR_FRAMERX_MASK + | FXMAC_TXSR_USEDREAD_MASK); /** @name transmit SRAM segment allocation by queue 0 to 7 register bit definitions * @{ - */ -pub(crate) const FXMAC_TXQSEGALLOC_QLOWER_JUMBO_MASK:u32 = BIT(2); /* 16 segments are distributed to queue 0*/ + */ +pub(crate) const FXMAC_TXQSEGALLOC_QLOWER_JUMBO_MASK: u32 = BIT(2); /* 16 segments are distributed to queue 0*/ /** * @name Interrupt Q1 status register bit definitions * @{ */ -pub(crate) const FXMAC_INTQ1SR_TXCOMPL_MASK:u32 = BIT(7); /* Transmit completed OK */ -pub(crate) const FXMAC_INTQ1SR_TXERR_MASK:u32 = BIT(6); /* Transmit AMBA Error */ +pub(crate) const FXMAC_INTQ1SR_TXCOMPL_MASK: u32 = BIT(7); /* Transmit completed OK */ +pub(crate) const FXMAC_INTQ1SR_TXERR_MASK: u32 = BIT(6); /* Transmit AMBA Error */ -pub(crate) const FXMAC_INTQ1_IXR_ALL_MASK:u32 = (FXMAC_INTQ1SR_TXCOMPL_MASK | FXMAC_INTQ1SR_TXERR_MASK); +pub(crate) const FXMAC_INTQ1_IXR_ALL_MASK: u32 = + (FXMAC_INTQ1SR_TXCOMPL_MASK | FXMAC_INTQ1SR_TXERR_MASK); /** * @name Interrupt QUEUE status register bit definitions * @{ */ -pub(crate) const FXMAC_INTQUESR_TXCOMPL_MASK:u32 = BIT(7); /* Transmit completed OK */ -pub(crate) const FXMAC_INTQUESR_TXERR_MASK:u32 = BIT(6); /* Transmit AMBA Error */ -pub(crate) const FXMAC_INTQUESR_RCOMP_MASK:u32 = BIT(1); -pub(crate) const FXMAC_INTQUESR_RXUBR_MASK:u32 = BIT(2); +pub(crate) const FXMAC_INTQUESR_TXCOMPL_MASK: u32 = BIT(7); /* Transmit completed OK */ +pub(crate) const FXMAC_INTQUESR_TXERR_MASK: u32 = BIT(6); /* Transmit AMBA Error */ +pub(crate) const FXMAC_INTQUESR_RCOMP_MASK: u32 = BIT(1); +pub(crate) const FXMAC_INTQUESR_RXUBR_MASK: u32 = BIT(2); -pub(crate) const FXMAC_INTQUE_IXR_ALL_MASK:u32 = (FXMAC_INTQUESR_TXCOMPL_MASK | FXMAC_INTQUESR_TXERR_MASK); +pub(crate) const FXMAC_INTQUE_IXR_ALL_MASK: u32 = + (FXMAC_INTQUESR_TXCOMPL_MASK | FXMAC_INTQUESR_TXERR_MASK); pub const fn FXMAC_QUEUE_REGISTER_OFFSET(base_addr: u64, queue_id: u32) -> u64 { base_addr + (queue_id as u64 - 1) * 4 @@ -473,59 +480,57 @@ pub const fn FXMAC_QUEUE_REGISTER_OFFSET(base_addr: u64, queue_id: u32) -> u64 { /* Design Configuration Register 1 - The GEM has many parameterisation options to configure the IP during compilation stage. */ -pub(crate) const FXMAC_DESIGNCFG_DEBUG1_BUS_WIDTH_MASK:u32 = GENMASK(27, 25); -pub(crate) const FXMAC_DESIGNCFG_DEBUG1_BUS_IRQCOR_MASK:u32 = BIT(23); +pub(crate) const FXMAC_DESIGNCFG_DEBUG1_BUS_WIDTH_MASK: u32 = GENMASK(27, 25); +pub(crate) const FXMAC_DESIGNCFG_DEBUG1_BUS_IRQCOR_MASK: u32 = BIT(23); /*GEM hs mac config register bitfields*/ -pub(crate) const FXMAC_GEM_HSMACSPEED_OFFSET:u64 = 0; -pub(crate) const FXMAC_GEM_HSMACSPEED_SIZE:u32 = 3; -pub(crate) const FXMAC_GEM_HSMACSPEED_MASK:u32 = 0x7; +pub(crate) const FXMAC_GEM_HSMACSPEED_OFFSET: u64 = 0; +pub(crate) const FXMAC_GEM_HSMACSPEED_SIZE: u32 = 3; +pub(crate) const FXMAC_GEM_HSMACSPEED_MASK: u32 = 0x7; /* Transmit buffer descriptor status words offset * @{ */ -pub(crate) const FXMAC_BD_ADDR_OFFSET:u64 = 0x00000000;/* word 0/addr of BDs */ -pub(crate) const FXMAC_BD_STAT_OFFSET:u64 = 4; /* word 1/status of BDs, 4 bytes */ -pub(crate) const FXMAC_BD_ADDR_HI_OFFSET:u32 = BIT(3); /* word 2/addr of BDs */ +pub(crate) const FXMAC_BD_ADDR_OFFSET: u64 = 0x00000000; /* word 0/addr of BDs */ +pub(crate) const FXMAC_BD_STAT_OFFSET: u64 = 4; /* word 1/status of BDs, 4 bytes */ +pub(crate) const FXMAC_BD_ADDR_HI_OFFSET: u32 = BIT(3); /* word 2/addr of BDs */ /** @name MAC address register word 1 mask * @{ */ -pub(crate) const FXMAC_GEM_SAB_MASK:u32 = GENMASK(15, 0); /* Address bits[47:32] bit[31:0] are in BOTTOM */ +pub(crate) const FXMAC_GEM_SAB_MASK: u32 = GENMASK(15, 0); /* Address bits[47:32] bit[31:0] are in BOTTOM */ /* USXGMII control register FXMAC_GEM_USX_CONTROL_OFFSET */ -pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_100M:u32 = (0x0 << 14); /* 100M operation */ -pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_1G:u32 = (0x1 << 14); /* 1G operation */ -pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_2_5G:u32 = (0x2 << 14); /* 2.5G operation */ -pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_5G:u32 = (0x3 << 14); /* 5G operation */ -pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_10G:u32 = (0x4 << 14); /* 10G operation */ -pub(crate) const FXMAC_GEM_USX_SERDES_RATE_5G:u32 = (0x0 << 12); -pub(crate) const FXMAC_GEM_USX_SERDES_RATE_10G:u32 = (0x1 << 12); -pub(crate) const FXMAC_GEM_USX_TX_SCR_BYPASS:u32 = BIT(8); /* RX Scrambler Bypass. Set high to bypass the receive descrambler. */ -pub(crate) const FXMAC_GEM_USX_RX_SCR_BYPASS:u32 = BIT(9); /* TX Scrambler Bypass. Set high to bypass the transmit scrambler. */ -pub(crate) const FXMAC_GEM_USX_RX_SYNC_RESET:u32 = BIT(2); /* RX Reset. Set high to reset the receive datapath. When low the receive datapath is enabled. */ -pub(crate) const FXMAC_GEM_USX_TX_DATAPATH_EN:u32 = BIT(1); /* TX Datapath Enable. */ -pub(crate) const FXMAC_GEM_USX_SIGNAL_OK:u32 = BIT(0); /* Enable the USXGMII/BASE-R receive PCS. */ +pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_100M: u32 = (0x0 << 14); /* 100M operation */ +pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_1G: u32 = (0x1 << 14); /* 1G operation */ +pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_2_5G: u32 = (0x2 << 14); /* 2.5G operation */ +pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_5G: u32 = (0x3 << 14); /* 5G operation */ +pub(crate) const FXMAC_GEM_USX_HS_MAC_SPEED_10G: u32 = (0x4 << 14); /* 10G operation */ +pub(crate) const FXMAC_GEM_USX_SERDES_RATE_5G: u32 = (0x0 << 12); +pub(crate) const FXMAC_GEM_USX_SERDES_RATE_10G: u32 = (0x1 << 12); +pub(crate) const FXMAC_GEM_USX_TX_SCR_BYPASS: u32 = BIT(8); /* RX Scrambler Bypass. Set high to bypass the receive descrambler. */ +pub(crate) const FXMAC_GEM_USX_RX_SCR_BYPASS: u32 = BIT(9); /* TX Scrambler Bypass. Set high to bypass the transmit scrambler. */ +pub(crate) const FXMAC_GEM_USX_RX_SYNC_RESET: u32 = BIT(2); /* RX Reset. Set high to reset the receive datapath. When low the receive datapath is enabled. */ +pub(crate) const FXMAC_GEM_USX_TX_DATAPATH_EN: u32 = BIT(1); /* TX Datapath Enable. */ +pub(crate) const FXMAC_GEM_USX_SIGNAL_OK: u32 = BIT(0); /* Enable the USXGMII/BASE-R receive PCS. */ /* All PCS registers */ -pub(crate) const FXMAC_PCS_CONTROL_ENABLE_AUTO_NEG:u32 = BIT(12); /* Enable auto-negotiation - when set active high, autonegotiation operation is enabled. */ - +pub(crate) const FXMAC_PCS_CONTROL_ENABLE_AUTO_NEG: u32 = BIT(12); /* Enable auto-negotiation - when set active high, autonegotiation operation is enabled. */ /* FXMAC_PCS_STATUS_OFFSET */ -pub(crate) const FXMAC_PCS_STATUS_LINK_STATUS_OFFSET:u32 = 2; -pub(crate) const FXMAC_PCS_STATUS_LINK_STATUS:u32 = BIT(FXMAC_PCS_STATUS_LINK_STATUS_OFFSET); /* Link status - indicates the status of the physical connection to the link partner. When set to logic 1 the link is up, and when set to logic 0, the link is down. */ +pub(crate) const FXMAC_PCS_STATUS_LINK_STATUS_OFFSET: u32 = 2; +pub(crate) const FXMAC_PCS_STATUS_LINK_STATUS: u32 = BIT(FXMAC_PCS_STATUS_LINK_STATUS_OFFSET); /* Link status - indicates the status of the physical connection to the link partner. When set to logic 1 the link is up, and when set to logic 0, the link is down. */ /* FXMAC_PCS_AN_LP_OFFSET */ -pub(crate) const FXMAC_PCS_AN_LP_SPEED_OFFSET:u64 = 10; -pub(crate) const FXMAC_PCS_AN_LP_SPEED:u32 = (0x3 << FXMAC_PCS_AN_LP_SPEED_OFFSET); /* SGMII 11 : Reserved 10 : 1000 Mbps 01 : 100Mbps 00 : 10 Mbps */ -pub(crate) const FXMAC_PCS_AN_LP_DUPLEX_OFFSET:u64 = 12; -pub(crate) const FXMAC_PCS_AN_LP_DUPLEX:u32 = (0x3 << FXMAC_PCS_AN_LP_DUPLEX_OFFSET); /* SGMII Bit 13: Reserved. read as 0. Bit 12 : 0 : half-duplex. 1: Full Duplex." */ -pub(crate) const FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_STATUS:u32 = (1 << 15); /* In sgmii mode, 0 is link down . 1 is link up */ -pub(crate) const FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_OFFSET:u64 = 15; - +pub(crate) const FXMAC_PCS_AN_LP_SPEED_OFFSET: u64 = 10; +pub(crate) const FXMAC_PCS_AN_LP_SPEED: u32 = (0x3 << FXMAC_PCS_AN_LP_SPEED_OFFSET); /* SGMII 11 : Reserved 10 : 1000 Mbps 01 : 100Mbps 00 : 10 Mbps */ +pub(crate) const FXMAC_PCS_AN_LP_DUPLEX_OFFSET: u64 = 12; +pub(crate) const FXMAC_PCS_AN_LP_DUPLEX: u32 = (0x3 << FXMAC_PCS_AN_LP_DUPLEX_OFFSET); /* SGMII Bit 13: Reserved. read as 0. Bit 12 : 0 : half-duplex. 1: Full Duplex." */ +pub(crate) const FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_STATUS: u32 = (1 << 15); /* In sgmii mode, 0 is link down . 1 is link up */ +pub(crate) const FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_OFFSET: u64 = 15; /* USXGMII Status Register */ -pub(crate) const FXMAC_GEM_USX_STATUS_BLOCK_LOCK:u32 = BIT(0); /* Block Lock. A value of one indicates that the PCS has achieved block synchronization. */ +pub(crate) const FXMAC_GEM_USX_STATUS_BLOCK_LOCK: u32 = BIT(0); /* Block Lock. A value of one indicates that the PCS has achieved block synchronization. */ // aarch64 pub(crate) const BITS_PER_LONG: u32 = 64; @@ -533,42 +538,39 @@ pub const fn BIT(n: u32) -> u32 { 1 << n } -pub const fn GENMASK(h:u32, l: u32) -> u32 { - ( - (!(0 as u64) - (1 << l) + 1) & - (!(0 as u64) >> (BITS_PER_LONG - 1 - h)) - ) as u32 +pub const fn GENMASK(h: u32, l: u32) -> u32 { + ((!0_u64 - (1 << l) + 1) & (!0_u64 >> (BITS_PER_LONG - 1 - h))) as u32 } //////////////////// // fxmac.h -pub(crate) const FXMAC_PROMISC_OPTION:u32 = 0x00000001; +pub(crate) const FXMAC_PROMISC_OPTION: u32 = 0x00000001; /* Accept all incoming packets. * This option defaults to disabled (cleared) */ -pub(crate) const FXMAC_FRAME1536_OPTION:u32 = 0x00000002; +pub(crate) const FXMAC_FRAME1536_OPTION: u32 = 0x00000002; /* Frame larger than 1516 support for Tx & Rx.x * This option defaults to disabled (cleared) */ -pub(crate) const FXMAC_VLAN_OPTION:u32 = 0x00000004; +pub(crate) const FXMAC_VLAN_OPTION: u32 = 0x00000004; /* VLAN Rx & Tx frame support. * This option defaults to disabled (cleared) */ -pub(crate) const FXMAC_FLOW_CONTROL_OPTION:u32 = 0x00000010; +pub(crate) const FXMAC_FLOW_CONTROL_OPTION: u32 = 0x00000010; /* Enable recognition of flow control frames on Rx * This option defaults to enabled (set) */ -pub(crate) const FXMAC_FCS_STRIP_OPTION:u32 = 0x00000020; +pub(crate) const FXMAC_FCS_STRIP_OPTION: u32 = 0x00000020; /* Strip FCS and PAD from incoming frames. Note: PAD from VLAN frames is not * stripped. * This option defaults to enabled (set) */ -pub(crate) const FXMAC_FCS_INSERT_OPTION:u32 = 0x00000040; +pub(crate) const FXMAC_FCS_INSERT_OPTION: u32 = 0x00000040; /* Generate FCS field and add PAD automatically for outgoing frames. * This option defaults to disabled (cleared) */ -pub(crate) const FXMAC_LENTYPE_ERR_OPTION:u32 = 0x00000080; +pub(crate) const FXMAC_LENTYPE_ERR_OPTION: u32 = 0x00000080; /* Enable Length/Type error checking for incoming frames. When this option is * set, the MAC will filter frames that have a mismatched type/length field * and if FXMAC_REPORT_RXERR_OPTION is set, the user is notified when these @@ -577,95 +579,96 @@ pub(crate) const FXMAC_LENTYPE_ERR_OPTION:u32 = 0x00000080; * * This option defaults to disabled (cleared) */ -pub(crate) const FXMAC_TRANSMITTER_ENABLE_OPTION:u32 = 0x00000100; +pub(crate) const FXMAC_TRANSMITTER_ENABLE_OPTION: u32 = 0x00000100; /* Enable the transmitter. * This option defaults to enabled (set) */ -pub(crate) const FXMAC_RECEIVER_ENABLE_OPTION:u32 = 0x00000200; +pub(crate) const FXMAC_RECEIVER_ENABLE_OPTION: u32 = 0x00000200; /* Enable the receiver * This option defaults to enabled (set) */ -pub(crate) const FXMAC_BROADCAST_OPTION:u32 = 0x00000400; +pub(crate) const FXMAC_BROADCAST_OPTION: u32 = 0x00000400; /* Allow reception of the broadcast address * This option defaults to enabled (set) */ -pub(crate) const FXMAC_MULTICAST_OPTION:u32 = 0x00000800; +pub(crate) const FXMAC_MULTICAST_OPTION: u32 = 0x00000800; /* Allows reception of multicast addresses programmed into hash * This option defaults to disabled (clear) */ -pub(crate) const FXMAC_RX_CHKSUM_ENABLE_OPTION:u32 = 0x00001000; +pub(crate) const FXMAC_RX_CHKSUM_ENABLE_OPTION: u32 = 0x00001000; /* Enable the RX checksum offload * This option defaults to enabled (set) */ -pub(crate) const FXMAC_TX_CHKSUM_ENABLE_OPTION:u32 = 0x00002000; +pub(crate) const FXMAC_TX_CHKSUM_ENABLE_OPTION: u32 = 0x00002000; /* Enable the TX checksum offload * This option defaults to enabled (set) */ -pub(crate) const FXMAC_JUMBO_ENABLE_OPTION:u32 = 0x00004000; -pub(crate) const FXMAC_SGMII_ENABLE_OPTION:u32 = 0x00008000; +pub(crate) const FXMAC_JUMBO_ENABLE_OPTION: u32 = 0x00004000; +pub(crate) const FXMAC_SGMII_ENABLE_OPTION: u32 = 0x00008000; -pub(crate) const FXMAC_LOOPBACK_NO_MII_OPTION:u32 = 0x00010000; -pub(crate) const FXMAC_LOOPBACK_USXGMII_OPTION:u32 = 0x00020000; +pub(crate) const FXMAC_LOOPBACK_NO_MII_OPTION: u32 = 0x00010000; +pub(crate) const FXMAC_LOOPBACK_USXGMII_OPTION: u32 = 0x00020000; -pub(crate) const FXMAC_UNICAST_OPTION:u32 = 0x00040000; +pub(crate) const FXMAC_UNICAST_OPTION: u32 = 0x00040000; -pub(crate) const FXMAC_TAIL_PTR_OPTION:u32 = 0x00080000; +pub(crate) const FXMAC_TAIL_PTR_OPTION: u32 = 0x00080000; +pub(crate) const FXMAC_DEFAULT_OPTIONS: u32 = (FXMAC_FLOW_CONTROL_OPTION + | FXMAC_FCS_INSERT_OPTION + | FXMAC_FCS_STRIP_OPTION + | FXMAC_BROADCAST_OPTION + | FXMAC_LENTYPE_ERR_OPTION + | FXMAC_TRANSMITTER_ENABLE_OPTION + | FXMAC_RECEIVER_ENABLE_OPTION + | FXMAC_RX_CHKSUM_ENABLE_OPTION + | FXMAC_TX_CHKSUM_ENABLE_OPTION); -pub(crate) const FXMAC_DEFAULT_OPTIONS:u32 = - (FXMAC_FLOW_CONTROL_OPTION | - FXMAC_FCS_INSERT_OPTION | - FXMAC_FCS_STRIP_OPTION | - FXMAC_BROADCAST_OPTION | - FXMAC_LENTYPE_ERR_OPTION | - FXMAC_TRANSMITTER_ENABLE_OPTION | - FXMAC_RECEIVER_ENABLE_OPTION | - FXMAC_RX_CHKSUM_ENABLE_OPTION | - FXMAC_TX_CHKSUM_ENABLE_OPTION); - /* The next few constants help upper layers determine the size of memory * pools used for Ethernet buffers and descriptor lists. */ -pub(crate) const FXMAC_MAC_ADDR_SIZE:u32 = 6; /* size of Ethernet header */ +pub(crate) const FXMAC_MAC_ADDR_SIZE: u32 = 6; /* size of Ethernet header */ -pub(crate) const FXMAC_MTU:u32 = 1500; /* max MTU size of Ethernet frame */ -pub(crate) const FXMAC_MTU_JUMBO:u32 = 10240; /* max MTU size of jumbo frame including Ip header + IP payload */ -pub(crate) const FXMAC_HDR_SIZE:u32 = 14; /* size of Ethernet header , DA + SA + TYPE*/ -pub(crate) const FXMAC_HDR_VLAN_SIZE:u32 = 18; /* size of Ethernet header with VLAN */ -pub(crate) const FXMAC_TRL_SIZE:u32 = 4; /* size of Ethernet trailer (FCS) */ +pub(crate) const FXMAC_MTU: u32 = 1500; /* max MTU size of Ethernet frame */ +pub(crate) const FXMAC_MTU_JUMBO: u32 = 10240; /* max MTU size of jumbo frame including Ip header + IP payload */ +pub(crate) const FXMAC_HDR_SIZE: u32 = 14; /* size of Ethernet header , DA + SA + TYPE*/ +pub(crate) const FXMAC_HDR_VLAN_SIZE: u32 = 18; /* size of Ethernet header with VLAN */ +pub(crate) const FXMAC_TRL_SIZE: u32 = 4; /* size of Ethernet trailer (FCS) */ -pub(crate) const FXMAC_MAX_FRAME_SIZE:u32 = (FXMAC_MTU + FXMAC_HDR_SIZE + FXMAC_TRL_SIZE); -pub(crate) const FXMAC_MAX_FRAME_SIZE_JUMBO:u32 = (FXMAC_MTU_JUMBO + FXMAC_HDR_SIZE + FXMAC_TRL_SIZE); +pub(crate) const FXMAC_MAX_FRAME_SIZE: u32 = (FXMAC_MTU + FXMAC_HDR_SIZE + FXMAC_TRL_SIZE); +pub(crate) const FXMAC_MAX_FRAME_SIZE_JUMBO: u32 = + (FXMAC_MTU_JUMBO + FXMAC_HDR_SIZE + FXMAC_TRL_SIZE); -pub(crate) const FXMAC_MAX_VLAN_FRAME_SIZE:u32 = (FXMAC_MTU + FXMAC_HDR_SIZE + FXMAC_HDR_VLAN_SIZE + FXMAC_TRL_SIZE); -pub(crate) const FXMAC_MAX_VLAN_FRAME_SIZE_JUMBO:u32 = (FXMAC_MTU_JUMBO + FXMAC_HDR_SIZE + FXMAC_HDR_VLAN_SIZE + FXMAC_TRL_SIZE); +pub(crate) const FXMAC_MAX_VLAN_FRAME_SIZE: u32 = + (FXMAC_MTU + FXMAC_HDR_SIZE + FXMAC_HDR_VLAN_SIZE + FXMAC_TRL_SIZE); +pub(crate) const FXMAC_MAX_VLAN_FRAME_SIZE_JUMBO: u32 = + (FXMAC_MTU_JUMBO + FXMAC_HDR_SIZE + FXMAC_HDR_VLAN_SIZE + FXMAC_TRL_SIZE); /** @name Callback identifiers * * These constants are used as parameters to FXMAC_SetHandler() * @{ */ -pub(crate) const FXMAC_HANDLER_DMASEND:u32 = 1; /* 发送中断 */ -pub(crate) const FXMAC_HANDLER_DMARECV:u32 = 2; /* 接收中断 */ -pub(crate) const FXMAC_HANDLER_ERROR:u32 = 3; /* 异常中断 */ -pub(crate) const FXMAC_HANDLER_LINKCHANGE:u32 = 4; /* 连接状态 */ -pub(crate) const FXMAC_HANDLER_RESTART:u32 = 5; /* 发送描述符队列发生异常 */ +pub(crate) const FXMAC_HANDLER_DMASEND: u32 = 1; /* 发送中断 */ +pub(crate) const FXMAC_HANDLER_DMARECV: u32 = 2; /* 接收中断 */ +pub(crate) const FXMAC_HANDLER_ERROR: u32 = 3; /* 异常中断 */ +pub(crate) const FXMAC_HANDLER_LINKCHANGE: u32 = 4; /* 连接状态 */ +pub(crate) const FXMAC_HANDLER_RESTART: u32 = 5; /* 发送描述符队列发生异常 */ /*@}*/ -pub(crate) const FXMAC_DMA_SG_IS_STARTED:u32 = 0; -pub(crate) const FXMAC_DMA_SG_IS_STOPED:u32 = 1; +pub(crate) const FXMAC_DMA_SG_IS_STARTED: u32 = 0; +pub(crate) const FXMAC_DMA_SG_IS_STOPED: u32 = 1; -pub(crate) const FXMAC_SPEED_10:u32 = 10; -pub(crate) const FXMAC_SPEED_100:u32 = 100; -pub(crate) const FXMAC_SPEED_1000:u32 = 1000; -pub(crate) const FXMAC_SPEED_2500:u32 = 2500; -pub(crate) const FXMAC_SPEED_5000:u32 = 5000; -pub(crate) const FXMAC_SPEED_10000:u32 = 10000; -pub(crate) const FXMAC_SPEED_25000:u32 = 25000; +pub(crate) const FXMAC_SPEED_10: u32 = 10; +pub(crate) const FXMAC_SPEED_100: u32 = 100; +pub(crate) const FXMAC_SPEED_1000: u32 = 1000; +pub(crate) const FXMAC_SPEED_2500: u32 = 2500; +pub(crate) const FXMAC_SPEED_5000: u32 = 5000; +pub(crate) const FXMAC_SPEED_10000: u32 = 10000; +pub(crate) const FXMAC_SPEED_25000: u32 = 25000; /* Capability mask bits */ -pub(crate) const FXMAC_CAPS_ISR_CLEAR_ON_WRITE:u32 = 0x00000001; /* irq status parameters need to be written to clear after they have been read */ -pub(crate) const FXMAC_CAPS_TAILPTR:u32 = 0x00000002; /* use tail ptr */ +pub(crate) const FXMAC_CAPS_ISR_CLEAR_ON_WRITE: u32 = 0x00000001; /* irq status parameters need to be written to clear after they have been read */ +pub(crate) const FXMAC_CAPS_TAILPTR: u32 = 0x00000002; /* use tail ptr */ /* Direction identifiers @@ -679,4 +682,4 @@ pub(crate) const FXMAC_PHY_INTERFACE_MODE_XGMII: u32 = 3; pub(crate) const FXMAC_PHY_INTERFACE_MODE_RGMII: u32 = 2; pub(crate) const FXMAC_PHY_INTERFACE_MODE_RMII: u32 = 1; pub(crate) const FXMAC_PHY_INTERFACE_MODE_SGMII: u32 = 0; -*/ \ No newline at end of file +*/ diff --git a/src/fxmac_dma.rs b/src/fxmac_dma.rs index 8d7575fb3..0c26a7f57 100644 --- a/src/fxmac_dma.rs +++ b/src/fxmac_dma.rs @@ -1,13 +1,18 @@ +//! DMA buffer descriptor management for FXMAC Ethernet. +//! +//! This module handles DMA-based packet transmission and reception, +//! including buffer descriptor ring management. + +use alloc::boxed::Box; +use alloc::vec::Vec; use core::cmp::min; -use core::ptr::{null_mut, null}; use core::mem::size_of; +use core::ptr::{null, null_mut}; use core::slice::from_raw_parts_mut; -use alloc::boxed::Box; -use alloc::vec::Vec; +use crate::fxmac::*; use crate::fxmac_const::*; use crate::fxmac_phy::*; -use crate::fxmac::*; use crate::utils::*; // fxmac_lwip_port.h @@ -17,29 +22,28 @@ pub const FXMAX_TX_BDSPACE_LENGTH: usize = FXMAX_RX_PBUFS_LENGTH * 64; // 0x2000 pub const FXMAX_RX_PBUFS_LENGTH: usize = 128; // 128 pub const FXMAX_TX_PBUFS_LENGTH: usize = 128; -pub const FXMAX_MAX_HARDWARE_ADDRESS_LENGTH: usize =6; +pub const FXMAX_MAX_HARDWARE_ADDRESS_LENGTH: usize = 6; /* configuration */ pub const FXMAC_LWIP_PORT_CONFIG_JUMBO: u32 = BIT(0); pub const FXMAC_LWIP_PORT_CONFIG_MULTICAST_ADDRESS_FILITER: u32 = BIT(1); /* Allow multicast address filtering */ -pub const FXMAC_LWIP_PORT_CONFIG_COPY_ALL_FRAMES: u32 =BIT(2); /* enable copy all frames */ -pub const FXMAC_LWIP_PORT_CONFIG_CLOSE_FCS_CHECK: u32 =BIT(3); /* close fcs check */ -pub const FXMAC_LWIP_PORT_CONFIG_UNICAST_ADDRESS_FILITER: u32 =BIT(5); /* Allow unicast address filtering */ +pub const FXMAC_LWIP_PORT_CONFIG_COPY_ALL_FRAMES: u32 = BIT(2); /* enable copy all frames */ +pub const FXMAC_LWIP_PORT_CONFIG_CLOSE_FCS_CHECK: u32 = BIT(3); /* close fcs check */ +pub const FXMAC_LWIP_PORT_CONFIG_UNICAST_ADDRESS_FILITER: u32 = BIT(5); /* Allow unicast address filtering */ /* Phy */ -pub const FXMAC_PHY_SPEED_10M: u32 = 10; -pub const FXMAC_PHY_SPEED_100M: u32 = 100; -pub const FXMAC_PHY_SPEED_1000M: u32 = 1000; -pub const FXMAC_PHY_SPEED_10G: u32 = 10000; +pub const FXMAC_PHY_SPEED_10M: u32 = 10; +pub const FXMAC_PHY_SPEED_100M: u32 = 100; +pub const FXMAC_PHY_SPEED_1000M: u32 = 1000; +pub const FXMAC_PHY_SPEED_10G: u32 = 10000; +pub const FXMAC_PHY_HALF_DUPLEX: u32 = 0; +pub const FXMAC_PHY_FULL_DUPLEX: u32 = 1; -pub const FXMAC_PHY_HALF_DUPLEX: u32 = 0; -pub const FXMAC_PHY_FULL_DUPLEX: u32 = 1; - -pub const FXMAC_RECV_MAX_COUNT: u32 =10; +pub const FXMAC_RECV_MAX_COUNT: u32 = 10; /* frame queue */ -pub const PQ_QUEUE_SIZE: u32 =4096; +pub const PQ_QUEUE_SIZE: u32 = 4096; /// Transmit buffer descriptor status words offset /// word 0/addr of BDs @@ -50,23 +54,23 @@ pub const FXMAC_BD_STAT_OFFSET: u32 = 4; pub const FXMAC_BD_ADDR_HI_OFFSET: u32 = 1 << 3; /// RX Used bit -pub const FXMAC_RXBUF_NEW_MASK: u32 = 1<<0; +pub const FXMAC_RXBUF_NEW_MASK: u32 = 1 << 0; /// RX Wrap bit, last BD -pub const FXMAC_RXBUF_WRAP_MASK: u32 = 1<<1; +pub const FXMAC_RXBUF_WRAP_MASK: u32 = 1 << 1; /// Mask for address pub const FXMAC_RXBUF_ADD_MASK: u32 = GENMASK(31, 2); // FXMAC_RXBUF_ADD_MASK=0xff_ff_ff_fc /// TX Used bit -pub const FXMAC_TXBUF_USED_MASK: u32 = 1<<31; +pub const FXMAC_TXBUF_USED_MASK: u32 = 1 << 31; /// TX Wrap bit, last descriptor -pub const FXMAC_TXBUF_WRAP_MASK: u32 = 1<<30; +pub const FXMAC_TXBUF_WRAP_MASK: u32 = 1 << 30; -pub const ULONG64_HI_MASK: u64 = 0xFFFFFFFF_00000000; -pub const ULONG64_LO_MASK: u64 = !ULONG64_HI_MASK; +// Note: ULONG64_HI_MASK and ULONG64_LO_MASK are defined in fxmac.rs +use crate::fxmac::{ULONG64_HI_MASK, ULONG64_LO_MASK}; /// Byte alignment of BDs -pub const BD_ALIGNMENT: u64 = FXMAC_DMABD_MINIMUM_ALIGNMENT*2; // 128 +pub const BD_ALIGNMENT: u64 = FXMAC_DMABD_MINIMUM_ALIGNMENT * 2; // 128 pub const FXMAC_DMABD_MINIMUM_ALIGNMENT: u64 = 64; pub const FXMAC_BD_NUM_WORDS: usize = 4; @@ -143,8 +147,7 @@ impl Default for FXmacBdRing { } } -pub struct FXmacNetifBuffer -{ +pub struct FXmacNetifBuffer { // 作为FXmacBdRing的基地址,并设置成一串多个BD pub rx_bdspace: usize, // [u8; FXMAX_RX_BDSPACE_LENGTH], aligned(256); 接收bd 缓冲区 pub tx_bdspace: usize, // FXMAX_TX_BDSPACE_LENGTH, aligned(256); 发送bd 缓冲区 @@ -156,12 +159,13 @@ pub struct FXmacNetifBuffer impl Default for FXmacNetifBuffer { fn default() -> Self { + let alloc_pages = FXMAX_RX_BDSPACE_LENGTH.div_ceil(PAGE_SIZE); + let (mut rx_vaddr, mut rx_dma) = + crate_interface::call_interface!(crate::KernelFunc::dma_alloc_coherent(alloc_pages)); - let alloc_pages = (FXMAX_RX_BDSPACE_LENGTH + (PAGE_SIZE - 1)) / PAGE_SIZE; - let (mut rx_vaddr, mut rx_dma) = crate_interface::call_interface!(crate::KernelFunc::dma_alloc_coherent(alloc_pages)); - - let alloc_pages = (FXMAX_TX_BDSPACE_LENGTH + (PAGE_SIZE - 1)) / PAGE_SIZE; - let (mut tx_vaddr, mut tx_dma) = crate_interface::call_interface!(crate::KernelFunc::dma_alloc_coherent(alloc_pages)); + let alloc_pages = FXMAX_TX_BDSPACE_LENGTH.div_ceil(PAGE_SIZE); + let (mut tx_vaddr, mut tx_dma) = + crate_interface::call_interface!(crate::KernelFunc::dma_alloc_coherent(alloc_pages)); //let rx_buf = unsafe { from_raw_parts_mut(vaddr as *mut u8, FXMAX_RX_BDSPACE_LENGTH) }; @@ -174,8 +178,7 @@ impl Default for FXmacNetifBuffer { } } -pub struct FXmacLwipPort -{ +pub struct FXmacLwipPort { pub buffer: FXmacNetifBuffer, // configuration pub feature: u32, @@ -186,92 +189,124 @@ pub struct FXmacLwipPort pub fn fxmac_bd_read(bd_ptr: u64, offset: u32) -> u32 { trace!("fxmac_bd_read at {:#x}", bd_ptr + offset as u64); - read_reg((crate_interface::call_interface!(crate::KernelFunc::virt_to_phys(bd_ptr as usize)) + offset as usize) as *const u32) + read_reg( + (crate_interface::call_interface!(crate::KernelFunc::virt_to_phys(bd_ptr as usize)) + + offset as usize) as *const u32, + ) } -pub fn fxmac_bd_write(bd_ptr: u64, offset: u32, data: u32) -{ - debug!("fxmac_bd_write {:#x} to {:#x}", data, bd_ptr + offset as u64); +pub fn fxmac_bd_write(bd_ptr: u64, offset: u32, data: u32) { + debug!( + "fxmac_bd_write {:#x} to {:#x}", + data, + bd_ptr + offset as u64 + ); // uintptr: u64 - write_reg((crate_interface::call_interface!(crate::KernelFunc::virt_to_phys(bd_ptr as usize)) + offset as usize) as *mut u32, data); + write_reg( + (crate_interface::call_interface!(crate::KernelFunc::virt_to_phys(bd_ptr as usize)) + + offset as usize) as *mut u32, + data, + ); } - + /// FXmacBdSetRxWrap /// Set this bit to mark the last descriptor in the receive buffer descriptor list. fn FXmacBdSetRxWrap(mut bdptr: u64) { - bdptr += FXMAC_BD_ADDR_OFFSET as u64; - let temp_ptr = bdptr as *mut u32; - if !temp_ptr.is_null() { - let mut data_value_rx: u32 = unsafe{*temp_ptr}; - info!("RX WRAP of BD @ {:#x} set {:#x} | FXMAC_RXBUF_WRAP_MASK", bdptr, data_value_rx); - data_value_rx |= FXMAC_RXBUF_WRAP_MASK; - unsafe { - temp_ptr.write_volatile(data_value_rx); - } - } + bdptr += FXMAC_BD_ADDR_OFFSET as u64; + let temp_ptr = bdptr as *mut u32; + if !temp_ptr.is_null() { + let mut data_value_rx: u32 = unsafe { *temp_ptr }; + info!( + "RX WRAP of BD @ {:#x} set {:#x} | FXMAC_RXBUF_WRAP_MASK", + bdptr, data_value_rx + ); + data_value_rx |= FXMAC_RXBUF_WRAP_MASK; + unsafe { + temp_ptr.write_volatile(data_value_rx); + } + } } /// FXmacBdSetTxWrap /// Sets this bit to mark the last descriptor in the transmit buffer descriptor list. fn FXmacBdSetTxWrap(mut bdptr: u64) { - bdptr += FXMAC_BD_STAT_OFFSET as u64; - let temp_ptr = bdptr as *mut u32; - if !temp_ptr.is_null() { - let mut data_value_tx: u32 = unsafe{*temp_ptr}; - info!("TX WRAP of BD @ {:#x} set {:#x} | TXBUF_WRAP", bdptr, data_value_tx); - data_value_tx |= FXMAC_TXBUF_WRAP_MASK; - unsafe { - temp_ptr.write_volatile(data_value_tx); - } - } + bdptr += FXMAC_BD_STAT_OFFSET as u64; + let temp_ptr = bdptr as *mut u32; + if !temp_ptr.is_null() { + let mut data_value_tx: u32 = unsafe { *temp_ptr }; + info!( + "TX WRAP of BD @ {:#x} set {:#x} | TXBUF_WRAP", + bdptr, data_value_tx + ); + data_value_tx |= FXMAC_TXBUF_WRAP_MASK; + unsafe { + temp_ptr.write_volatile(data_value_tx); + } + } } /// Reset BD ring head and tail pointers. -fn FXmacBdringPtrReset(ring_ptr: &mut FXmacBdRing, virtaddrloc: *mut FXmacBd) -{ - ring_ptr.free_head = virtaddrloc; - ring_ptr.pre_head = virtaddrloc; - ring_ptr.hw_head = virtaddrloc; - ring_ptr.hw_tail = virtaddrloc; - ring_ptr.post_head = virtaddrloc; +fn FXmacBdringPtrReset(ring_ptr: &mut FXmacBdRing, virtaddrloc: *mut FXmacBd) { + ring_ptr.free_head = virtaddrloc; + ring_ptr.pre_head = virtaddrloc; + ring_ptr.hw_head = virtaddrloc; + ring_ptr.hw_tail = virtaddrloc; + ring_ptr.post_head = virtaddrloc; } /// Set the BD's address field (word 0). fn fxmac_bd_set_address_rx(bd_ptr: u64, addr: u64) { - fxmac_bd_write((bd_ptr), FXMAC_BD_ADDR_OFFSET, - ((fxmac_bd_read(bd_ptr, FXMAC_BD_ADDR_OFFSET) & !FXMAC_RXBUF_ADD_MASK) | - (addr & ULONG64_LO_MASK) as u32)); - - fxmac_bd_write(bd_ptr, FXMAC_BD_ADDR_HI_OFFSET, ((addr & ULONG64_HI_MASK) >> 32) as u32); + fxmac_bd_write( + (bd_ptr), + FXMAC_BD_ADDR_OFFSET, + ((fxmac_bd_read(bd_ptr, FXMAC_BD_ADDR_OFFSET) & !FXMAC_RXBUF_ADD_MASK) + | (addr & ULONG64_LO_MASK) as u32), + ); + + fxmac_bd_write( + bd_ptr, + FXMAC_BD_ADDR_HI_OFFSET, + ((addr & ULONG64_HI_MASK) >> 32) as u32, + ); // For aarch64 } /// Set the BD's address field (word 0). fn fxmac_bd_set_address_tx(bd_ptr: u64, addr: u64) { - fxmac_bd_write(bd_ptr, FXMAC_BD_ADDR_OFFSET, (addr & ULONG64_LO_MASK) as u32); - - fxmac_bd_write(bd_ptr, FXMAC_BD_ADDR_HI_OFFSET, ((addr & ULONG64_HI_MASK) >> 32) as u32); + fxmac_bd_write( + bd_ptr, + FXMAC_BD_ADDR_OFFSET, + (addr & ULONG64_LO_MASK) as u32, + ); + + fxmac_bd_write( + bd_ptr, + FXMAC_BD_ADDR_HI_OFFSET, + ((addr & ULONG64_HI_MASK) >> 32) as u32, + ); // For aarch64 } /// 将bdptr参数向前移动任意数量的bd,绕到环的开头。 -fn FXMAC_RING_SEEKAHEAD(ring_ptr: &mut FXmacBdRing, bdptr: &mut (*mut FXmacBd), num_bd: u32) -{ +fn FXMAC_RING_SEEKAHEAD(ring_ptr: &mut FXmacBdRing, bdptr: &mut (*mut FXmacBd), num_bd: u32) { trace!("FXMAC_RING_SEEKAHEAD, bdptr={:#x}", *bdptr as u64); - // 第一个free BD - // bdptr = free_head - let mut addr: u64 = *bdptr as u64; - addr += (ring_ptr.separation * num_bd) as u64; + // 第一个free BD + // bdptr = free_head + let mut addr: u64 = *bdptr as u64; + addr += (ring_ptr.separation * num_bd) as u64; - if (addr > ring_ptr.high_bd_addr) || (*bdptr as u64 > addr) - { - addr -= ring_ptr.length as u64; - } - *bdptr = addr as *mut FXmacBd; - - trace!("FXMAC_RING_SEEKAHEAD, bdptr: {:#x}, addr: {:#x}", *bdptr as u64, addr); + if (addr > ring_ptr.high_bd_addr) || (*bdptr as u64 > addr) { + addr -= ring_ptr.length as u64; + } + *bdptr = addr as *mut FXmacBd; + + trace!( + "FXMAC_RING_SEEKAHEAD, bdptr: {:#x}, addr: {:#x}", + *bdptr as u64, + addr + ); } pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { @@ -283,28 +318,32 @@ pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { // Allocate RX descriptors, 1 RxBD at a time. info!("Allocate RX descriptors, 1 RxBD at a time."); - for i in 0..FXMAX_RX_PBUFS_LENGTH - { - let max_frame_size = if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 - { info!("FXMAC_LWIP_PORT_CONFIG_JUMBO"); FXMAC_MAX_FRAME_SIZE_JUMBO } else { info!("NO CONFIG_JUMBO"); FXMAC_MAX_FRAME_SIZE }; + for i in 0..FXMAX_RX_PBUFS_LENGTH { + let max_frame_size = if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 { + info!("FXMAC_LWIP_PORT_CONFIG_JUMBO"); + FXMAC_MAX_FRAME_SIZE_JUMBO + } else { + info!("NO CONFIG_JUMBO"); + FXMAC_MAX_FRAME_SIZE + }; - let alloc_rx_buffer_pages = (max_frame_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; - let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = - crate_interface::call_interface!(crate::KernelFunc::dma_alloc_coherent(alloc_rx_buffer_pages)); + let alloc_rx_buffer_pages = (max_frame_size as usize).div_ceil(PAGE_SIZE); + let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) = crate_interface::call_interface!( + crate::KernelFunc::dma_alloc_coherent(alloc_rx_buffer_pages) + ); - let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; + let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; let mut rxbd: *mut FXmacBd = null_mut(); -//let my_speed: Box = Box::new(88); -//rxbd = Box::into_raw(my_speed); -// OR -//let mut my_speed: i32 = 88; -//rxbd = &mut my_speed; + //let my_speed: Box = Box::new(88); + //rxbd = Box::into_raw(my_speed); + // OR + //let mut my_speed: i32 = 88; + //rxbd = &mut my_speed; // 在BD list中预留待设置的BD status = FXmacBdRingAlloc(rxringptr, 1, &mut rxbd); assert!(!rxbd.is_null()); - if (status != 0) - { + if (status != 0) { error!("FXmacInitDma: Error allocating RxBD"); return status; } @@ -321,10 +360,10 @@ pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { // Marks last descriptor in receive buffer descriptor list v |= FXMAC_RXBUF_WRAP_MASK; } - unsafe{ - temp.write_volatile(v); - // Clear word 1 in descriptor - temp.add(1).write_volatile(0); + unsafe { + temp.write_volatile(v); + // Clear word 1 in descriptor + temp.add(1).write_volatile(0); } crate::utils::DSB(); @@ -338,50 +377,55 @@ pub fn FXmacAllocDmaPbufs(instance_p: &mut FXmac) -> u32 { instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize] = rx_mbufs_vaddr as u64; } - for index in 0..FXMAX_TX_PBUFS_LENGTH { - let max_fr_size = if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 - { - FXMAC_MAX_FRAME_SIZE_JUMBO - } else { - FXMAC_MAX_FRAME_SIZE - }; - let alloc_pages = (max_fr_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; - let (mut tx_mbufs_vaddr, mut tx_mbufs_dma) = + for index in 0..FXMAX_TX_PBUFS_LENGTH { + let max_fr_size = if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 { + FXMAC_MAX_FRAME_SIZE_JUMBO + } else { + FXMAC_MAX_FRAME_SIZE + }; + let alloc_pages = (max_fr_size as usize).div_ceil(PAGE_SIZE); + let (mut tx_mbufs_vaddr, mut tx_mbufs_dma) = crate_interface::call_interface!(crate::KernelFunc::dma_alloc_coherent(alloc_pages)); - - instance_p.lwipport.buffer.tx_pbufs_storage[index as usize] = tx_mbufs_vaddr as u64; - /* - let txbd: *mut FXmacBd = null_mut(); - FXmacBdRingAlloc(txringptr, 1, txbd); - FXmacBdRingToHw(txringptr, 1, txbd); + instance_p.lwipport.buffer.tx_pbufs_storage[index] = tx_mbufs_vaddr as u64; - let bdindex = FXMAC_BD_TO_INDEX(txringptr, txbd as u64); - assert!(index == bdindex as usize); - */ + /* + let txbd: *mut FXmacBd = null_mut(); + FXmacBdRingAlloc(txringptr, 1, txbd); + FXmacBdRingToHw(txringptr, 1, txbd); - // From index to BD - let txbd = (txringptr.base_bd_addr + (index as u64 * txringptr.separation as u64)) as *mut FXmacBd; + let bdindex = FXMAC_BD_TO_INDEX(txringptr, txbd as u64); + assert!(index == bdindex as usize); + */ - fxmac_bd_set_address_tx(txbd as u64, tx_mbufs_dma as u64); - //debug!("TX DMA DESC {}: {:#010x?}", index, unsafe{*(txbd as *const macb_dma_desc)}); + // From index to BD + let txbd = + (txringptr.base_bd_addr + (index as u64 * txringptr.separation as u64)) as *mut FXmacBd; - //curbdpntr = FXMAC_BD_RING_NEXT(txring, curbdpntr); - crate::utils::DSB(); - } + fxmac_bd_set_address_tx(txbd as u64, tx_mbufs_dma as u64); + //debug!("TX DMA DESC {}: {:#010x?}", index, unsafe{*(txbd as *const macb_dma_desc)}); + + //curbdpntr = FXMAC_BD_RING_NEXT(txring, curbdpntr); + crate::utils::DSB(); + } 0 } -pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 -{ +pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 { //let mut rxbd: FXmacBd = [0; FXMAC_BD_NUM_WORDS]; let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; info!("FXmacInitDma, rxringptr: {:p}", rxringptr); info!("FXmacInitDma, txringptr: {:p}", txringptr); - info!("FXmacInitDma, rx_bdspace: {:#x}", &instance_p.lwipport.buffer.rx_bdspace); - info!("FXmacInitDma, tx_bdspace: {:#x}", &instance_p.lwipport.buffer.tx_bdspace); + info!( + "FXmacInitDma, rx_bdspace: {:#x}", + &instance_p.lwipport.buffer.rx_bdspace + ); + info!( + "FXmacInitDma, tx_bdspace: {:#x}", + &instance_p.lwipport.buffer.tx_bdspace + ); // Setup RxBD space. // 对BD域清零 @@ -392,7 +436,13 @@ pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 // Create the RxBD ring, bdspace地址必须对齐128 // 创建收包的环形缓冲区 - let mut status: u32 = FXmacBdRingCreate(rxringptr, instance_p.lwipport.buffer.rx_bdspace as u64, instance_p.lwipport.buffer.rx_bdspace as u64, BD_ALIGNMENT, FXMAX_RX_PBUFS_LENGTH as u32); + let mut status: u32 = FXmacBdRingCreate( + rxringptr, + instance_p.lwipport.buffer.rx_bdspace as u64, + instance_p.lwipport.buffer.rx_bdspace as u64, + BD_ALIGNMENT, + FXMAX_RX_PBUFS_LENGTH as u32, + ); // 将给定的BD, 克隆到list中的每个BD上 status = FXmacBdRingClone(rxringptr, &bdtemplate, FXMAC_RECV); @@ -406,11 +456,21 @@ pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 bdtemplate.fill(0); // FXMAC_BD_SET_STATUS(), 注:这里先设置TXBUF_USED位,对于发包很重要! // Set the BD's Status field (word 1). - fxmac_bd_write((&bdtemplate as *const _ as u64), FXMAC_BD_STAT_OFFSET, - fxmac_bd_read((&mut bdtemplate as *mut _ as u64), FXMAC_BD_STAT_OFFSET) | (FXMAC_TXBUF_USED_MASK)); + fxmac_bd_write( + (&bdtemplate as *const _ as u64), + FXMAC_BD_STAT_OFFSET, + fxmac_bd_read((&mut bdtemplate as *mut _ as u64), FXMAC_BD_STAT_OFFSET) + | (FXMAC_TXBUF_USED_MASK), + ); /* Create the TxBD ring */ - status = FXmacBdRingCreate(txringptr, instance_p.lwipport.buffer.tx_bdspace as u64, instance_p.lwipport.buffer.tx_bdspace as u64, BD_ALIGNMENT, FXMAX_TX_PBUFS_LENGTH as u32); + status = FXmacBdRingCreate( + txringptr, + instance_p.lwipport.buffer.tx_bdspace as u64, + instance_p.lwipport.buffer.tx_bdspace as u64, + BD_ALIGNMENT, + FXMAX_TX_PBUFS_LENGTH as u32, + ); /* We reuse the bd template, as the same one will work for both rx and tx. */ status = FXmacBdRingClone(txringptr, &bdtemplate, FXMAC_SEND); @@ -420,58 +480,65 @@ pub fn FXmacInitDma(instance_p: &mut FXmac) -> u32 FXmacSetQueuePtr(instance_p.rx_bd_queue.bdring.phys_base_addr, 0, FXMAC_RECV); FXmacSetQueuePtr(instance_p.tx_bd_queue.bdring.phys_base_addr, 0, FXMAC_SEND); - + let FXMAC_TAIL_QUEUE = |queue: u64| 0x0e80 + (queue << 2); - if (instance_p.config.caps & FXMAC_CAPS_TAILPTR) != 0 - { - write_reg((instance_p.config.base_address + FXMAC_TAIL_QUEUE(0)) as *mut u32, (1<<31) | 0); + if (instance_p.config.caps & FXMAC_CAPS_TAILPTR) != 0 { + write_reg( + (instance_p.config.base_address + FXMAC_TAIL_QUEUE(0)) as *mut u32, + 1 << 31, + ); } 0 } -fn FXMAC_BD_TO_INDEX (ringptr: &mut FXmacBdRing, bdptr: u64) -> u32 { - ( (bdptr - ringptr.base_bd_addr as u64) / ringptr.separation as u64 ) as u32 +fn FXMAC_BD_TO_INDEX(ringptr: &mut FXmacBdRing, bdptr: u64) -> u32 { + ((bdptr - ringptr.base_bd_addr) / ringptr.separation as u64) as u32 } /// 从bptr的列表中,获取下一个BD fn FXMAC_BD_RING_NEXT(ring_ptr: &mut FXmacBdRing, bd_ptr: *mut FXmacBd) -> *mut FXmacBd { - if bd_ptr as u64 >= ring_ptr.high_bd_addr { - ring_ptr.base_bd_addr as *mut FXmacBd - }else{ - (bd_ptr as u64 + ring_ptr.separation as u64) as *mut FXmacBd - } + if bd_ptr as u64 >= ring_ptr.high_bd_addr { + ring_ptr.base_bd_addr as *mut FXmacBd + } else { + (bd_ptr as u64 + ring_ptr.separation as u64) as *mut FXmacBd + } } /// Create the RxBD ring /// 创建收包的环形缓冲区 -pub fn FXmacBdRingCreate(ring_ptr: &mut FXmacBdRing, phys_addr: u64, virt_addr: u64, alignment: u64, bd_count: u32) -> u32 - { +pub fn FXmacBdRingCreate( + ring_ptr: &mut FXmacBdRing, + phys_addr: u64, + virt_addr: u64, + alignment: u64, + bd_count: u32, +) -> u32 { // uintptr: u64 - // alignment=128, bd_count=128 - //let alignment = BD_ALIGNMENT; - //let bd_count = FXMAX_RX_PBUFS_LENGTH; + // alignment=128, bd_count=128 + //let alignment = BD_ALIGNMENT; + //let bd_count = FXMAX_RX_PBUFS_LENGTH; - //let virt_addr_loc = instance_p.buffer.rx_bdspace; - let virt_addr_loc: u64 = virt_addr; + //let virt_addr_loc = instance_p.buffer.rx_bdspace; + let virt_addr_loc: u64 = virt_addr; - ring_ptr.all_cnt = 0; - ring_ptr.free_cnt = 0; - ring_ptr.hw_cnt = 0; - ring_ptr.pre_cnt = 0; - ring_ptr.post_cnt = 0; + ring_ptr.all_cnt = 0; + ring_ptr.free_cnt = 0; + ring_ptr.hw_cnt = 0; + ring_ptr.pre_cnt = 0; + ring_ptr.post_cnt = 0; - // 该地址必须对齐alignment=128 - assert!((virt_addr_loc % alignment) == 0); - assert!(bd_count > 0); + // 该地址必须对齐alignment=128 + assert!((virt_addr_loc % alignment) == 0); + assert!(bd_count > 0); - // 相邻BD之间隔多少bytes - ring_ptr.separation = size_of::() as u32; + // 相邻BD之间隔多少bytes + ring_ptr.separation = size_of::() as u32; -// Initial ring setup: -// - Clear the entire space -// - Setup each BD's BDA field with the physical address of the next BD + // Initial ring setup: + // - Clear the entire space + // - Setup each BD's BDA field with the physical address of the next BD let rxringptr = unsafe { from_raw_parts_mut(virt_addr_loc as *mut FXmacBd, bd_count as usize) }; rxringptr.fill([0; FXMAC_BD_NUM_WORDS]); @@ -480,7 +547,10 @@ pub fn FXmacBdRingCreate(ring_ptr: &mut FXmacBdRing, phys_addr: u64, virt_addr: bd_virt_addr += ring_ptr.separation as u64; } - info!("FXmacBdRingCreate BDs count={}, separation={}, {:#x}~{:#x}", bd_count, ring_ptr.separation, virt_addr, bd_virt_addr); + info!( + "FXmacBdRingCreate BDs count={}, separation={}, {:#x}~{:#x}", + bd_count, ring_ptr.separation, virt_addr, bd_virt_addr + ); // Setup and initialize pointers and counters // BD list中第一个的虚拟地址 @@ -499,7 +569,7 @@ pub fn FXmacBdRingCreate(ring_ptr: &mut FXmacBdRing, phys_addr: u64, virt_addr: // 总共的BD数 ring_ptr.all_cnt = bd_count; - ring_ptr.run_state = FXMAC_DMA_SG_IS_STOPED as u32; + ring_ptr.run_state = FXMAC_DMA_SG_IS_STOPED; ring_ptr.phys_base_addr = phys_addr; ring_ptr.hw_head = virt_addr_loc as *mut FXmacBd; ring_ptr.hw_tail = virt_addr_loc as *mut FXmacBd; @@ -507,11 +577,10 @@ pub fn FXmacBdRingCreate(ring_ptr: &mut FXmacBdRing, phys_addr: u64, virt_addr: ring_ptr.bda_restart = phys_addr as *mut FXmacBd; 0 - } +} /// 将给定的BD, 克隆到list中的每个BD上 -pub fn FXmacBdRingClone(ring_ptr: &mut FXmacBdRing, src_bd_ptr: & FXmacBd, direction: u32) -> u32 -{ +pub fn FXmacBdRingClone(ring_ptr: &mut FXmacBdRing, src_bd_ptr: &FXmacBd, direction: u32) -> u32 { // Can't do this function with some of the BDs in use assert!(ring_ptr.free_cnt == ring_ptr.all_cnt); @@ -537,13 +606,16 @@ pub fn FXmacBdRingClone(ring_ptr: &mut FXmacBdRing, src_bd_ptr: & FXmacBd, direc 0 } - /// 在BD list中预留待设置的BD -pub fn FXmacBdRingAlloc(ring_ptr: &mut FXmacBdRing, num_bd: u32, bd_set_ptr: &mut(*mut FXmacBd)) -> u32 { -/* -let num_bd = 1; -let bd_set_ptr = &rxbd; -*/ +pub fn FXmacBdRingAlloc( + ring_ptr: &mut FXmacBdRing, + num_bd: u32, + bd_set_ptr: &mut (*mut FXmacBd), +) -> u32 { + /* + let num_bd = 1; + let bd_set_ptr = &rxbd; + */ if ring_ptr.free_cnt < num_bd { error!("No Enough free BDs available for the request: {}", num_bd); @@ -551,13 +623,16 @@ let bd_set_ptr = &rxbd; } else { // 获取待设置的BD,并向前移动free BD *bd_set_ptr = ring_ptr.free_head as *mut FXmacBd; - + let b = ring_ptr.free_head; let mut free_head_t = ring_ptr.free_head; FXMAC_RING_SEEKAHEAD(ring_ptr, &mut free_head_t, num_bd); ring_ptr.free_head = free_head_t; - debug!("free_head {:#x} seekahead to {:#x}", b as usize, ring_ptr.free_head as usize); + debug!( + "free_head {:#x} seekahead to {:#x}", + b as usize, ring_ptr.free_head as usize + ); assert!(b as usize != ring_ptr.free_head as usize); ring_ptr.free_cnt -= num_bd; @@ -567,7 +642,6 @@ let bd_set_ptr = &rxbd; } } - /// 将一组BD排队到之前由FXmacBdRingAlloc分配了的硬件上 pub fn FXmacBdRingToHw(ring_ptr: &mut FXmacBdRing, num_bd: u32, bd_set_ptr: *mut FXmacBd) -> u32 { // uintptr: u64 @@ -588,8 +662,11 @@ pub fn FXmacBdRingToHw(ring_ptr: &mut FXmacBdRing, num_bd: u32, bd_set_ptr: *mut 0 } -pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_ptr: &mut (*mut FXmacBd)) -> u32 { - +pub fn FXmacBdRingFromHwRx( + ring_ptr: &mut FXmacBdRing, + bd_limit: usize, + bd_set_ptr: &mut (*mut FXmacBd), +) -> u32 { let mut cur_bd_ptr: *mut FXmacBd = ring_ptr.hw_head; let mut status: u32 = 0; let mut bd_str: u32 = 0; @@ -600,22 +677,22 @@ pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p warn!("No BDs in RX work group, there's nothing to search"); *bd_set_ptr = null_mut(); - + status = 0; - }else{ - /* Starting at hw_head, keep moving forward in the list until: + } else { + /* Starting at hw_head, keep moving forward in the list until: * - A BD is encountered with its new/used bit set which means hardware has completed processing of that BD. * - ring_ptr->hw_tail is reached and ring_ptr->hw_cnt is reached. * - The number of requested BDs has been processed */ while (bd_count as usize) < bd_limit { - // Read the status bd_str = fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_STAT_OFFSET); // FXMAC_BD_IS_RX_NEW, Determine the new bit of the receive BD - let bd_rx_new = fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_ADDR_OFFSET) & FXMAC_RXBUF_NEW_MASK; + let bd_rx_new = + fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_ADDR_OFFSET) & FXMAC_RXBUF_NEW_MASK; if bd_rx_new == 0 { break; } @@ -629,15 +706,12 @@ pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p */ if (bd_str & FXMAC_RXBUF_EOF_MASK) != 0 { bd_partial_count = 0; - } - else - { - bd_partial_count+=1; + } else { + bd_partial_count += 1; } // Move on to next BD in work group cur_bd_ptr = FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr); - } // 减去找到的任何partial packet BDs @@ -652,7 +726,7 @@ pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p ring_ptr.hw_cnt -= bd_count; ring_ptr.post_cnt += bd_count; - let mut hw_head_t = ring_ptr.hw_head; + let mut hw_head_t = ring_ptr.hw_head; FXMAC_RING_SEEKAHEAD(ring_ptr, &mut hw_head_t, bd_count); ring_ptr.hw_head = hw_head_t; @@ -668,13 +742,16 @@ pub fn FXmacBdRingFromHwRx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p status } - /// @name: FXmacBdRingFromHwTx /// @msg: Returns a set of BD(s) that have been processed by hardware. The returned /// BDs may be examined to determine the outcome of the DMA transaction(s). /// Once the BDs have been examined, the user must call FXmacBdRingFree() /// in the same order which they were retrieved here. -pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_ptr: &mut(*mut FXmacBd)) -> u32 { +pub fn FXmacBdRingFromHwTx( + ring_ptr: &mut FXmacBdRing, + bd_limit: usize, + bd_set_ptr: &mut (*mut FXmacBd), +) -> u32 { let mut bd_str: u32 = 0; let mut bd_count: u32 = 0; let mut bd_partial_count: u32 = 0; @@ -684,15 +761,12 @@ pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p let mut cur_bd_ptr: *mut FXmacBd = ring_ptr.hw_head; /* If no BDs in work group, then there's nothing to search */ - if ring_ptr.hw_cnt == 0 - { + if ring_ptr.hw_cnt == 0 { debug!("No BDs in TX work group, then there's nothing to search"); *bd_set_ptr = null_mut(); status = 0; } else { - - if bd_limitLoc > ring_ptr.hw_cnt - { + if bd_limitLoc > ring_ptr.hw_cnt { bd_limitLoc = ring_ptr.hw_cnt; } /* Starting at hw_head, keep moving forward in the list until: @@ -703,12 +777,11 @@ pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p */ while bd_count < bd_limitLoc { // Read the status - bd_str = fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_STAT_OFFSET); + bd_str = fxmac_bd_read(cur_bd_ptr as u64, FXMAC_BD_STAT_OFFSET); // 当DMA硬件对该BD已经成功处理完成时,TXBUF_USED位则处于被硬件置位; // 当软件去主动清除TX_USED位时,使能了为DMA硬件准备好的buffer - if (bd_str & FXMAC_TXBUF_USED_MASK) != 0 - { + if (bd_str & FXMAC_TXBUF_USED_MASK) != 0 { info!("FXmacBdRingFromHwTx, found a hardware USED TXBUF"); bd_count += 1; bd_partial_count += 1; @@ -718,8 +791,7 @@ pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p * If it is clear, then there are more BDs for the current * packet. Keep a count of these partial packet BDs. */ - if (bd_str & FXMAC_TXBUF_LAST_MASK) != 0 - { + if (bd_str & FXMAC_TXBUF_LAST_MASK) != 0 { bd_partial_count = 0; } @@ -754,7 +826,28 @@ pub fn FXmacBdRingFromHwTx(ring_ptr: &mut FXmacBdRing, bd_limit: usize, bd_set_p status } -/// 发包函数 +/// Transmits packets using scatter-gather DMA. +/// +/// This function sends one or more packets through the FXMAC controller using +/// DMA buffer descriptors. Each packet in the vector is transmitted as a +/// separate frame. +/// +/// # Arguments +/// +/// * `instance_p` - Mutable reference to the FXMAC instance. +/// * `p` - Vector of packets to transmit, where each packet is a `Vec`. +/// +/// # Returns +/// +/// The total number of bytes queued for transmission. +/// +/// # Example +/// +/// ```ignore +/// let mut packets = Vec::new(); +/// packets.push(ethernet_frame.to_vec()); +/// let bytes_sent = FXmacSgsend(fxmac, packets); +/// ``` pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { let mut status: u32 = 0; let mut bdindex: u32 = 0; @@ -781,13 +874,10 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { { panic!("PBUFS not available"); }*/ - - if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 - { + + if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 { max_fr_size = FXMAC_MAX_FRAME_SIZE_JUMBO; - } - else - { + } else { max_fr_size = FXMAC_MAX_FRAME_SIZE; } let pbufs_len = min(q.len(), max_fr_size as usize); @@ -796,23 +886,33 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { let pbuf = unsafe { from_raw_parts_mut(pbufs_virt as *mut u8, pbufs_len) }; pbuf.copy_from_slice(q); crate::utils::FCacheDCacheFlushRange(pbufs_virt, pbufs_len as u64); - warn!(">>>>>>>>> TX PKT {} @{:#x} - {}", pbufs_len, pbufs_virt, bdindex); + warn!( + ">>>>>>>>> TX PKT {} @{:#x} - {}", + pbufs_len, pbufs_virt, bdindex + ); debug!(">>>>>>>>> {:x?}", pbuf); //fxmac_bd_set_address_tx(txbd as u64, (pbufs_virt & 0xffff_ffff) as u64); send_len += pbufs_len as u32; - if q.len() > max_fr_size as usize - { + if q.len() > max_fr_size as usize { warn!("The packet: {} to be send is TOO LARGE", q.len()); // FXMAC_BD_SET_LENGTH: 设置BD的发送长度(以bytes为单位)。每次BD交给硬件时,都必须设置该长度 // FXMAC_BD_SET_LENGTH(txbd, max_fr_size & 0x3FFF); - fxmac_bd_write(txbd as u64, FXMAC_BD_STAT_OFFSET, - ((fxmac_bd_read(txbd as u64, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) | (max_fr_size & 0x3FFF))); + fxmac_bd_write( + txbd as u64, + FXMAC_BD_STAT_OFFSET, + ((fxmac_bd_read(txbd as u64, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) + | (max_fr_size & 0x3FFF)), + ); } else { - fxmac_bd_write(txbd as u64, FXMAC_BD_STAT_OFFSET, - ((fxmac_bd_read(txbd as u64, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) | (q.len() as u32 & 0x3FFF))); + fxmac_bd_write( + txbd as u64, + FXMAC_BD_STAT_OFFSET, + ((fxmac_bd_read(txbd as u64, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LEN_MASK) + | (q.len() as u32 & 0x3FFF)), + ); } //instance_p.lwipport.buffer.tx_pbufs_storage[bdindex as usize] = q.as_ptr() as u64; @@ -824,29 +924,39 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { // 告诉DMA当前包不是以该BD结束 //FXMAC_BD_CLEAR_LAST(txbd); let t_txbd = txbd as u64; - fxmac_bd_write(t_txbd, FXMAC_BD_STAT_OFFSET, - fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LAST_MASK ); + fxmac_bd_write( + t_txbd, + FXMAC_BD_STAT_OFFSET, + fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) & !FXMAC_TXBUF_LAST_MASK, + ); txbd = FXMAC_BD_RING_NEXT(txring, txbd); } // 告诉DMA 该BD标志着当前数据包的结束 //FXMAC_BD_SET_LAST(last_txbd); let t_txbd = last_txbd as u64; - fxmac_bd_write(t_txbd, FXMAC_BD_STAT_OFFSET, - fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) | FXMAC_TXBUF_LAST_MASK ); + fxmac_bd_write( + t_txbd, + FXMAC_BD_STAT_OFFSET, + fxmac_bd_read(t_txbd, FXMAC_BD_STAT_OFFSET) | FXMAC_TXBUF_LAST_MASK, + ); ///////// /* The bdindex always points to the first free_head in tx_bdrings */ - if (instance_p.config.caps & FXMAC_CAPS_TAILPTR) != 0 - { - bdindex = FXMAC_BD_TO_INDEX(txring, txbd as u64); + if (instance_p.config.caps & FXMAC_CAPS_TAILPTR) != 0 { + bdindex = FXMAC_BD_TO_INDEX(txring, txbd as u64); } // The used bit for the 1st BD should be cleared at the end after clearing out used bits for other fragments. let mut txbd = txbdset; - let FXMAC_BD_CLEAR_TX_USED = |bd_ptr: u64| - fxmac_bd_write(bd_ptr, FXMAC_BD_STAT_OFFSET, fxmac_bd_read(bd_ptr, FXMAC_BD_STAT_OFFSET) & (!FXMAC_TXBUF_USED_MASK)); + let FXMAC_BD_CLEAR_TX_USED = |bd_ptr: u64| { + fxmac_bd_write( + bd_ptr, + FXMAC_BD_STAT_OFFSET, + fxmac_bd_read(bd_ptr, FXMAC_BD_STAT_OFFSET) & (!FXMAC_TXBUF_USED_MASK), + ) + }; for q in 1..p.len() { txbd = FXMAC_BD_RING_NEXT(txring, txbd); @@ -859,21 +969,53 @@ pub fn FXmacSgsend(instance_p: &mut FXmac, p: Vec>) -> u32 { status = FXmacBdRingToHw(txring, n_pbufs, txbdset); let FXMAC_TAIL_QUEUE = |queue: u64| 0x0e80 + (queue << 2); - if (instance_p.config.caps & FXMAC_CAPS_TAILPTR) != 0 - { - write_reg((instance_p.config.base_address + FXMAC_TAIL_QUEUE(0)) as *mut u32, (1 << 31) | bdindex); + if (instance_p.config.caps & FXMAC_CAPS_TAILPTR) != 0 { + write_reg( + (instance_p.config.base_address + FXMAC_TAIL_QUEUE(0)) as *mut u32, + (1 << 31) | bdindex, + ); } - debug!("TX DMA DESC: {:#010x?}", unsafe{*(txbdset as *const macb_dma_desc)}); + debug!("TX DMA DESC: {:#010x?}", unsafe { + *(txbdset as *const macb_dma_desc) + }); // Start transmit - let value = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32) | FXMAC_NWCTRL_STARTTX_MASK; - write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, value); + let value = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32) + | FXMAC_NWCTRL_STARTTX_MASK; + write_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, + value, + ); send_len } -/// 收包函数 +/// Handles received packets from the DMA queue. +/// +/// This function processes all received packets currently available in the +/// RX buffer descriptor ring. It extracts packet data and returns them as +/// a vector of byte vectors. +/// +/// # Arguments +/// +/// * `instance_p` - Mutable reference to the FXMAC instance. +/// +/// # Returns +/// +/// * `Some(Vec>)` - A vector of received packets if any are available. +/// * `None` - If no packets are available. +/// +/// # Example +/// +/// ```ignore +/// if let Some(packets) = FXmacRecvHandler(fxmac) { +/// for packet in packets { +/// // Process each received Ethernet frame +/// process_packet(&packet); +/// } +/// } +/// ``` pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { trace!("RX receive packets"); let mut recv_packets = Vec::new(); @@ -882,15 +1024,21 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { //let rxring: &mut FXmacBdRing = &mut (instance_p.rx_bd_queue.bdring); /* If Reception done interrupt is asserted, call RX call back function - to handle the processed BDs and then raise the according flag.*/ + to handle the processed BDs and then raise the according flag.*/ let regval: u32 = read_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *const u32); - write_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, regval); + write_reg( + (instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, + regval, + ); loop { // Returns a set of BD(s) that have been processed by hardware. - let bd_processed: u32 = FXmacBdRingFromHwRx(&mut instance_p.rx_bd_queue.bdring, FXMAX_RX_PBUFS_LENGTH, &mut rxbdset); - if bd_processed == 0 - { + let bd_processed: u32 = FXmacBdRingFromHwRx( + &mut instance_p.rx_bd_queue.bdring, + FXMAX_RX_PBUFS_LENGTH, + &mut rxbdset, + ); + if bd_processed == 0 { // 没有待收的网络包了 break; } @@ -898,16 +1046,13 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { let mut curbdptr: *mut FXmacBd = rxbdset; for k in 0..bd_processed { - - let rxring: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; + let rxring: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; // Adjust the buffer size to the actual number of bytes received. - let rx_bytes: u32 = - if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 + let rx_bytes: u32 = if (instance_p.lwipport.feature & FXMAC_LWIP_PORT_CONFIG_JUMBO) != 0 { //FXMAC_GET_RX_FRAME_SIZE(curbdptr) fxmac_bd_read(curbdptr as u64, FXMAC_BD_STAT_OFFSET) & 0x00003FFF - } else { debug!("FXMAC_RXBUF_LEN_MASK={:#x}", FXMAC_RXBUF_LEN_MASK); //FXMAC_BD_GET_LENGTH(curbdptr) @@ -916,7 +1061,10 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { let bdindex: u32 = FXMAC_BD_TO_INDEX(rxring, curbdptr as u64); let pbufs_virt = instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize]; - debug!("RX PKT {} @{:#x} <<<<<<<<< - {}", rx_bytes, pbufs_virt, bdindex); + debug!( + "RX PKT {} @{:#x} <<<<<<<<< - {}", + rx_bytes, pbufs_virt, bdindex + ); let mbuf = unsafe { from_raw_parts_mut(pbufs_virt as *mut u8, rx_bytes as usize) }; debug!("pbuf: {:x?}", mbuf); @@ -924,20 +1072,25 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { // Copy mbuf into a new Vec recv_packets.push(mbuf.to_vec()); - /* - The value of hash_match indicates the hash result of the received packet - 0: No hash match - 1: Unicast hash match - 2: Multicast hash match - 3: Reserved, the value is not legal - */ - // FXMAC_BD_GET_HASH_MATCH(bd_ptr) - let mut hash_match: u32 = (fxmac_bd_read(curbdptr as u64, FXMAC_BD_STAT_OFFSET) & FXMAC_RXBUF_HASH_MASK) >> 29; + /* + The value of hash_match indicates the hash result of the received packet + 0: No hash match + 1: Unicast hash match + 2: Multicast hash match + 3: Reserved, the value is not legal + */ + // FXMAC_BD_GET_HASH_MATCH(bd_ptr) + let mut hash_match: u32 = (fxmac_bd_read(curbdptr as u64, FXMAC_BD_STAT_OFFSET) + & FXMAC_RXBUF_HASH_MASK) + >> 29; debug!("hash_match is {:#x}", hash_match); - + // Invalidate RX frame before queuing to handle // L1 cache prefetch conditions on any architecture. - crate::utils::FCacheDCacheInvalidateRange(instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize], rx_bytes as u64); + crate::utils::FCacheDCacheInvalidateRange( + instance_p.lwipport.buffer.rx_pbufs_storage[bdindex as usize], + rx_bytes as u64, + ); /* store it in the receive queue, * where it'll be processed by a different handler @@ -958,7 +1111,7 @@ pub fn FXmacRecvHandler(instance_p: &mut FXmac) -> Option>> { SetupRxBds(instance_p); } - if recv_packets.len() > 0 { + if !recv_packets.is_empty() { Some(recv_packets) } else { None @@ -984,7 +1137,7 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { } else { FXMAC_MAX_FRAME_SIZE }; - let alloc_rx_buffer_pages: usize = (max_frame_size as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; + let alloc_rx_buffer_pages: usize = (max_frame_size as usize).div_ceil(PAGE_SIZE); status = FXmacBdRingAlloc(rxring, 1, &mut rxbd); assert!(!rxbd.is_null()); @@ -996,7 +1149,7 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { let bdindex: u32 = FXMAC_BD_TO_INDEX(rxring, rxbd as u64); - let rx_macb_dma_desc = unsafe{(rxbd as *const macb_dma_desc).read_volatile()}; + let rx_macb_dma_desc = unsafe { (rxbd as *const macb_dma_desc).read_volatile() }; trace!("SetupRxBds - {}: {:#010x?}", bdindex, rx_macb_dma_desc); let mut v = rx_macb_dma_desc.addr & (!0x7f); // 128位对齐? @@ -1004,11 +1157,11 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { // Mask last descriptor in receive buffer list v |= FXMAC_RXBUF_WRAP_MASK; } - + let mut temp = rxbd as *mut u32; unsafe { - temp.add(1).write_volatile(0); // clear rx ctl - temp.write_volatile(v); // set rx addr + temp.add(1).write_volatile(0); // clear rx ctl + temp.write_volatile(v); // set rx addr } crate::utils::DSB(); @@ -1022,7 +1175,6 @@ pub fn SetupRxBds(instance_p: &mut FXmac) { /// FXmacBdRingFree, Frees a set of BDs that had been previously retrieved with pub fn FXmacBdRingFree(ring_ptr: &mut FXmacBdRing, num_bd: u32) -> u32 { - // if no bds to process, simply return. if 0 == num_bd { 0 @@ -1034,29 +1186,33 @@ pub fn FXmacBdRingFree(ring_ptr: &mut FXmacBdRing, num_bd: u32) -> u32 { let mut post_head_t = ring_ptr.post_head; FXMAC_RING_SEEKAHEAD(ring_ptr, &mut post_head_t, num_bd); ring_ptr.post_head = post_head_t; - + 0 } } /// Reset Tx and Rx DMA pointers after FXmacStop -pub fn ResetDma(instance_p: &mut FXmac) -{ +pub fn ResetDma(instance_p: &mut FXmac) { info!("Resetting DMA"); let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; let rxringptr: &mut FXmacBdRing = &mut instance_p.rx_bd_queue.bdring; - FXmacBdringPtrReset(txringptr, instance_p.lwipport.buffer.tx_bdspace as *mut FXmacBd); - FXmacBdringPtrReset(rxringptr, instance_p.lwipport.buffer.rx_bdspace as *mut FXmacBd); + FXmacBdringPtrReset( + txringptr, + instance_p.lwipport.buffer.tx_bdspace as *mut FXmacBd, + ); + FXmacBdringPtrReset( + rxringptr, + instance_p.lwipport.buffer.rx_bdspace as *mut FXmacBd, + ); FXmacSetQueuePtr(instance_p.tx_bd_queue.bdring.phys_base_addr, 0, FXMAC_SEND); FXmacSetQueuePtr(instance_p.rx_bd_queue.bdring.phys_base_addr, 0, FXMAC_RECV); } /// Handle DMA interrupt error -pub fn FXmacHandleDmaTxError(instance_p: &mut FXmac) -{ +pub fn FXmacHandleDmaTxError(instance_p: &mut FXmac) { panic!("Failed to handle DMA interrupt error"); /* FreeTxRxPbufs(instance_p); @@ -1074,45 +1230,60 @@ pub fn FXmacHandleDmaTxError(instance_p: &mut FXmac) */ } -pub fn FXmacHandleTxErrors(instance_p: &mut FXmac) -{ - let mut netctrlreg: u32 = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); - netctrlreg = netctrlreg & !FXMAC_NWCTRL_TXEN_MASK; - write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, netctrlreg); +pub fn FXmacHandleTxErrors(instance_p: &mut FXmac) { + let mut netctrlreg: u32 = + read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + netctrlreg &= !FXMAC_NWCTRL_TXEN_MASK; + write_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, + netctrlreg, + ); FreeOnlyTxPbufs(instance_p); CleanDmaTxdescs(instance_p); netctrlreg = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); - netctrlreg = netctrlreg | FXMAC_NWCTRL_TXEN_MASK; - write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, netctrlreg); + netctrlreg |= FXMAC_NWCTRL_TXEN_MASK; + write_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, + netctrlreg, + ); } -fn CleanDmaTxdescs(instance_p: &mut FXmac) -{ +fn CleanDmaTxdescs(instance_p: &mut FXmac) { warn!("Clean DMA TX DESCs"); let txringptr: &mut FXmacBdRing = &mut instance_p.tx_bd_queue.bdring; let mut bdtemplate: FXmacBd = [0; FXMAC_BD_NUM_WORDS]; //FXMAC_BD_SET_STATUS(&bdtemplate, FXMAC_TXBUF_USED_MASK); - fxmac_bd_write((&mut bdtemplate as *mut _ as u64), FXMAC_BD_STAT_OFFSET, - fxmac_bd_read((&mut bdtemplate as *mut _ as u64), FXMAC_BD_STAT_OFFSET) | (FXMAC_TXBUF_USED_MASK)); + fxmac_bd_write( + (&mut bdtemplate as *mut _ as u64), + FXMAC_BD_STAT_OFFSET, + fxmac_bd_read((&mut bdtemplate as *mut _ as u64), FXMAC_BD_STAT_OFFSET) + | (FXMAC_TXBUF_USED_MASK), + ); let tx_bdspace_ptr = instance_p.lwipport.buffer.tx_bdspace as u64; - FXmacBdRingCreate(txringptr, tx_bdspace_ptr, tx_bdspace_ptr, BD_ALIGNMENT, FXMAX_TX_BDSPACE_LENGTH as u32); + FXmacBdRingCreate( + txringptr, + tx_bdspace_ptr, + tx_bdspace_ptr, + BD_ALIGNMENT, + FXMAX_TX_BDSPACE_LENGTH as u32, + ); FXmacBdRingClone(txringptr, &bdtemplate, FXMAC_SEND); } -fn FreeOnlyTxPbufs(instance_p: &mut FXmac) -{ +fn FreeOnlyTxPbufs(instance_p: &mut FXmac) { warn!("Free all TX DMA pbuf"); - for index in 0..FXMAX_TX_PBUFS_LENGTH - { - if (instance_p.lwipport.buffer.tx_pbufs_storage[index] != 0) - { + for index in 0..FXMAX_TX_PBUFS_LENGTH { + if (instance_p.lwipport.buffer.tx_pbufs_storage[index] != 0) { let pbuf = instance_p.lwipport.buffer.tx_pbufs_storage[index]; - let pages = (FXMAC_MAX_FRAME_SIZE as usize + (PAGE_SIZE - 1)) / PAGE_SIZE; - crate_interface::call_interface!(crate::KernelFunc::dma_free_coherent(pbuf as usize, pages)); + let pages = (FXMAC_MAX_FRAME_SIZE as usize).div_ceil(PAGE_SIZE); + crate_interface::call_interface!(crate::KernelFunc::dma_free_coherent( + pbuf as usize, + pages + )); instance_p.lwipport.buffer.tx_pbufs_storage[index] = 0; } @@ -1120,8 +1291,7 @@ fn FreeOnlyTxPbufs(instance_p: &mut FXmac) } /// FXmacProcessSentBds, 释放发送队列q参数 -pub fn FXmacProcessSentBds(instance_p: &mut FXmac) -{ +pub fn FXmacProcessSentBds(instance_p: &mut FXmac) { let txring: &mut FXmacBdRing = &mut (instance_p.tx_bd_queue.bdring); let mut txbdset: *mut FXmacBd = null_mut(); loop { @@ -1137,7 +1307,9 @@ pub fn FXmacProcessSentBds(instance_p: &mut FXmac) while n_pbufs_freed > 0 { let bdindex = FXMAC_BD_TO_INDEX(txring, curbdpntr as u64) as usize; - trace!("FXmacProcessSentBds - {}: {:#010x?}", bdindex, unsafe{*(curbdpntr as *const macb_dma_desc)}); + trace!("FXmacProcessSentBds - {}: {:#010x?}", bdindex, unsafe { + *(curbdpntr as *const macb_dma_desc) + }); let mut v = 0; if bdindex == (FXMAX_TX_PBUFS_LENGTH - 1) { v = 0xC0000000; /* Word 1 ,used/Wrap – marks last descriptor in transmit buffer descriptor list.*/ @@ -1147,9 +1319,9 @@ pub fn FXmacProcessSentBds(instance_p: &mut FXmac) let mut temp = curbdpntr as *mut u32; // Word 0 - unsafe{ - //temp.write_volatile(0); // 这里不再对dma buffer地址清0 - temp.add(1).write_volatile(v); // 设置dma desc的ctrl + unsafe { + //temp.write_volatile(0); // 这里不再对dma buffer地址清0 + temp.add(1).write_volatile(v); // 设置dma desc的ctrl } crate::utils::DSB(); @@ -1175,56 +1347,54 @@ pub fn FXmacProcessSentBds(instance_p: &mut FXmac) } } -pub fn FXmacSendHandler(instance: &mut FXmac) -{ +pub fn FXmacSendHandler(instance: &mut FXmac) { debug!("-> FXmacSendHandler"); //let txringptr: FXmacBdRing = instance.tx_bd_queue.bdring; let regval: u32 = read_reg((instance.config.base_address + FXMAC_TXSR_OFFSET) as *const u32); // 清除中断状态位来停止中断 - write_reg((instance.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, regval); + write_reg( + (instance.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, + regval, + ); // If Transmit done interrupt is asserted, process completed BD's // 释放发送队列q参数 FXmacProcessSentBds(instance); } -pub fn FXmacLinkChange(instance: &mut FXmac) -{ +pub fn FXmacLinkChange(instance: &mut FXmac) { debug!("-> FXmacLinkChange"); - if instance.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII - { + if instance.config.interface == FXmacPhyInterface::FXMAC_PHY_INTERFACE_MODE_SGMII { let mut link: u32 = 0; let mut link_status: u32 = 0; - let ctrl: u32 = read_reg((instance.config.base_address + FXMAC_PCS_AN_LP_OFFSET) as *const u32); - let link: u32 = (ctrl & FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_STATUS) >> FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_OFFSET; + let ctrl: u32 = + read_reg((instance.config.base_address + FXMAC_PCS_AN_LP_OFFSET) as *const u32); + let link: u32 = (ctrl & FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_STATUS) + >> FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_OFFSET; match link { - 0 => { - info!("link status is down"); - link_status = FXMAC_LINKDOWN; - } - 1 => { - info!("link status is up"); - link_status = FXMAC_LINKUP; - } - _ => { - error!("link status is error {:#x}", link); + 0 => { + info!("link status is down"); + link_status = FXMAC_LINKDOWN; + } + 1 => { + info!("link status is up"); + link_status = FXMAC_LINKUP; + } + _ => { + error!("link status is error {:#x}", link); + } } - } - if link_status == FXMAC_LINKUP - { - if link_status != instance.link_status - { + if link_status == FXMAC_LINKUP { + if link_status != instance.link_status { instance.link_status = FXMAC_NEGOTIATING; info!("need NEGOTIATING"); } - } - else - { + } else { instance.link_status = FXMAC_LINKDOWN; } } @@ -1240,8 +1410,7 @@ pub fn FXmacLinkChange(instance: &mut FXmac) * @param {u32} phy_addr * @return {*} 1 is link up , 0 is link down */ -pub fn phy_link_detect(xmac_p: &mut FXmac, phy_addr: u32) -> u32 -{ +pub fn phy_link_detect(xmac_p: &mut FXmac, phy_addr: u32) -> u32 { let mut status: u16 = 0; // Read Phy Status register twice to get the confirmation of the current link status. @@ -1254,14 +1423,12 @@ pub fn phy_link_detect(xmac_p: &mut FXmac, phy_addr: u32) -> u32 0 } -pub fn phy_autoneg_status(xmac_p: &mut FXmac, phy_addr: u32) -> u32 -{ +pub fn phy_autoneg_status(xmac_p: &mut FXmac, phy_addr: u32) -> u32 { let mut status: u16 = 0; // Read Phy Status register twice to get the confirmation of the current link status. FXmacPhyRead(xmac_p, phy_addr, PHY_STATUS_REG_OFFSET, &mut status); - if status & PHY_STATUS_AUTONEGOTIATE_COMPLETE != 0 { return 1; } @@ -1269,39 +1436,72 @@ pub fn phy_autoneg_status(xmac_p: &mut FXmac, phy_addr: u32) -> u32 0 } +/// Transmits packets through the lwIP-compatible interface. +/// +/// This is the high-level transmission function that should be called by +/// network stack implementations. It handles buffer availability checking +/// and automatically processes completed transmissions. +/// +/// # Arguments +/// +/// * `instance` - Mutable reference to the FXMAC instance. +/// * `pbuf` - Vector of packets to transmit. +/// +/// # Returns +/// +/// * Positive value: Number of bytes successfully queued for transmission. +/// * `-3`: No buffer space available; packet was dropped. +/// +/// # Example +/// +/// ```ignore +/// let mut packets = Vec::new(); +/// packets.push(frame_data.to_vec()); +/// +/// match FXmacLwipPortTx(fxmac, packets) { +/// n if n > 0 => println!("Sent {} bytes", n), +/// -3 => println!("TX buffer full, packet dropped"), +/// _ => println!("Unknown error"), +/// } +/// ``` pub fn FXmacLwipPortTx(instance: &mut FXmac, pbuf: Vec>) -> i32 { info!("TX transmit packets"); // 发送网络包时注意屏蔽下中断 // check if space is available to send let freecnt = (instance.tx_bd_queue.bdring).free_cnt; - if freecnt <= 4 { //5 + if freecnt <= 4 { + //5 info!("TX freecnt={}, let's process sent BDs", freecnt); FXmacProcessSentBds(instance); } if (instance.tx_bd_queue.bdring).free_cnt != 0 { FXmacSgsend(instance, pbuf) as i32 - }else{ + } else { error!(" TX packets dropped, no space"); -3 // FREERTOS_XMAC_NO_VALID_SPACE } } -pub fn ethernetif_input_to_recv_packets(instance_p: &mut FXmac) -{ - if(instance_p.lwipport.recv_flg > 0) - { - info!("ethernetif_input_to_recv_packets, fxmac_port->recv_flg={}", instance_p.lwipport.recv_flg); +pub fn ethernetif_input_to_recv_packets(instance_p: &mut FXmac) { + if (instance_p.lwipport.recv_flg > 0) { + info!( + "ethernetif_input_to_recv_packets, fxmac_port->recv_flg={}", + instance_p.lwipport.recv_flg + ); - // 也许需要屏蔽中断的临界区来保护 - instance_p.lwipport.recv_flg -= 1; + // 也许需要屏蔽中断的临界区来保护 + instance_p.lwipport.recv_flg -= 1; - // 开中断 - write_reg((instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, instance_p.mask); + // 开中断 + write_reg( + (instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, + instance_p.mask, + ); - // 若需要中断处理函数中来接收包,可以这里解注释 - //FXmacRecvHandler(instance_p); + // 若需要中断处理函数中来接收包,可以这里解注释 + //FXmacRecvHandler(instance_p); } { @@ -1310,4 +1510,4 @@ pub fn ethernetif_input_to_recv_packets(instance_p: &mut FXmac) // IP or ARP packet // full packet send to tcpip thread to process } -} \ No newline at end of file +} diff --git a/src/fxmac_intr.rs b/src/fxmac_intr.rs index 0cb82a633..91ef23853 100644 --- a/src/fxmac_intr.rs +++ b/src/fxmac_intr.rs @@ -1,391 +1,499 @@ +//! Interrupt handling for FXMAC Ethernet controller. +//! +//! This module provides interrupt handlers and ISR setup functions for +//! handling TX/RX completion, errors, and link status changes. + use core::sync::atomic::AtomicPtr; use core::sync::atomic::Ordering; use alloc::boxed::Box; -use crate::fxmac_const::*; use crate::fxmac::*; +use crate::fxmac_const::*; use crate::fxmac_dma::*; /* XMAC */ -pub const FXMAC_NUM:u32 = 4; -pub const FXMAC0_ID:u32 = 0; -pub const FXMAC1_ID:u32 = 1; -pub const FXMAC2_ID:u32 = 2; -pub const FXMAC3_ID:u32 = 3; -pub const FXMAC0_BASE_ADDR:u32 = 0x3200C000; -pub const FXMAC1_BASE_ADDR:u32 = 0x3200E000; -pub const FXMAC2_BASE_ADDR:u32 = 0x32010000; -pub const FXMAC3_BASE_ADDR:u32 = 0x32012000; -pub const FXMAC0_MODE_SEL_BASE_ADDR:u32 = 0x3200DC00; -pub const FXMAC0_LOOPBACK_SEL_BASE_ADDR:u32 = 0x3200DC04; -pub const FXMAC1_MODE_SEL_BASE_ADDR:u32 = 0x3200FC00; -pub const FXMAC1_LOOPBACK_SEL_BASE_ADDR:u32 = 0x3200FC04; -pub const FXMAC2_MODE_SEL_BASE_ADDR:u32 = 0x32011C00; -pub const FXMAC2_LOOPBACK_SEL_BASE_ADDR:u32 = 0x32011C04; -pub const FXMAC3_MODE_SEL_BASE_ADDR:u32 = 0x32013C00; -pub const FXMAC3_LOOPBACK_SEL_BASE_ADDR:u32 = 0x32013C04; -pub const FXMAC0_PCLK:u32 = 50000000; -pub const FXMAC1_PCLK:u32 = 50000000; -pub const FXMAC2_PCLK:u32 = 50000000; -pub const FXMAC3_PCLK:u32 = 50000000; -pub const FXMAC0_HOTPLUG_IRQ_NUM:u32 = (53 + 30); -pub const FXMAC1_HOTPLUG_IRQ_NUM:u32 = (54 + 30); -pub const FXMAC2_HOTPLUG_IRQ_NUM:u32 = (55 + 30); -pub const FXMAC3_HOTPLUG_IRQ_NUM:u32 = (56 + 30); -pub const FXMAC_QUEUE_MAX_NUM:u32 = 16; // #define FXMAC_QUEUE_MAX_NUM 16U -pub const FXMAC0_QUEUE0_IRQ_NUM:u32 = (57 + 30); -pub const FXMAC0_QUEUE1_IRQ_NUM:u32 = (58 + 30); -pub const FXMAC0_QUEUE2_IRQ_NUM:u32 = (59 + 30); -pub const FXMAC0_QUEUE3_IRQ_NUM:u32 = (60 + 30); -pub const FXMAC0_QUEUE4_IRQ_NUM:u32 = (30 + 30); -pub const FXMAC0_QUEUE5_IRQ_NUM:u32 = (31 + 30); -pub const FXMAC0_QUEUE6_IRQ_NUM:u32 = (32 + 30); -pub const FXMAC0_QUEUE7_IRQ_NUM:u32 = (33 + 30); -pub const FXMAC1_QUEUE0_IRQ_NUM:u32 = (61 + 30); -pub const FXMAC1_QUEUE1_IRQ_NUM:u32 = (62 + 30); -pub const FXMAC1_QUEUE2_IRQ_NUM:u32 = (63 + 30); -pub const FXMAC1_QUEUE3_IRQ_NUM:u32 = (64 + 30); -pub const FXMAC2_QUEUE0_IRQ_NUM:u32 = (66 + 30); -pub const FXMAC2_QUEUE1_IRQ_NUM:u32 = (67 + 30); -pub const FXMAC2_QUEUE2_IRQ_NUM:u32 = (68 + 30); -pub const FXMAC2_QUEUE3_IRQ_NUM:u32 = (69 + 30); -pub const FXMAC3_QUEUE0_IRQ_NUM:u32 = (70 + 30); -pub const FXMAC3_QUEUE1_IRQ_NUM:u32 = (71 + 30); -pub const FXMAC3_QUEUE2_IRQ_NUM:u32 = (72 + 30); -pub const FXMAC3_QUEUE3_IRQ_NUM:u32 = (73 + 30); +pub const FXMAC_NUM: u32 = 4; +pub const FXMAC0_ID: u32 = 0; +pub const FXMAC1_ID: u32 = 1; +pub const FXMAC2_ID: u32 = 2; +pub const FXMAC3_ID: u32 = 3; +pub const FXMAC0_BASE_ADDR: u32 = 0x3200C000; +pub const FXMAC1_BASE_ADDR: u32 = 0x3200E000; +pub const FXMAC2_BASE_ADDR: u32 = 0x32010000; +pub const FXMAC3_BASE_ADDR: u32 = 0x32012000; +pub const FXMAC0_MODE_SEL_BASE_ADDR: u32 = 0x3200DC00; +pub const FXMAC0_LOOPBACK_SEL_BASE_ADDR: u32 = 0x3200DC04; +pub const FXMAC1_MODE_SEL_BASE_ADDR: u32 = 0x3200FC00; +pub const FXMAC1_LOOPBACK_SEL_BASE_ADDR: u32 = 0x3200FC04; +pub const FXMAC2_MODE_SEL_BASE_ADDR: u32 = 0x32011C00; +pub const FXMAC2_LOOPBACK_SEL_BASE_ADDR: u32 = 0x32011C04; +pub const FXMAC3_MODE_SEL_BASE_ADDR: u32 = 0x32013C00; +pub const FXMAC3_LOOPBACK_SEL_BASE_ADDR: u32 = 0x32013C04; +pub const FXMAC0_PCLK: u32 = 50000000; +pub const FXMAC1_PCLK: u32 = 50000000; +pub const FXMAC2_PCLK: u32 = 50000000; +pub const FXMAC3_PCLK: u32 = 50000000; +pub const FXMAC0_HOTPLUG_IRQ_NUM: u32 = (53 + 30); +pub const FXMAC1_HOTPLUG_IRQ_NUM: u32 = (54 + 30); +pub const FXMAC2_HOTPLUG_IRQ_NUM: u32 = (55 + 30); +pub const FXMAC3_HOTPLUG_IRQ_NUM: u32 = (56 + 30); +pub const FXMAC_QUEUE_MAX_NUM: u32 = 16; // #define FXMAC_QUEUE_MAX_NUM 16U +pub const FXMAC0_QUEUE0_IRQ_NUM: u32 = (57 + 30); +pub const FXMAC0_QUEUE1_IRQ_NUM: u32 = (58 + 30); +pub const FXMAC0_QUEUE2_IRQ_NUM: u32 = (59 + 30); +pub const FXMAC0_QUEUE3_IRQ_NUM: u32 = (60 + 30); +pub const FXMAC0_QUEUE4_IRQ_NUM: u32 = (30 + 30); +pub const FXMAC0_QUEUE5_IRQ_NUM: u32 = (31 + 30); +pub const FXMAC0_QUEUE6_IRQ_NUM: u32 = (32 + 30); +pub const FXMAC0_QUEUE7_IRQ_NUM: u32 = (33 + 30); +pub const FXMAC1_QUEUE0_IRQ_NUM: u32 = (61 + 30); +pub const FXMAC1_QUEUE1_IRQ_NUM: u32 = (62 + 30); +pub const FXMAC1_QUEUE2_IRQ_NUM: u32 = (63 + 30); +pub const FXMAC1_QUEUE3_IRQ_NUM: u32 = (64 + 30); +pub const FXMAC2_QUEUE0_IRQ_NUM: u32 = (66 + 30); +pub const FXMAC2_QUEUE1_IRQ_NUM: u32 = (67 + 30); +pub const FXMAC2_QUEUE2_IRQ_NUM: u32 = (68 + 30); +pub const FXMAC2_QUEUE3_IRQ_NUM: u32 = (69 + 30); +pub const FXMAC3_QUEUE0_IRQ_NUM: u32 = (70 + 30); +pub const FXMAC3_QUEUE1_IRQ_NUM: u32 = (71 + 30); +pub const FXMAC3_QUEUE2_IRQ_NUM: u32 = (72 + 30); +pub const FXMAC3_QUEUE3_IRQ_NUM: u32 = (73 + 30); //pub const FXMAC_PHY_MAX_NUM:u32 = 32; // #define FXMAC_CLK_TYPE_0 +/// Global pointer to the active FXMAC instance. +/// +/// This atomic pointer is set during initialization and used by the interrupt +/// handler to access the controller instance. pub static XMAC: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); +/// Top-level interrupt handler for FXMAC. +/// +/// This function should be registered as the interrupt handler for the FXMAC +/// controller. It retrieves the global FXMAC instance and dispatches to the +/// appropriate sub-handlers. +/// +/// # Safety +/// +/// This function accesses the global `XMAC` pointer. It assumes that the +/// pointer has been properly initialized by [`xmac_init`]. pub fn xmac_intr_handler() { debug!("Handling xmac intr ..."); let xmac = XMAC.load(Ordering::Relaxed); if !xmac.is_null() { - let xmac_ptr = unsafe{ &mut (*xmac)}; + let xmac_ptr = unsafe { &mut (*xmac) }; - // maybe irq num - let vector = xmac_ptr.config.queue_irq_num[0] ; - FXmacIntrHandler(vector as i32, xmac_ptr); - - info!("xmac intr is already handled"); -} else { - error!("static FXmac has not been initialized"); -} + // maybe irq num + let vector = xmac_ptr.config.queue_irq_num[0]; + FXmacIntrHandler(vector as i32, xmac_ptr); + info!("xmac intr is already handled"); + } else { + error!("static FXmac has not been initialized"); + } } -/// FXmacIntrHandler, 中断处理函数. vector是中断号, 目前中断只支持单queue的情况 -/// FXMAC_HANDLER_DMARECV 接收中断 => instance_p->recv_irq_handler = FXmacRecvIsrHandler; -/// FXMAC_HANDLER_DMASEND 发送中断 => instance_p->send_irq_handler = FXmacSendHandler; -/// FXMAC_HANDLER_ERROR 异常中断 => instance_p->error_irq_handler = FXmacErrorHandler; -/// FXMAC_HANDLER_LINKCHANGE 连接状态 => instance_p->link_change_handler = FXmacLinkChange; -/// +/// Main interrupt handler for FXMAC controller. +/// +/// Processes all pending interrupts for the specified FXMAC instance. This +/// handler supports the following interrupt types: +/// +/// - **FXMAC_HANDLER_DMARECV**: RX completion - calls `FXmacRecvIsrHandler` +/// - **FXMAC_HANDLER_DMASEND**: TX completion - calls `FXmacSendHandler` +/// - **FXMAC_HANDLER_ERROR**: Error conditions - calls `FXmacErrorHandler` +/// - **FXMAC_HANDLER_LINKCHANGE**: Link status change - calls `FXmacLinkChange` +/// +/// # Arguments +/// +/// * `vector` - The IRQ vector number that triggered the interrupt. +/// * `instance_p` - Mutable reference to the FXMAC instance. +/// +/// # Note +/// +/// Currently only single-queue operation is fully supported. pub fn FXmacIntrHandler(vector: i32, instance_p: &mut FXmac) { - assert!(instance_p.is_ready == FT_COMPONENT_IS_READY as u32); - - // 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number - let tx_queue_id = instance_p.tx_bd_queue.queue_id; - // 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number - let rx_queue_id = instance_p.rx_bd_queue.queue_id; - - assert!((rx_queue_id < FXMAC_QUEUE_MAX_NUM) && (tx_queue_id < FXMAC_QUEUE_MAX_NUM)); - - /* This ISR will try to handle as many interrupts as it can in a single - * call. However, in most of the places where the user's error handler - * is called, this ISR exits because it is expected that the user will - * reset the device in nearly all instances. - */ - let mut reg_isr: u32 = read_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *const u32); - - info!("+++++++++ IRQ num vector={}, Interrupt Status ISR={:#x}, tx_queue_id={}, rx_queue_id={}", vector, reg_isr, tx_queue_id, rx_queue_id); - - if vector as u32 == instance_p.config.queue_irq_num[tx_queue_id as usize] { - if tx_queue_id == 0 - { - if (reg_isr & FXMAC_IXR_TXCOMPL_MASK) != 0 - { - // Clear TX status register TX complete indication but preserve error bits if there is any - write_reg((instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, FXMAC_TXSR_TXCOMPL_MASK | FXMAC_TXSR_USEDREAD_MASK); - - FXmacSendHandler(instance_p); - - /* add */ - if(instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 - { - write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_TXCOMPL_MASK); - - } - } - - /* Transmit error conditions interrupt */ - if ((reg_isr & FXMAC_IXR_TX_ERR_MASK) != 0) && - ((reg_isr & FXMAC_IXR_TXCOMPL_MASK) == 0) - { - /* Clear TX status register */ - let reg_txsr: u32 = read_reg((instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *const u32); - - write_reg((instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, reg_txsr); - - FXmacErrorHandler(instance_p, FXMAC_SEND as u8, reg_txsr); - - /* add */ - if(instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 - { - write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_TX_ERR_MASK); - } - } - - /* add restart */ - if (reg_isr & FXMAC_IXR_TXUSED_MASK) != 0 - { - /* add */ - if(instance_p.caps& FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 - { - write_reg((instance_p.config.base_address+FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_TXUSED_MASK); - - } - - /* - if (instance_p->restart_handler) - { - instance_p->restart_handler(instance_p->restart_args); - } - */ - } - - /* link changed */ - if (reg_isr & FXMAC_IXR_LINKCHANGE_MASK) != 0 - { - - FXmacLinkChange(instance_p); - - if(instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 - { - write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_LINKCHANGE_MASK); - } - } - } - else /* use queue number more than 0 */ - { - reg_isr = read_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id)) as *const u32); - - /* Transmit Q1 complete interrupt */ - if ((reg_isr & FXMAC_INTQUESR_TXCOMPL_MASK) != 0) - { - /* Clear TX status register TX complete indication but preserve - * error bits if there is any */ - write_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id)) as *mut u32, FXMAC_INTQUESR_TXCOMPL_MASK); - write_reg((instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, (FXMAC_TXSR_TXCOMPL_MASK | FXMAC_TXSR_USEDREAD_MASK) as u32); + assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); + + // 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number + let tx_queue_id = instance_p.tx_bd_queue.queue_id; + // 0 ~ FXMAC_QUEUE_MAX_NUM ,Index queue number + let rx_queue_id = instance_p.rx_bd_queue.queue_id; + + assert!((rx_queue_id < FXMAC_QUEUE_MAX_NUM) && (tx_queue_id < FXMAC_QUEUE_MAX_NUM)); + + /* This ISR will try to handle as many interrupts as it can in a single + * call. However, in most of the places where the user's error handler + * is called, this ISR exits because it is expected that the user will + * reset the device in nearly all instances. + */ + let mut reg_isr: u32 = + read_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *const u32); + + info!( + "+++++++++ IRQ num vector={}, Interrupt Status ISR={:#x}, tx_queue_id={}, rx_queue_id={}", + vector, reg_isr, tx_queue_id, rx_queue_id + ); + + if vector as u32 == instance_p.config.queue_irq_num[tx_queue_id as usize] { + if tx_queue_id == 0 { + if (reg_isr & FXMAC_IXR_TXCOMPL_MASK) != 0 { + // Clear TX status register TX complete indication but preserve error bits if there is any + write_reg( + (instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, + FXMAC_TXSR_TXCOMPL_MASK | FXMAC_TXSR_USEDREAD_MASK, + ); FXmacSendHandler(instance_p); - } - - /* Transmit Q1 error conditions interrupt */ - if (((reg_isr & FXMAC_INTQ1SR_TXERR_MASK) != 0) && - ((reg_isr & FXMAC_INTQ1SR_TXCOMPL_MASK) != 0)) - { - /* Clear Interrupt Q1 status register */ - write_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id)) as *mut u32, reg_isr); - FXmacErrorHandler(instance_p, FXMAC_SEND as u8, reg_isr); - } - } - } - - if vector as u32 == instance_p.config.queue_irq_num[rx_queue_id as usize] - { - if rx_queue_id == 0 - { - /* Receive complete interrupt */ - if (reg_isr & FXMAC_IXR_RXCOMPL_MASK) != 0 - { - /* Clear RX status register RX complete indication but preserve - * error bits if there is any */ - write_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, (FXMAC_RXSR_FRAMERX_MASK | FXMAC_RXSR_BUFFNA_MASK) as u32); - FXmacRecvIsrHandler(instance_p); - - /* add */ - if(instance_p.caps& FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 - { - write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_RXCOMPL_MASK); - } - } - - /* Receive error conditions interrupt */ - if (reg_isr & FXMAC_IXR_RX_ERR_MASK) != 0 { - /* Clear RX status register */ - let mut reg_rxsr: u32 = read_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *const u32); - write_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, reg_rxsr); - - if (reg_isr & FXMAC_IXR_RXUSED_MASK) != 0 { - let reg_ctrl: u32 = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); - - let mut reg_temp: u32 = reg_ctrl | FXMAC_NWCTRL_FLUSH_DPRAM_MASK as u32; - reg_temp &= (!FXMAC_NWCTRL_RXEN_MASK) as u32; - write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_temp); - - /* add */ - reg_temp = reg_ctrl | FXMAC_NWCTRL_RXEN_MASK as u32; - write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_temp); - - if(instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 - { - write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_RXUSED_MASK); - } - } - - /* add */ - if ((reg_isr & FXMAC_IXR_RXOVR_MASK) != 0) && ((instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0) { - write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_RXOVR_MASK); - } - - /* add */ - if ((reg_isr & FXMAC_IXR_HRESPNOK_MASK) != 0) && ((instance_p.caps& FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0) { - write_reg((instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, FXMAC_IXR_HRESPNOK_MASK); - } - - if reg_rxsr != 0 { - FXmacErrorHandler(instance_p, FXMAC_RECV as u8, reg_rxsr); - } - } - } else { /* use queue number more than 0 */ - reg_isr = read_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)) as *const u32); - - /* Receive complete interrupt */ - if ((reg_isr & FXMAC_INTQUESR_RCOMP_MASK) != 0) - { - /* Clear RX status register RX complete indication but preserve - * error bits if there is any */ - write_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)) as *mut u32, FXMAC_INTQUESR_RCOMP_MASK); - FXmacRecvIsrHandler(instance_p); - } - - /* Receive error conditions interrupt */ - if (reg_isr & FXMAC_IXR_RX_ERR_MASK) != 0 - { - - let mut reg_ctrl: u32 = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); - reg_ctrl &= !(FXMAC_NWCTRL_RXEN_MASK as u32); - - write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_ctrl); - - /* Clear RX status register */ - let mut reg_rxsr = read_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET)as *const u32); - write_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, reg_rxsr); - - if ((reg_isr & FXMAC_IXR_RXUSED_MASK) != 0) - { - reg_ctrl = read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); - reg_ctrl |= FXMAC_NWCTRL_FLUSH_DPRAM_MASK; - - write_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, reg_ctrl); - } - - /* Clear RX status register RX complete indication but preserve - * error bits if there is any */ - write_reg((instance_p.config.base_address + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)) as *mut u32, FXMAC_INTQUESR_RXUBR_MASK); - FXmacRecvIsrHandler(instance_p); - - if reg_rxsr != 0 - { - FXmacErrorHandler(instance_p, FXMAC_RECV as u8, reg_rxsr); - } - } - } - } - } - - /** - * @name: FXmacQueueIrqDisable - * @msg: Disable queue irq - * @param {FXmac} *instance_p a pointer to the instance to be worked on. - * @param {u32} queue_num queue number - * @param {u32} mask is interrupt disable value mask - */ - pub fn FXmacQueueIrqDisable(instance_p: &mut FXmac, queue_num: u32, mask: u32) - { - assert!(instance_p.is_ready == FT_COMPONENT_IS_READY as u32); - assert!(instance_p.config.max_queue_num > queue_num); - - if queue_num == 0 { - write_reg((instance_p.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, mask & FXMAC_IXR_ALL_MASK); - - } else { - write_reg((instance_p.config.base_address + FXMAC_INTQX_IDR_SIZE_OFFSET(queue_num as u64)) as *mut u32, mask & FXMAC_IXR_ALL_MASK); - - } - } - - /// FXmacQueueIrqEnable, Enable queue irq - pub fn FXmacQueueIrqEnable(instance_p: &mut FXmac, queue_num: u32, mask: u32) - { - assert!(instance_p.is_ready == FT_COMPONENT_IS_READY as u32); - assert!(instance_p.config.max_queue_num > queue_num); - - if queue_num == 0 { - write_reg((instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, mask & FXMAC_IXR_ALL_MASK); - - } else { - write_reg((instance_p.config.base_address + FXMAC_INTQX_IER_SIZE_OFFSET(queue_num as u64)) as *mut u32, mask & FXMAC_IXR_ALL_MASK); - - } - } - -pub fn FXmacErrorHandler(instance_p: &mut FXmac, direction: u8, error_word: u32) -{ - debug!("-> FXmacErrorHandler, direction={}, error_word={}", direction, error_word); - if error_word != 0 - { - match direction as u32 - { - FXMAC_RECV => { - if (error_word & FXMAC_RXSR_HRESPNOK_MASK) != 0 - { - error!("Receive DMA error"); - FXmacHandleDmaTxError(instance_p); + /* add */ + if (instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 { + write_reg( + (instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, + FXMAC_IXR_TXCOMPL_MASK, + ); + } } - if (error_word & FXMAC_RXSR_RXOVR_MASK) != 0 + + /* Transmit error conditions interrupt */ + if ((reg_isr & FXMAC_IXR_TX_ERR_MASK) != 0) && ((reg_isr & FXMAC_IXR_TXCOMPL_MASK) == 0) { - error!("Receive over run"); - //FXmacRecvHandler(instance_p); + /* Clear TX status register */ + let reg_txsr: u32 = + read_reg((instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *const u32); + + write_reg( + (instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, + reg_txsr, + ); + + FXmacErrorHandler(instance_p, FXMAC_SEND as u8, reg_txsr); + + /* add */ + if (instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 { + write_reg( + (instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, + FXMAC_IXR_TX_ERR_MASK, + ); + } } - if (error_word & FXMAC_RXSR_BUFFNA_MASK) != 0 - { - error!("Receive buffer not available"); - //FXmacRecvHandler(instance_p); + + /* add restart */ + if (reg_isr & FXMAC_IXR_TXUSED_MASK) != 0 { + /* add */ + if (instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 { + write_reg( + (instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, + FXMAC_IXR_TXUSED_MASK, + ); + } + + /* + if (instance_p->restart_handler) + { + instance_p->restart_handler(instance_p->restart_args); + } + */ } - } - FXMAC_SEND => { - if (error_word & FXMAC_TXSR_HRESPNOK_MASK) != 0 - { - error!("Transmit DMA error"); - FXmacHandleDmaTxError(instance_p); + + /* link changed */ + if (reg_isr & FXMAC_IXR_LINKCHANGE_MASK) != 0 { + FXmacLinkChange(instance_p); + + if (instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 { + write_reg( + (instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, + FXMAC_IXR_LINKCHANGE_MASK, + ); + } } - if (error_word & FXMAC_TXSR_URUN_MASK) != 0 - { - error!("Transmit under run"); - FXmacHandleTxErrors(instance_p); + } else + /* use queue number more than 0 */ + { + reg_isr = read_reg( + (instance_p.config.base_address + + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id)) + as *const u32, + ); + + /* Transmit Q1 complete interrupt */ + if ((reg_isr & FXMAC_INTQUESR_TXCOMPL_MASK) != 0) { + /* Clear TX status register TX complete indication but preserve + * error bits if there is any */ + write_reg( + (instance_p.config.base_address + + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id)) + as *mut u32, + FXMAC_INTQUESR_TXCOMPL_MASK, + ); + write_reg( + (instance_p.config.base_address + FXMAC_TXSR_OFFSET) as *mut u32, + FXMAC_TXSR_TXCOMPL_MASK | FXMAC_TXSR_USEDREAD_MASK, + ); + + FXmacSendHandler(instance_p); } - if (error_word & FXMAC_TXSR_BUFEXH_MASK) != 0 + + /* Transmit Q1 error conditions interrupt */ + if (((reg_isr & FXMAC_INTQ1SR_TXERR_MASK) != 0) + && ((reg_isr & FXMAC_INTQ1SR_TXCOMPL_MASK) != 0)) { - error!("Transmit buffer exhausted"); - FXmacHandleTxErrors(instance_p); + /* Clear Interrupt Q1 status register */ + write_reg( + (instance_p.config.base_address + + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id)) + as *mut u32, + reg_isr, + ); + + FXmacErrorHandler(instance_p, FXMAC_SEND as u8, reg_isr); } - if (error_word & FXMAC_TXSR_RXOVR_MASK) != 0 - { - error!("Transmit retry excessed limits"); - FXmacHandleTxErrors(instance_p); + } + } + + if vector as u32 == instance_p.config.queue_irq_num[rx_queue_id as usize] { + if rx_queue_id == 0 { + /* Receive complete interrupt */ + if (reg_isr & FXMAC_IXR_RXCOMPL_MASK) != 0 { + /* Clear RX status register RX complete indication but preserve + * error bits if there is any */ + write_reg( + (instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, + FXMAC_RXSR_FRAMERX_MASK | FXMAC_RXSR_BUFFNA_MASK, + ); + FXmacRecvIsrHandler(instance_p); + + /* add */ + if (instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 { + write_reg( + (instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, + FXMAC_IXR_RXCOMPL_MASK, + ); + } } - if (error_word & FXMAC_TXSR_FRAMERX_MASK) != 0 - { - error!("Transmit collision"); - FXmacProcessSentBds(instance_p); + + /* Receive error conditions interrupt */ + if (reg_isr & FXMAC_IXR_RX_ERR_MASK) != 0 { + /* Clear RX status register */ + let mut reg_rxsr: u32 = + read_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *const u32); + write_reg( + (instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, + reg_rxsr, + ); + + if (reg_isr & FXMAC_IXR_RXUSED_MASK) != 0 { + let reg_ctrl: u32 = read_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32, + ); + + let mut reg_temp: u32 = reg_ctrl | FXMAC_NWCTRL_FLUSH_DPRAM_MASK; + reg_temp &= !FXMAC_NWCTRL_RXEN_MASK; + write_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, + reg_temp, + ); + + /* add */ + reg_temp = reg_ctrl | FXMAC_NWCTRL_RXEN_MASK; + write_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, + reg_temp, + ); + + if (instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0 { + write_reg( + (instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, + FXMAC_IXR_RXUSED_MASK, + ); + } + } + + /* add */ + if ((reg_isr & FXMAC_IXR_RXOVR_MASK) != 0) + && ((instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0) + { + write_reg( + (instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, + FXMAC_IXR_RXOVR_MASK, + ); + } + + /* add */ + if ((reg_isr & FXMAC_IXR_HRESPNOK_MASK) != 0) + && ((instance_p.caps & FXMAC_CAPS_ISR_CLEAR_ON_WRITE) != 0) + { + write_reg( + (instance_p.config.base_address + FXMAC_ISR_OFFSET) as *mut u32, + FXMAC_IXR_HRESPNOK_MASK, + ); + } + + if reg_rxsr != 0 { + FXmacErrorHandler(instance_p, FXMAC_RECV as u8, reg_rxsr); + } + } + } else { + /* use queue number more than 0 */ + reg_isr = read_reg( + (instance_p.config.base_address + + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)) + as *const u32, + ); + + /* Receive complete interrupt */ + if ((reg_isr & FXMAC_INTQUESR_RCOMP_MASK) != 0) { + /* Clear RX status register RX complete indication but preserve + * error bits if there is any */ + write_reg( + (instance_p.config.base_address + + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)) + as *mut u32, + FXMAC_INTQUESR_RCOMP_MASK, + ); + FXmacRecvIsrHandler(instance_p); + } + + /* Receive error conditions interrupt */ + if (reg_isr & FXMAC_IXR_RX_ERR_MASK) != 0 { + let mut reg_ctrl: u32 = + read_reg((instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32); + reg_ctrl &= !FXMAC_NWCTRL_RXEN_MASK; + + write_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, + reg_ctrl, + ); + + /* Clear RX status register */ + let mut reg_rxsr = + read_reg((instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *const u32); + write_reg( + (instance_p.config.base_address + FXMAC_RXSR_OFFSET) as *mut u32, + reg_rxsr, + ); + + if ((reg_isr & FXMAC_IXR_RXUSED_MASK) != 0) { + reg_ctrl = read_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *const u32, + ); + reg_ctrl |= FXMAC_NWCTRL_FLUSH_DPRAM_MASK; + + write_reg( + (instance_p.config.base_address + FXMAC_NWCTRL_OFFSET) as *mut u32, + reg_ctrl, + ); + } + + /* Clear RX status register RX complete indication but preserve + * error bits if there is any */ + write_reg( + (instance_p.config.base_address + + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)) + as *mut u32, + FXMAC_INTQUESR_RXUBR_MASK, + ); + FXmacRecvIsrHandler(instance_p); + + if reg_rxsr != 0 { + FXmacErrorHandler(instance_p, FXMAC_RECV as u8, reg_rxsr); + } } } - _ => { error!("FXmacErrorHandler failed, unknown direction={}", direction); } + } +} + +/** + * @name: FXmacQueueIrqDisable + * @msg: Disable queue irq + * @param {FXmac} *instance_p a pointer to the instance to be worked on. + * @param {u32} queue_num queue number + * @param {u32} mask is interrupt disable value mask + */ +pub fn FXmacQueueIrqDisable(instance_p: &mut FXmac, queue_num: u32, mask: u32) { + assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); + assert!(instance_p.config.max_queue_num > queue_num); + + if queue_num == 0 { + write_reg( + (instance_p.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, + mask & FXMAC_IXR_ALL_MASK, + ); + } else { + write_reg( + (instance_p.config.base_address + FXMAC_INTQX_IDR_SIZE_OFFSET(queue_num as u64)) + as *mut u32, + mask & FXMAC_IXR_ALL_MASK, + ); + } +} + +/// FXmacQueueIrqEnable, Enable queue irq +pub fn FXmacQueueIrqEnable(instance_p: &mut FXmac, queue_num: u32, mask: u32) { + assert!(instance_p.is_ready == FT_COMPONENT_IS_READY); + assert!(instance_p.config.max_queue_num > queue_num); + + if queue_num == 0 { + write_reg( + (instance_p.config.base_address + FXMAC_IER_OFFSET) as *mut u32, + mask & FXMAC_IXR_ALL_MASK, + ); + } else { + write_reg( + (instance_p.config.base_address + FXMAC_INTQX_IER_SIZE_OFFSET(queue_num as u64)) + as *mut u32, + mask & FXMAC_IXR_ALL_MASK, + ); + } +} + +pub fn FXmacErrorHandler(instance_p: &mut FXmac, direction: u8, error_word: u32) { + debug!( + "-> FXmacErrorHandler, direction={}, error_word={}", + direction, error_word + ); + if error_word != 0 { + match direction as u32 { + FXMAC_RECV => { + if (error_word & FXMAC_RXSR_HRESPNOK_MASK) != 0 { + error!("Receive DMA error"); + FXmacHandleDmaTxError(instance_p); + } + if (error_word & FXMAC_RXSR_RXOVR_MASK) != 0 { + error!("Receive over run"); + //FXmacRecvHandler(instance_p); + } + if (error_word & FXMAC_RXSR_BUFFNA_MASK) != 0 { + error!("Receive buffer not available"); + //FXmacRecvHandler(instance_p); + } + } + FXMAC_SEND => { + if (error_word & FXMAC_TXSR_HRESPNOK_MASK) != 0 { + error!("Transmit DMA error"); + FXmacHandleDmaTxError(instance_p); + } + if (error_word & FXMAC_TXSR_URUN_MASK) != 0 { + error!("Transmit under run"); + FXmacHandleTxErrors(instance_p); + } + if (error_word & FXMAC_TXSR_BUFEXH_MASK) != 0 { + error!("Transmit buffer exhausted"); + FXmacHandleTxErrors(instance_p); + } + if (error_word & FXMAC_TXSR_RXOVR_MASK) != 0 { + error!("Transmit retry excessed limits"); + FXmacHandleTxErrors(instance_p); + } + if (error_word & FXMAC_TXSR_FRAMERX_MASK) != 0 { + error!("Transmit collision"); + FXmacProcessSentBds(instance_p); + } + } + _ => { + error!("FXmacErrorHandler failed, unknown direction={}", direction); + } } } } @@ -393,16 +501,18 @@ pub fn FXmacErrorHandler(instance_p: &mut FXmac, direction: u8, error_word: u32) pub fn FXmacRecvIsrHandler(instance: &mut FXmac) { debug!("-> FXmacRecvIsrHandler"); // 关中断 - write_reg((instance.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, FXMAC_IXR_RXCOMPL_MASK); + write_reg( + (instance.config.base_address + FXMAC_IDR_OFFSET) as *mut u32, + FXMAC_IXR_RXCOMPL_MASK, + ); instance.lwipport.recv_flg += 1; ethernetif_input_to_recv_packets(instance); // 处理后会开中断 } - /// 网卡中断设置 -pub fn FXmacSetupIsr(instance: &mut FXmac) -{ +/// 网卡中断设置 +pub fn FXmacSetupIsr(instance: &mut FXmac) { // 获取当前CPU ID: 0, 1, 2, 3, 4, 5, 6, 7 //let cpu_id: u32 = get_cpu_id(); // 路由中断到指定的cpu,或所有的cpu @@ -425,6 +535,8 @@ pub fn FXmacSetupIsr(instance: &mut FXmac) // SPI(Shared Peripheral Interrupt) rang: 32..1020 info!("register callback function for irq: {}", irq_num); - crate_interface::call_interface!(crate::KernelFunc::dma_request_irq(irq_num, xmac_intr_handler)); - -} \ No newline at end of file + crate_interface::call_interface!(crate::KernelFunc::dma_request_irq( + irq_num, + xmac_intr_handler + )); +} diff --git a/src/fxmac_phy.rs b/src/fxmac_phy.rs index 96ea08b10..c95bfc0db 100644 --- a/src/fxmac_phy.rs +++ b/src/fxmac_phy.rs @@ -1,6 +1,12 @@ -use crate::fxmac_const::*; +//! PHY management for FXMAC Ethernet controller. +//! +//! This module provides functions for PHY initialization, configuration, +//! and management through the MDIO interface. + use crate::fxmac::*; +use crate::fxmac_const::*; +/// PHY Control Register offset (MII register 0). pub const PHY_CONTROL_REG_OFFSET: u32 = 0; pub const PHY_STATUS_REG_OFFSET: u32 = 1; pub const PHY_IDENTIFIER_1_REG: u32 = 2; @@ -23,22 +29,25 @@ pub const PHY_CONTROL_LOOPBACK_MASK: u16 = 0x4000; pub const PHY_CONTROL_AUTONEGOTIATE_ENABLE: u16 = 0x1000; pub const PHY_CONTROL_AUTONEGOTIATE_RESTART: u16 = 0x0200; pub const PHY_STATUS_AUTONEGOTIATE_COMPLETE: u16 = 0x0020; -pub const PHY_STAT_LINK_STATUS: u16 = 0x0004; +pub const PHY_STAT_LINK_STATUS: u16 = 0x0004; pub const PHY_AUTOADVERTISE_ASYMMETRIC_PAUSE_MASK: u16 = 0x0800; pub const PHY_AUTOADVERTISE_PAUSE_MASK: u16 = 0x0400; pub const PHY_AUTOADVERTISE_AUTONEG_ERROR_MASK: u16 = 0x8000; /* Advertisement control register. */ -pub const PHY_AUTOADVERTISE_10HALF: u16 = 0x0020; /* Try for 10mbps half-duplex */ -pub const PHY_AUTOADVERTISE_1000XFULL: u16 = 0x0020; /* Try for 1000BASE-X full-duplex */ -pub const PHY_AUTOADVERTISE_10FULL: u16 = 0x0040; /* Try for 10mbps full-duplex */ -pub const PHY_AUTOADVERTISE_1000XHALF: u16 = 0x0040; /* Try for 1000BASE-X half-duplex */ -pub const PHY_AUTOADVERTISE_100HALF: u16 = 0x0080; /* Try for 100mbps half-duplex */ -pub const PHY_AUTOADVERTISE_1000XPAUSE: u16 = 0x0080; /* Try for 1000BASE-X pause */ -pub const PHY_AUTOADVERTISE_100FULL: u16 = 0x0100; /* Try for 100mbps full-duplex */ +pub const PHY_AUTOADVERTISE_10HALF: u16 = 0x0020; /* Try for 10mbps half-duplex */ +pub const PHY_AUTOADVERTISE_1000XFULL: u16 = 0x0020; /* Try for 1000BASE-X full-duplex */ +pub const PHY_AUTOADVERTISE_10FULL: u16 = 0x0040; /* Try for 10mbps full-duplex */ +pub const PHY_AUTOADVERTISE_1000XHALF: u16 = 0x0040; /* Try for 1000BASE-X half-duplex */ +pub const PHY_AUTOADVERTISE_100HALF: u16 = 0x0080; /* Try for 100mbps half-duplex */ +pub const PHY_AUTOADVERTISE_1000XPAUSE: u16 = 0x0080; /* Try for 1000BASE-X pause */ +pub const PHY_AUTOADVERTISE_100FULL: u16 = 0x0100; /* Try for 100mbps full-duplex */ pub const PHY_AUTOADVERTISE_1000XPSE_ASYM: u16 = 0x0100; /* Try for 1000BASE-X asym pause */ -pub const PHY_AUTOADVERTISE_100BASE4: u16 = 0x0200; /* Try for 100mbps 4k packets */ -pub const PHY_AUTOADVERTISE_100_AND_10: u16 = (PHY_AUTOADVERTISE_10FULL | PHY_AUTOADVERTISE_100FULL | PHY_AUTOADVERTISE_10HALF | PHY_AUTOADVERTISE_100HALF); +pub const PHY_AUTOADVERTISE_100BASE4: u16 = 0x0200; /* Try for 100mbps 4k packets */ +pub const PHY_AUTOADVERTISE_100_AND_10: u16 = (PHY_AUTOADVERTISE_10FULL + | PHY_AUTOADVERTISE_100FULL + | PHY_AUTOADVERTISE_10HALF + | PHY_AUTOADVERTISE_100HALF); pub const PHY_AUTOADVERTISE_100: u16 = (PHY_AUTOADVERTISE_100FULL | PHY_AUTOADVERTISE_100HALF); pub const PHY_AUTOADVERTISE_10: u16 = (PHY_AUTOADVERTISE_10FULL | PHY_AUTOADVERTISE_10HALF); pub const PHY_AUTOADVERTISE_1000: u16 = 0x0300; @@ -53,44 +62,68 @@ pub const FXMAC_PHY_AUTONEGOTIATION_DISABLE: u32 = 0; pub const FXMAC_PHY_AUTONEGOTIATION_ENABLE: u32 = 1; pub const FXMAC_PHY_MODE_HALFDUPLEX: u32 = 0; pub const FXMAC_PHY_MODE_FULLDUPLEX: u32 = 1; -pub const FXMAC_PHY_MAX_NUM:u32 = 32; - -/* - * Write data to the specified PHY register. The Ethernet driver does not - * require the device to be stopped before writing to the PHY. Although it is - * probably a good idea to stop the device, it is the responsibility of the - * application to deem this necessary. The MAC provides the driver with the - * ability to talk to a PHY that adheres to the Media Independent Interface - * (MII) as defined in the IEEE 802.3 standard. - * - * Prior to PHY access with this function, the user should have setup the MDIO - * clock with FXmacSetMdioDivisor(). - */ -pub fn FXmacPhyWrite(instance_p: &mut FXmac, phy_address: u32, register_num: u32, phy_data: u16) -> u32 -{ +pub const FXMAC_PHY_MAX_NUM: u32 = 32; + +/// Writes data to a PHY register via MDIO. +/// +/// Writes a 16-bit value to the specified register of the PHY at the given +/// address. The MAC provides MDIO access to PHYs adhering to the IEEE 802.3 +/// Media Independent Interface (MII) standard. +/// +/// # Arguments +/// +/// * `instance_p` - Mutable reference to the FXMAC instance. +/// * `phy_address` - PHY address on the MDIO bus (0-31). +/// * `register_num` - PHY register number to write. +/// * `phy_data` - 16-bit data to write to the register. +/// +/// # Returns +/// +/// * `0` (FT_SUCCESS) on successful write. +/// * `6` (FXMAC_ERR_PHY_BUSY) if the MDIO bus is busy. +/// +/// # Note +/// +/// The device does not need to be stopped before PHY access, but the MDIO +/// clock should be properly configured. +pub fn FXmacPhyWrite( + instance_p: &mut FXmac, + phy_address: u32, + register_num: u32, + phy_data: u16, +) -> u32 { let mut mgtcr: u32 = 0; let mut ipisr: u32 = 0; let mut ip_write_temp: u32 = 0; let mut status: u32 = 0; - debug!("FXmacPhyWrite, phy_address={:#x}, register_num={}, phy_data={:#x}", phy_address, register_num, phy_data); + debug!( + "FXmacPhyWrite, phy_address={:#x}, register_num={}, phy_data={:#x}", + phy_address, register_num, phy_data + ); /* Make sure no other PHY operation is currently in progress */ - if (read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32) & - FXMAC_NWSR_MDIOIDLE_MASK) == 0 - { + if (read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32) + & FXMAC_NWSR_MDIOIDLE_MASK) + == 0 + { status = 6; // FXMAC_ERR_PHY_BUSY; error!("FXmacPhyRead error: PHY busy!"); - }else{ + } else { /* Construct mgtcr mask for the operation */ - mgtcr = FXMAC_PHYMNTNC_OP_MASK | FXMAC_PHYMNTNC_OP_W_MASK | - (phy_address << FXMAC_PHYMNTNC_PHAD_SHFT_MSK) | - (register_num << FXMAC_PHYMNTNC_PREG_SHFT_MSK) | phy_data as u32; + mgtcr = FXMAC_PHYMNTNC_OP_MASK + | FXMAC_PHYMNTNC_OP_W_MASK + | (phy_address << FXMAC_PHYMNTNC_PHAD_SHFT_MSK) + | (register_num << FXMAC_PHYMNTNC_PREG_SHFT_MSK) + | phy_data as u32; /* Write mgtcr and wait for completion */ - write_reg((instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *mut u32, mgtcr); + write_reg( + (instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *mut u32, + mgtcr, + ); - loop{ + loop { ipisr = read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32); ip_write_temp = ipisr; @@ -105,28 +138,54 @@ pub fn FXmacPhyWrite(instance_p: &mut FXmac, phy_address: u32, register_num: u32 status } -pub fn FXmacPhyRead(instance_p: &mut FXmac, phy_address: u32, register_num: u32, phydat_aptr: &mut u16) -> u32 -{ +/// Reads data from a PHY register via MDIO. +/// +/// Reads a 16-bit value from the specified register of the PHY at the given +/// address. +/// +/// # Arguments +/// +/// * `instance_p` - Mutable reference to the FXMAC instance. +/// * `phy_address` - PHY address on the MDIO bus (0-31). +/// * `register_num` - PHY register number to read. +/// * `phydat_aptr` - Mutable reference to store the read data. +/// +/// # Returns +/// +/// * `0` (FT_SUCCESS) on successful read. +/// * `6` (FXMAC_ERR_PHY_BUSY) if the MDIO bus is busy. +pub fn FXmacPhyRead( + instance_p: &mut FXmac, + phy_address: u32, + register_num: u32, + phydat_aptr: &mut u16, +) -> u32 { let mut mgtcr: u32 = 0; let mut ipisr: u32 = 0; let mut IpReadTemp: u32 = 0; let mut status: u32 = 0; /* Make sure no other PHY operation is currently in progress */ - if (read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32) & FXMAC_NWSR_MDIOIDLE_MASK) == 0 - { + if (read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32) + & FXMAC_NWSR_MDIOIDLE_MASK) + == 0 + { status = 6; error!("FXmacPhyRead error: PHY busy!"); - }else{ + } else { /* Construct mgtcr mask for the operation */ - mgtcr = FXMAC_PHYMNTNC_OP_MASK | FXMAC_PHYMNTNC_OP_R_MASK | - (phy_address << FXMAC_PHYMNTNC_PHAD_SHFT_MSK) | - (register_num << FXMAC_PHYMNTNC_PREG_SHFT_MSK); + mgtcr = FXMAC_PHYMNTNC_OP_MASK + | FXMAC_PHYMNTNC_OP_R_MASK + | (phy_address << FXMAC_PHYMNTNC_PHAD_SHFT_MSK) + | (register_num << FXMAC_PHYMNTNC_PREG_SHFT_MSK); /* Write mgtcr and wait for completion */ - write_reg((instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *mut u32, mgtcr); + write_reg( + (instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *mut u32, + mgtcr, + ); - loop{ + loop { ipisr = read_reg((instance_p.config.base_address + FXMAC_NWSR_OFFSET) as *const u32); IpReadTemp = ipisr; @@ -136,31 +195,54 @@ pub fn FXmacPhyRead(instance_p: &mut FXmac, phy_address: u32, register_num: u32, } // Read data - *phydat_aptr = read_reg((instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *const u32) as u16; + *phydat_aptr = + read_reg((instance_p.config.base_address + FXMAC_PHYMNTNC_OFFSET) as *const u32) as u16; - debug!("FXmacPhyRead, phy_address={:#x}, register_num={}, phydat_aptr={:#x}", phy_address, register_num, phydat_aptr); + debug!( + "FXmacPhyRead, phy_address={:#x}, register_num={}, phydat_aptr={:#x}", + phy_address, register_num, phydat_aptr + ); status = 0; } - + status } - -/// FXmacPhyInit -/// Setup the PHYs for proper speed setting. -pub fn FXmacPhyInit( - instance_p: &mut FXmac, - reset_flag: u32, -) -> u32 { +/// Initializes the PHY for the FXMAC controller. +/// +/// This function performs PHY detection, optional reset, and speed/duplex +/// configuration. It supports both auto-negotiation and manual speed setting. +/// +/// # Arguments +/// +/// * `instance_p` - Mutable reference to the FXMAC instance. +/// * `reset_flag` - Set to `XMAC_PHY_RESET_ENABLE` to perform a PHY reset +/// before configuration, or `XMAC_PHY_RESET_DISABLE` to skip. +/// +/// # Returns +/// +/// * `0` (FT_SUCCESS) on successful initialization. +/// * `7` (FXMAC_PHY_IS_NOT_FOUND) if no PHY is detected. +/// * `8` (FXMAC_PHY_AUTO_AUTONEGOTIATION_FAILED) if auto-negotiation fails. +/// * Other error codes for PHY communication failures. +/// +/// # Example +/// +/// ```ignore +/// // Initialize PHY with reset +/// let result = FXmacPhyInit(fxmac, XMAC_PHY_RESET_ENABLE); +/// if result == 0 { +/// println!("PHY initialized, speed: {} Mbps", fxmac.config.speed); +/// } +/// ``` +pub fn FXmacPhyInit(instance_p: &mut FXmac, reset_flag: u32) -> u32 { let speed = instance_p.config.speed; let duplex_mode = instance_p.config.duplex; let autonegotiation_en = instance_p.config.auto_neg; - info!("FXmacPhyInit, speed={}, duplex_mode={}, autonegotiation_en={}, reset_flag={}", - speed, - duplex_mode, - autonegotiation_en, - reset_flag + info!( + "FXmacPhyInit, speed={}, duplex_mode={}, autonegotiation_en={}, reset_flag={}", + speed, duplex_mode, autonegotiation_en, reset_flag ); let mut ret: u32 = 0; let mut phy_addr: u32 = 0; @@ -180,7 +262,7 @@ pub fn FXmacPhyInit( } } else { info!("Set the communication speed manually."); - assert!(speed != FXMAC_SPEED_1000, "The speed must be 100M or 10M!"); + assert!(speed != FXMAC_SPEED_1000, "The speed must be 100M or 10M!"); ret = FXmacConfigureIeeePhySpeed(instance_p, phy_addr, speed, duplex_mode); if ret != 0 { @@ -193,30 +275,32 @@ pub fn FXmacPhyInit( 0 //FT_SUCCESS } -pub fn FXmacDetect(instance_p: &mut FXmac, phy_addr_p: &mut u32) -> u32 -{ +pub fn FXmacDetect(instance_p: &mut FXmac, phy_addr_p: &mut u32) -> u32 { let mut phy_addr: u32 = 0; let mut phy_reg: u16 = 0; let mut phy_id1_reg: u16 = 0; let mut phy_id2_reg: u16 = 0; - for phy_addr in 0..FXMAC_PHY_MAX_NUM - { + for phy_addr in 0..FXMAC_PHY_MAX_NUM { let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &mut phy_reg); - if (ret != FT_SUCCESS) - { + if (ret != FT_SUCCESS) { error!("Phy operation is busy."); return ret; } info!("Phy status reg is {:#x}", phy_reg); - if (phy_reg != 0xffff) - { + if (phy_reg != 0xffff) { ret = FXmacPhyRead(instance_p, phy_addr, PHY_IDENTIFIER_1_REG, &mut phy_id1_reg); ret |= FXmacPhyRead(instance_p, phy_addr, PHY_IDENTIFIER_2_REG, &mut phy_id2_reg); - info!("Phy id1 reg is {:#x}, Phy id2 reg is {:#x}", phy_id1_reg , phy_id2_reg); - - if ((ret == FT_SUCCESS) && (phy_id2_reg != 0) && (phy_id2_reg != 0xffff) && (phy_id1_reg != 0xffff)) + info!( + "Phy id1 reg is {:#x}, Phy id2 reg is {:#x}", + phy_id1_reg, phy_id2_reg + ); + + if ((ret == FT_SUCCESS) + && (phy_id2_reg != 0) + && (phy_id2_reg != 0xffff) + && (phy_id1_reg != 0xffff)) { *phy_addr_p = phy_addr; //phy_addr_b = phy_addr; @@ -230,46 +314,39 @@ pub fn FXmacDetect(instance_p: &mut FXmac, phy_addr_p: &mut u32) -> u32 } /// FXmacPhyReset: Perform phy software reset -pub fn FXmacPhyReset(instance_p: &mut FXmac, phy_addr: u32) -> u32 - { +pub fn FXmacPhyReset(instance_p: &mut FXmac, phy_addr: u32) -> u32 { let mut control: u16 = 0; - - let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &mut control); - if (ret != FT_SUCCESS) - { - error!("FXmacPhyReset, read PHY_CONTROL_REG_OFFSET is error"); - return ret; - } - - control |= PHY_CONTROL_RESET_MASK; - - ret = FXmacPhyWrite(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, control); - if (ret != FT_SUCCESS) - { - error!("FXmacPhyReset, write PHY_CONTROL_REG_OFFSET is error"); - return ret; - } - - loop - { - ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &mut control); - if (ret != FT_SUCCESS) - { - error!("FXmacPhyReset, read PHY_CONTROL_REG_OFFSET is error"); - return ret; - } - if (control & PHY_CONTROL_RESET_MASK) == 0 { + + let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &mut control); + if (ret != FT_SUCCESS) { + error!("FXmacPhyReset, read PHY_CONTROL_REG_OFFSET is error"); + return ret; + } + + control |= PHY_CONTROL_RESET_MASK; + + ret = FXmacPhyWrite(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, control); + if (ret != FT_SUCCESS) { + error!("FXmacPhyReset, write PHY_CONTROL_REG_OFFSET is error"); + return ret; + } + + loop { + ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &mut control); + if (ret != FT_SUCCESS) { + error!("FXmacPhyReset, read PHY_CONTROL_REG_OFFSET is error"); + return ret; + } + if (control & PHY_CONTROL_RESET_MASK) == 0 { break; - } - } - - info!("Phy reset end."); - ret - } + } + } + info!("Phy reset end."); + ret +} -pub fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 -{ +pub fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 { let mut temp: u16 = 0; let mut temp2: u16 = 0; let mut control: u16 = 0; @@ -279,8 +356,7 @@ pub fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 info!("Start phy auto negotiation."); let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &mut control); - if (ret != FT_SUCCESS) - { + if (ret != FT_SUCCESS) { error!("FXmacGetIeeePhySpeed,read PHY_CONTROL_REG_OFFSET is error"); return ret; } @@ -288,79 +364,65 @@ pub fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 control |= PHY_CONTROL_AUTONEGOTIATE_ENABLE; control |= PHY_CONTROL_AUTONEGOTIATE_RESTART; ret = FXmacPhyWrite(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, control); - if (ret != FT_SUCCESS) - { + if (ret != FT_SUCCESS) { error!("FXmacGetIeeePhySpeed,write PHY_CONTROL_REG_OFFSET is error"); return ret; } info!("Waiting for phy to complete auto negotiation."); - loop{ + loop { // 睡眠50毫秒 crate::utils::msdelay(50); ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &mut status); - if (ret != FT_SUCCESS) - { + if (ret != FT_SUCCESS) { error!("FXmacGetIeeePhySpeed,read PHY_STATUS_REG_OFFSET is error"); return ret; } - negotitation_timeout_cnt += 1; - if (negotitation_timeout_cnt >= 0xff) - { + if (negotitation_timeout_cnt >= 0xff) { error!("Auto negotiation is error."); - return 8;//FXMAC_PHY_AUTO_AUTONEGOTIATION_FAILED; + return 8; //FXMAC_PHY_AUTO_AUTONEGOTIATION_FAILED; } if (status & PHY_STATUS_AUTONEGOTIATE_COMPLETE) != 0 { - break + break; } } - + info!("Auto negotiation complete."); ret = FXmacPhyRead(instance_p, phy_addr, PHY_SPECIFIC_STATUS_REG, &mut temp); - if (ret != FT_SUCCESS) - { + if (ret != FT_SUCCESS) { error!("FXmacGetIeeePhySpeed,read PHY_SPECIFIC_STATUS_REG is error"); return ret; } info!("Temp is {:#x}", temp); ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &mut temp2); - if (ret != FT_SUCCESS) - { + if (ret != FT_SUCCESS) { error!("FXmacGetIeeePhySpeed,read PHY_STATUS_REG_OFFSET is error"); return ret; } info!("Temp2 is {:#x}", temp2); - if (temp & (1 << 13)) != 0 - { + if (temp & (1 << 13)) != 0 { info!("Duplex is full."); instance_p.config.duplex = 1; - } - else - { + } else { info!("Duplex is half."); instance_p.config.duplex = 0; } - if (temp & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_1000M - { + if (temp & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_1000M { info!("Speed is 1000M."); instance_p.config.speed = 1000; - } - else if (temp & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_100M - { + } else if (temp & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_100M { info!("Speed is 100M."); instance_p.config.speed = 100; - } - else - { + } else { info!("Speed is 10M."); instance_p.config.speed = 10; } @@ -368,17 +430,28 @@ pub fn FXmacGetIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32) -> u32 FT_SUCCESS } -pub fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: u32, duplex_mode: u32) -> u32 -{ - let mut control: u16 = 0;; - let mut autonereg: u16 = 0;; +pub fn FXmacConfigureIeeePhySpeed( + instance_p: &mut FXmac, + phy_addr: u32, + speed: u32, + duplex_mode: u32, +) -> u32 { + let mut control: u16 = 0; + let mut autonereg: u16 = 0; let mut specific_reg: u16 = 0; - info!("Manual setting, phy_addr is {:#x},speed {}, duplex_mode is {}.", phy_addr, speed, duplex_mode); + info!( + "Manual setting, phy_addr is {:#x},speed {}, duplex_mode is {}.", + phy_addr, speed, duplex_mode + ); - let mut ret: u32 = FXmacPhyRead(instance_p, phy_addr, PHY_AUTONEGO_ADVERTISE_REG, &mut autonereg); - if (ret != FT_SUCCESS) - { + let mut ret: u32 = FXmacPhyRead( + instance_p, + phy_addr, + PHY_AUTONEGO_ADVERTISE_REG, + &mut autonereg, + ); + if (ret != FT_SUCCESS) { error!("FXmacConfigureIeeePhySpeed, read PHY_AUTONEGO_ADVERTISE_REG is error."); return ret; } @@ -386,15 +459,13 @@ pub fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: autonereg |= PHY_AUTOADVERTISE_ASYMMETRIC_PAUSE_MASK; autonereg |= PHY_AUTOADVERTISE_PAUSE_MASK; ret = FXmacPhyWrite(instance_p, phy_addr, PHY_AUTONEGO_ADVERTISE_REG, autonereg); - if (ret != FT_SUCCESS) - { + if (ret != FT_SUCCESS) { error!("FXmacConfigureIeeePhySpeed, write PHY_AUTONEGO_ADVERTISE_REG is error."); return ret; } ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &mut control); - if (ret != FT_SUCCESS) - { + if (ret != FT_SUCCESS) { error!("FXmacConfigureIeeePhySpeed, read PHY_AUTONEGO_ADVERTISE_REG is error."); return ret; } @@ -404,21 +475,15 @@ pub fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: control &= !PHY_CONTROL_LINKSPEED_100M; control &= !PHY_CONTROL_LINKSPEED_10M; - if speed == 100 - { + if speed == 100 { control |= PHY_CONTROL_LINKSPEED_100M; - } - else if speed == 10 - { + } else if speed == 10 { control |= PHY_CONTROL_LINKSPEED_10M; } - if duplex_mode == 1 - { + if duplex_mode == 1 { control |= PHY_CONTROL_FULL_DUPLEX_MASK; - } - else - { + } else { control &= !PHY_CONTROL_FULL_DUPLEX_MASK; } @@ -427,8 +492,7 @@ pub fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: control &= !PHY_CONTROL_AUTONEGOTIATE_RESTART; ret = FXmacPhyWrite(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, control); /* Technology Ability Field */ - if (ret != FT_SUCCESS) - { + if (ret != FT_SUCCESS) { error!("FXmacConfigureIeeePhySpeed, write PHY_AUTONEGO_ADVERTISE_REG is error."); return ret; } @@ -438,33 +502,31 @@ pub fn FXmacConfigureIeeePhySpeed(instance_p: &mut FXmac, phy_addr: u32, speed: info!("Manual selection completed."); - ret = FXmacPhyRead(instance_p, phy_addr, PHY_SPECIFIC_STATUS_REG, &mut specific_reg); - if (ret != FT_SUCCESS) - { + ret = FXmacPhyRead( + instance_p, + phy_addr, + PHY_SPECIFIC_STATUS_REG, + &mut specific_reg, + ); + if (ret != FT_SUCCESS) { error!("FXmacConfigureIeeePhySpeed, read PHY_SPECIFIC_STATUS_REG is error."); return ret; } info!("Specific reg is {:#x}", specific_reg); - if (specific_reg & (1 << 13)) != 0 - { + if (specific_reg & (1 << 13)) != 0 { info!("Duplex is full."); instance_p.config.duplex = 1; - } - else - { + } else { info!("Duplex is half."); instance_p.config.duplex = 0; } - if (specific_reg & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_100M - { + if (specific_reg & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_100M { info!("Speed is 100M."); instance_p.config.speed = 100; - } - else - { + } else { info!("Speed is 10M."); instance_p.config.speed = 10; } diff --git a/src/lib.rs b/src/lib.rs index 7c543d2f0..aa57ea6fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,95 @@ +//! # FXMAC Ethernet Driver +//! +//! A `no_std` Rust driver for the FXMAC Ethernet controller found on the PhytiumPi (Phytium Pi) board. +//! This driver supports DMA-based packet transmission and reception, providing a foundation for +//! network communication in embedded and bare-metal environments. +//! +//! ## Features +//! +//! - **DMA Support**: Efficient packet transmission and reception using DMA buffer descriptors. +//! - **PHY Management**: Support for PHY initialization, auto-negotiation, and manual speed configuration. +//! - **Interrupt Handling**: Built-in interrupt handlers for TX/RX completion and error conditions. +//! - **Multiple PHY Interfaces**: Support for SGMII, RGMII, RMII, XGMII, and other interface modes. +//! - **Configurable**: Supports jumbo frames, multicast filtering, and various MAC options. +//! +//! ## Target Platform +//! +//! This driver is designed for the aarch64 architecture, specifically targeting the PhytiumPi board +//! with the Motorcomm YT8521 PHY. +//! +//! ## Quick Start +//! +//! To use this driver, you need to implement the [`KernelFunc`] trait to provide the necessary +//! kernel functions for address translation and DMA memory allocation. +//! +//! ```ignore +//! use fxmac_rs::{KernelFunc, xmac_init, FXmacLwipPortTx, FXmacRecvHandler}; +//! +//! // Implement the KernelFunc trait for your platform +//! pub struct FXmacDriver; +//! +//! #[crate_interface::impl_interface] +//! impl KernelFunc for FXmacDriver { +//! fn virt_to_phys(addr: usize) -> usize { +//! // Your implementation +//! addr +//! } +//! +//! fn phys_to_virt(addr: usize) -> usize { +//! // Your implementation +//! addr +//! } +//! +//! fn dma_alloc_coherent(pages: usize) -> (usize, usize) { +//! // Your implementation: returns (virtual_addr, physical_addr) +//! unimplemented!() +//! } +//! +//! fn dma_free_coherent(vaddr: usize, pages: usize) { +//! // Your implementation +//! } +//! +//! fn dma_request_irq(irq: usize, handler: fn()) { +//! // Your implementation +//! } +//! } +//! +//! // Initialize the driver +//! let hwaddr: [u8; 6] = [0x55, 0x44, 0x33, 0x22, 0x11, 0x00]; +//! let fxmac = xmac_init(&hwaddr); +//! +//! // Send packets +//! let mut tx_vec = Vec::new(); +//! tx_vec.push(packet_data.to_vec()); +//! FXmacLwipPortTx(fxmac, tx_vec); +//! +//! // Receive packets +//! if let Some(recv_packets) = FXmacRecvHandler(fxmac) { +//! for packet in recv_packets { +//! // Process received packet +//! } +//! } +//! ``` +//! +//! ## Module Structure +//! +//! - [`fxmac`]: Core MAC controller functionality and configuration. +//! - [`fxmac_dma`]: DMA buffer descriptor management and packet handling. +//! - [`fxmac_intr`]: Interrupt handling and callback management. +//! - [`fxmac_phy`]: PHY initialization and management functions. +//! +//! ## Safety and Environment +//! +//! - This crate targets `no_std` and assumes the platform provides DMA-coherent +//! memory and interrupt routing. +//! - Most APIs interact with memory-mapped registers and should be used with +//! care in the correct execution context. +//! +//! ## Feature Flags +//! +//! - `debug`: Enable logging via the `log` crate. Without this feature, logging +//! macros become no-ops. + #![no_std] #![feature(linkage)] #![allow(unused)] @@ -34,41 +126,135 @@ mod log { //mod mii_const; mod fxmac_const; -mod utils; -mod fxmac_phy; +mod fxmac; mod fxmac_dma; mod fxmac_intr; -mod fxmac; +mod fxmac_phy; +mod utils; +// Re-exports for core MAC functionality pub use fxmac::*; +// Re-exports for DMA operations pub use fxmac_dma::*; -pub use fxmac_intr::{FXmacIntrHandler, xmac_intr_handler}; - -// PHY interface +// Re-exports for interrupt handling +pub use fxmac_intr::{xmac_intr_handler, FXmacIntrHandler}; +// Re-exports for PHY interface pub use fxmac_phy::{FXmacPhyInit, FXmacPhyRead, FXmacPhyWrite}; -/// 声明网卡驱动所需的内核功能接口 +/// Kernel function interface required by the FXMAC Ethernet driver. +/// +/// This trait defines the platform-specific functions that must be implemented +/// by the host system to support the FXMAC driver. These functions handle +/// address translation, DMA memory management, and interrupt registration. +/// +/// # Implementation Requirements +/// +/// All implementations must be `#[crate_interface::impl_interface]` compatible +/// and provide thread-safe operations where applicable. +/// +/// # Example +/// +/// ```ignore +/// pub struct MyPlatform; +/// +/// #[crate_interface::impl_interface] +/// impl fxmac_rs::KernelFunc for MyPlatform { +/// fn virt_to_phys(addr: usize) -> usize { +/// // Platform-specific virtual to physical address translation +/// addr - KERNEL_OFFSET +/// } +/// +/// fn phys_to_virt(addr: usize) -> usize { +/// // Platform-specific physical to virtual address translation +/// addr + KERNEL_OFFSET +/// } +/// +/// fn dma_alloc_coherent(pages: usize) -> (usize, usize) { +/// // Allocate DMA-capable coherent memory +/// // Returns (virtual_address, physical_address) +/// allocator.alloc_dma(pages) +/// } +/// +/// fn dma_free_coherent(vaddr: usize, pages: usize) { +/// // Free previously allocated DMA memory +/// allocator.free_dma(vaddr, pages) +/// } +/// +/// fn dma_request_irq(irq: usize, handler: fn()) { +/// // Register interrupt handler for the specified IRQ +/// interrupt_controller.register(irq, handler) +/// } +/// } +/// ``` #[crate_interface::def_interface] -pub trait KernelFunc{ - /// 虚拟地址转换成物理地址 +pub trait KernelFunc { + /// Converts a virtual address to its corresponding physical address. + /// + /// This function is used by the driver to obtain physical addresses for + /// DMA operations, as the hardware requires physical addresses for + /// buffer descriptors. + /// + /// # Arguments + /// + /// * `addr` - The virtual address to convert. + /// + /// # Returns + /// + /// The corresponding physical address. fn virt_to_phys(addr: usize) -> usize; - /// 物理地址转换成虚拟地址 + /// Converts a physical address to its corresponding virtual address. + /// + /// This function is used by the driver to access hardware registers + /// and DMA buffers through virtual addresses. + /// + /// # Arguments + /// + /// * `addr` - The physical address to convert. + /// + /// # Returns + /// + /// The corresponding virtual address. fn phys_to_virt(addr: usize) -> usize; - /// 申请DMA连续内存页 + /// Allocates DMA-coherent memory pages. + /// + /// Allocates physically contiguous memory that is suitable for DMA + /// operations. The memory should be cache-coherent or properly managed + /// for DMA access. + /// + /// # Arguments + /// + /// * `pages` - The number of pages (typically 4KB each) to allocate. + /// + /// # Returns + /// + /// A tuple containing `(virtual_address, physical_address)` of the + /// allocated memory region. fn dma_alloc_coherent(pages: usize) -> (usize, usize); - /// 释放DMA内存页 + /// Frees previously allocated DMA-coherent memory. + /// + /// # Arguments + /// + /// * `vaddr` - The virtual address of the memory region to free. + /// * `pages` - The number of pages to free. fn dma_free_coherent(vaddr: usize, pages: usize); - /// 请求分配irq + /// Registers an interrupt handler for DMA/network interrupts. + /// + /// This function should configure the interrupt controller to route + /// the specified IRQ to the provided handler function. + /// + /// # Arguments + /// + /// * `irq` - The IRQ number to register. + /// * `handler` - The interrupt handler function to call when the IRQ fires. fn dma_request_irq(irq: usize, handler: fn()); } #[cfg(test)] mod tests { #[test] - fn it_works() { - } + fn it_works() {} } diff --git a/src/utils.rs b/src/utils.rs index 75668b2fb..c0ff5cf8e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,8 @@ +//! Architecture helpers for FXMAC on supported targets. +//! +//! This module provides low-level helpers (CPU ID, barriers, cache ops) used by +//! the driver on aarch64 platforms. + #[cfg(target_arch = "aarch64")] mod arch { use core::arch::asm; @@ -127,7 +132,7 @@ mod arch { MTCPSR(currmask); } - use aarch64_cpu::registers::{CNTFRQ_EL0, CNTVCT_EL0, Readable}; + use aarch64_cpu::registers::{Readable, CNTFRQ_EL0, CNTVCT_EL0}; #[inline] pub fn now_tsc() -> u64 { @@ -254,4 +259,4 @@ pub(crate) fn dma_request_irq(irq: usize, handler: fn()) { } // 路由中断到指定的cpu,或所有的cpu -//pub(crate) fn InterruptSetTargetCpus() {} \ No newline at end of file +//pub(crate) fn InterruptSetTargetCpus() {} From 8dfca0ea6c8af4f7fc4e9f1369bfcd1eb80b7144 Mon Sep 17 00:00:00 2001 From: DeathWish5 Date: Wed, 28 Jan 2026 22:16:45 +0800 Subject: [PATCH 038/132] ci: add GitHub Actions for quality checks and doc deployment - check.yaml: reusable workflow for code format, build, clippy, and doc checks - deploy.yaml: build and deploy documentation to GitHub Pages - fix clippy warnings in lib.rs and utils.rs --- .github/workflows/check.yaml | 39 ++++++++++++++++++++++ .github/workflows/deploy.yaml | 63 +++++++++++++++++++++++++++++++++++ src/lib.rs | 10 +++--- src/utils.rs | 6 ++-- 4 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/check.yaml create mode 100644 .github/workflows/deploy.yaml diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml new file mode 100644 index 000000000..17419001f --- /dev/null +++ b/.github/workflows/check.yaml @@ -0,0 +1,39 @@ +name: Quality Checks + +on: + workflow_call: + +jobs: + check: + name: Quality Checks + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + targets: [aarch64-unknown-none-softfloat, aarch64-unknown-none] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2025-01-18 + components: rust-src, clippy, rustfmt + targets: ${{ matrix.targets }} + + - name: Check rust version + run: rustc --version --verbose + + - name: Check code format + run: cargo fmt --all -- --check + + - name: Build + run: cargo build --target ${{ matrix.targets }} + + - name: Run clippy + run: cargo clippy --target ${{ matrix.targets }} -- -D warnings + + - name: Build documentation + run: cargo doc --no-deps --target ${{ matrix.targets }} diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 000000000..cef44929f --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,63 @@ +name: Deploy + +on: + push: + branches: + - '**' + tags-ignore: + - 'v*' + - 'v*-pre.*' + pull_request: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +jobs: + quality-check: + uses: ./.github/workflows/check.yaml + + build-doc: + name: Build documentation + runs-on: ubuntu-latest + needs: quality-check + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2025-01-18 + components: rust-src + targets: aarch64-unknown-none-softfloat + + - name: Build docs + run: cargo doc --no-deps --target aarch64-unknown-none-softfloat + + - name: Create index redirect + run: | + printf '' > target/aarch64-unknown-none-softfloat/doc/index.html + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: target/aarch64-unknown-none-softfloat/doc + + deploy-doc: + name: Deploy to GitHub Pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build-doc + if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/src/lib.rs b/src/lib.rs index aa57ea6fa..7bfad147c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,19 +107,19 @@ extern crate log; #[macro_use] mod log { macro_rules! trace { - ($($arg:expr),*) => { $( let _ = $arg; )* }; + ($($arg:tt)*) => {}; } macro_rules! debug { - ($($arg:expr),*) => { $( let _ = $arg; )* }; + ($($arg:tt)*) => {}; } macro_rules! info { - ($($arg:expr),*) => { $( let _ = $arg; )*}; + ($($arg:tt)*) => {}; } macro_rules! warn { - ($($arg:expr),*) => { $( let _ = $arg; )*}; + ($($arg:tt)*) => {}; } macro_rules! error { - ($($arg:expr),*) => { $( let _ = $arg; )* }; + ($($arg:tt)*) => {}; } } diff --git a/src/utils.rs b/src/utils.rs index c0ff5cf8e..a7ddfcea3 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -99,7 +99,7 @@ mod arch { /// len: Length of the range to be invalidated in bytes. pub(crate) fn FCacheDCacheInvalidateRange(mut adr: u64, len: u64) { let end: u64 = adr + len; - adr = adr & (!CACHE_LINE_ADDR_MASK); + adr &= !CACHE_LINE_ADDR_MASK; let currmask: u32 = MFCPSR(); MTCPSR(currmask | IRQ_FIQ_MASK); if (len != 0) { @@ -118,7 +118,7 @@ mod arch { /// adr: 64bit start address of the range to be flush. pub(crate) fn FCacheDCacheFlushRange(mut adr: u64, len: u64) { let end: u64 = adr + len; - adr = adr & (!CACHE_LINE_ADDR_MASK); + adr &= !CACHE_LINE_ADDR_MASK; let currmask: u32 = MFCPSR(); MTCPSR(currmask | IRQ_FIQ_MASK); if len != 0 { @@ -141,7 +141,7 @@ mod arch { #[inline] pub fn timer_freq() -> u64 { - CNTFRQ_EL0.get() as u64 + CNTFRQ_EL0.get() } } From 22fdaea7fa4ee5473a390ca796919cf04394358c Mon Sep 17 00:00:00 2001 From: hky1999 <976929993@qq.com> Date: Fri, 30 Jan 2026 13:42:14 +0800 Subject: [PATCH 039/132] [feat] refactor according to rules and prepare to release (#8) * [feat] refactor according to rules and prepare to release * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * [feat] modify github workflows --------- Co-authored-by: Su Mingxian Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/check.yml | 45 +++++++++ .github/workflows/ci.yml | 57 ----------- .github/workflows/deploy.yml | 68 +++++++++++++ .github/workflows/release.yml | 174 ++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 23 +++++ CHANGELOG.md | 38 ++++++++ Cargo.toml | 30 ++++-- README.md | 5 +- axvisor_api_proc/Cargo.toml | 11 ++- axvisor_api_proc/src/lib.rs | 138 +++++++++++++++++++++++++-- src/arch.rs | 109 +++++++++++++++++++-- src/host.rs | 51 +++++++++- src/memory.rs | 160 ++++++++++++++++++++++++++++++- src/time.rs | 174 +++++++++++++++++++++++++++++++--- src/vmm.rs | 168 +++++++++++++++++++++++++++++--- 15 files changed, 1136 insertions(+), 115 deletions(-) create mode 100644 .github/workflows/check.yml delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 CHANGELOG.md diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 000000000..a6ee3b08e --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,45 @@ +name: Quality Checks + +on: + workflow_call: + +jobs: + check: + name: Quality Checks + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - x86_64-unknown-none + - riscv64gc-unknown-none-elf + - aarch64-unknown-none-softfloat + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + components: rust-src, clippy, rustfmt + targets: ${{ matrix.target }} + + - name: Check rust version + run: rustc --version --verbose + + - name: Check code format + run: cargo fmt --all -- --check + + - name: Build + run: cargo build --target ${{ matrix.target }} --all-features + + - name: Run clippy + run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings -A clippy::new_without_default + + - name: Build documentation + env: + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + run: cargo doc --no-deps --target ${{ matrix.target }} --all-features diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 5d66dd55f..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: CI - -on: [push, pull_request] - -jobs: - ci: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - rust-toolchain: [nightly-2025-05-20, nightly] - targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat] - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: ${{ matrix.rust-toolchain }} - components: rust-src, clippy, rustfmt - targets: ${{ matrix.targets }} - - name: Check rust version - run: rustc --version --verbose - - name: Check code format - run: cargo fmt --all -- --check - - name: Clippy - run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default - - name: Build - run: cargo build --target ${{ matrix.targets }} --all-features - - name: Unit test - if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} - run: cargo test --target ${{ matrix.targets }} -- --nocapture - - doc: - runs-on: ubuntu-latest - strategy: - fail-fast: false - permissions: - contents: write - env: - default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: nightly-2025-05-20 - - name: Build docs - continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - name: Deploy to Github Pages - if: ${{ github.ref == env.default-branch }} - uses: JamesIves/github-pages-deploy-action@v4 - with: - single-commit: true - branch: gh-pages - folder: target/doc diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..d43864156 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,68 @@ +name: Deploy + +on: + push: + branches: + - '**' + tags-ignore: + - 'v*' + - 'v*-pre.*' + pull_request: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + quality-check: + uses: ./.github/workflows/check.yml + + test: + uses: ./.github/workflows/test.yml + + build-doc: + name: Build documentation + runs-on: ubuntu-latest + needs: quality-check + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + + - name: Build docs + env: + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + run: | + cargo doc --no-deps --all-features + printf '' > target/doc/index.html + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: target/doc + + deploy-doc: + name: Deploy to GitHub Pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build-doc + if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..c92d84fa6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,174 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + - 'v*.*.*-pre.*' + +permissions: + contents: write + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + +jobs: + check: + uses: ./.github/workflows/check.yml + + test: + uses: ./.github/workflows/test.yml + needs: check + + create-release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: check + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate tag and branch (HEAD-based) + shell: bash + run: | + set -e + + TAG="${{ github.ref_name }}" + TAG_COMMIT=$(git rev-list -n 1 "$TAG") + + git fetch origin main dev + + MAIN_HEAD=$(git rev-parse origin/main) + DEV_HEAD=$(git rev-parse origin/dev) + + echo "Tag: $TAG" + echo "Tag commit: $TAG_COMMIT" + echo "main HEAD: $MAIN_HEAD" + echo "dev HEAD: $DEV_HEAD" + + if [[ "$TAG" == *-pre.* ]]; then + if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then + echo "❌ prerelease tag must be created from dev HEAD" + exit 1 + fi + echo "✅ prerelease tag validated on dev" + else + if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then + echo "❌ stable release tag must be created from main HEAD" + exit 1 + fi + echo "✅ stable release tag validated on main" + fi + + - name: Verify version consistency + run: | + # Extract version from git tag (remove 'v' prefix) + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + # Extract version from Cargo.toml using cargo metadata + CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version') + echo "Git tag version: $TAG_VERSION" + echo "Cargo.toml version: $CARGO_VERSION" + if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then + echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" + exit 1 + fi + echo "Version check passed!" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + draft: false + prerelease: ${{ contains(github.ref_name, '-pre.') }} + body: | + ## ${{ github.ref_name }} + + - [Documentation](https://docs.rs/axvisor_api) + - [crates.io](https://crates.io/crates/axvisor_api) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-crates: + name: Publish to crates.io + runs-on: ubuntu-latest + needs: check + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate tag and branch (HEAD-based) + shell: bash + run: | + set -e + + TAG="${{ github.ref_name }}" + TAG_COMMIT=$(git rev-list -n 1 "$TAG") + + git fetch origin main dev + + MAIN_HEAD=$(git rev-parse origin/main) + DEV_HEAD=$(git rev-parse origin/dev) + + echo "Tag: $TAG" + echo "Tag commit: $TAG_COMMIT" + echo "main HEAD: $MAIN_HEAD" + echo "dev HEAD: $DEV_HEAD" + + if [[ "$TAG" == *-pre.* ]]; then + if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then + echo "❌ prerelease tag must be created from dev HEAD" + exit 1 + fi + echo "✅ prerelease tag validated on dev" + else + if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then + echo "❌ stable release tag must be created from main HEAD" + exit 1 + fi + echo "✅ stable release tag validated on main" + fi + + - name: Verify version consistency + run: | + # Extract version from git tag (remove 'v' prefix) + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + # Extract version from Cargo.toml using cargo metadata + CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version') + echo "Git tag version: $TAG_VERSION" + echo "Cargo.toml version: $CARGO_VERSION" + if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then + echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" + exit 1 + fi + echo "Version check passed!" + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + + # Workspace crates must be published in dependency order + - name: Dry run publish (axvisor_api_proc) + run: cargo publish --dry-run -p axvisor_api_proc + + - name: Dry run publish (axvisor_api) + run: cargo publish --dry-run -p axvisor_api + + - name: Publish axvisor_api_proc to crates.io + run: cargo publish -p axvisor_api_proc --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + + # Wait for crates.io to index the proc-macro crate + - name: Wait for crates.io indexing + run: sleep 30 + + - name: Publish axvisor_api to crates.io + run: cargo publish -p axvisor_api --token ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..d8abfaf8a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Test + +on: + workflow_call: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + + - name: Run tests + run: cargo test --all-features -- --nocapture + + - name: Run doc tests + run: cargo test --doc --all-features diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..c6e8c0022 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.2.0] - 2026-01-24 + +### Added + +- Added `api_def` and `api_impl` procedural macros for defining and implementing APIs. +- Added `arch` module with architecture-specific APIs for AArch64 GIC operations. +- Added `host` module with host system APIs. +- Added `memory` module with memory allocation and address translation APIs. +- Added `time` module with time and timer APIs. +- Added `vmm` module with virtual machine management APIs. +- Added `PhysFrame` type alias for automatic frame deallocation. +- Added comprehensive documentation and examples in crate-level docs. +- Added docs.rs configuration for multi-target documentation. + +### Changed + +- Improved Cargo.toml with complete metadata fields. +- Enhanced CI configuration with documentation checks. + +## [0.1.0] - Initial Release + +### Added + +- Initial implementation of the axvisor_api crate. +- Basic API definition and implementation framework using `crate_interface`. + +[Unreleased]: https://github.com/arceos-hypervisor/axvisor_api/compare/v0.2.0...HEAD +[0.2.0]: https://github.com/arceos-hypervisor/axvisor_api/compare/v0.1.0...v0.2.0 +[0.1.0]: https://github.com/arceos-hypervisor/axvisor_api/releases/tag/v0.1.0 diff --git a/Cargo.toml b/Cargo.toml index b21e2f67c..a7ae5fb81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,6 @@ [workspace] resolver = "2" -members = [ - "axvisor_api_proc", -] +members = ["axvisor_api_proc"] [workspace.package] authors = ["Su Mingxian "] @@ -12,9 +10,11 @@ repository = "https://github.com/arceos-hypervisor/axvisor_api" [package] name = "axvisor_api" -description = "Basic API for components of the Hypervisor on ArceOS" version = "0.2.0" -keywords = ["axvisor", "api", "embedded"] +description = "Basic API for components of the Hypervisor on ArceOS" +documentation = "https://docs.rs/axvisor_api" +readme = "README.md" +keywords = ["axvisor", "api", "embedded", "hypervisor"] categories = ["embedded", "no-std"] authors.workspace = true edition.workspace = true @@ -22,8 +22,24 @@ license.workspace = true repository.workspace = true [dependencies] -axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0"} +# === Core Components === +# Procedural macro definitions +axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0" } +# === Third-party Libraries === +# Address space abstraction +axaddrspace = "0.1.0" +# Interface definition tools crate_interface = "0.2" +# Physical/virtual address types memory_addr = "0.4" -axaddrspace = "0.1.0" + +[package.metadata.docs.rs] +all-features = true +default-target = "x86_64-unknown-linux-gnu" +targets = [ + "x86_64-unknown-linux-gnu", + "x86_64-unknown-none", + "riscv64gc-unknown-none-elf", + "aarch64-unknown-none-softfloat", +] diff --git a/README.md b/README.md index 05303f124..a3494b4aa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # axvisor\_api (Experimental Next-Generation Axvisor API) + [![CI](https://github.com/arceos-hypervisor/axvisor_api/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/arceos-hypervisor/axvisor_api/actions/workflows/ci.yml) -[![License](https://img.shields.io/badge/License-MIT%20OR%20Apache--2.0-blue.svg)] +[![Crates.io](https://img.shields.io/crates/v/axvisor_api)](https://crates.io/crates/axvisor_api) +[![Docs.rs](https://docs.rs/axvisor_api/badge.svg)](https://docs.rs/axvisor_api) +[![License](https://img.shields.io/badge/License-GPL--3.0--or--later%20OR%20Apache--2.0%20OR%20MulanPSL--2.0-blue.svg)](LICENSE.Apache2) \> [中文README](README.zh-cn.md) < diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml index f080e7672..2d05c2b6d 100644 --- a/axvisor_api_proc/Cargo.toml +++ b/axvisor_api_proc/Cargo.toml @@ -2,6 +2,8 @@ name = "axvisor_api_proc" version = "0.2.0" description = "Procedural macros for the `axvisor_api` crate" +documentation = "https://docs.rs/axvisor_api_proc" +readme = "README.md" keywords = ["axvisor", "api", "embedded", "macros"] categories = ["development-tools::procedural-macro-helpers"] authors.workspace = true @@ -9,12 +11,15 @@ edition.workspace = true license.workspace = true repository.workspace = true - [lib] proc-macro = true [dependencies] -syn = { version = "2.0", features = ["full"] } -quote = "1.0" +# Procedural macro helper utilities proc-macro2 = "1.0" +# Crate name resolution proc-macro-crate = "3.1.0" +# Code generation +quote = "1.0" +# Rust syntax parsing +syn = { version = "2.0", features = ["full"] } diff --git a/axvisor_api_proc/src/lib.rs b/axvisor_api_proc/src/lib.rs index b8f1c9080..3b1a8751b 100644 --- a/axvisor_api_proc/src/lib.rs +++ b/axvisor_api_proc/src/lib.rs @@ -1,3 +1,45 @@ +//! Procedural macros for the `axvisor_api` crate. +//! +//! This crate provides the procedural macros used to define and implement +//! AxVisor API interfaces. These macros are built on top of the +//! `crate_interface` crate and provide a convenient way to create +//! link-time-resolved API interfaces. +//! +//! # Macros +//! +//! - [`api_def`] - Define an API interface trait. +//! - [`api_impl`] - Implement an API interface. +//! +//! # Usage +//! +//! This crate is re-exported by `axvisor_api` and should not be used directly. +//! Instead, use the macros through `axvisor_api`: +//! +//! ```rust,ignore +//! use axvisor_api::{api_def, api_impl}; +//! +//! #[api_def] +//! pub trait MyApiIf { +//! fn my_function() -> u32; +//! } +//! +//! struct MyApiImpl; +//! +//! #[api_impl] +//! impl MyApiIf for MyApiImpl { +//! fn my_function() -> u32 { +//! 42 +//! } +//! } +//! ``` +//! +//! # How It Works +//! +//! The macros use `crate_interface` under the hood, which leverages Rust's +//! link-time symbol resolution to connect API definitions with their +//! implementations. This allows for a cleaner API without explicit generic +//! parameters. + use proc_macro::TokenStream as TokenStream1; use proc_macro_crate::{FoundCrate, crate_name}; use proc_macro2::{Span, TokenStream}; @@ -5,6 +47,11 @@ use quote::{quote, quote_spanned}; use syn::{Ident, spanned::Spanned}; /// Find the path to the `axvisor_api` crate. +/// +/// This function determines the correct path to use when referring to +/// `axvisor_api` from within the generated code, handling both the case +/// where we're inside the `axvisor_api` crate itself and when we're in +/// an external crate. fn axvisor_api_crate() -> TokenStream { match crate_name("axvisor_api") { Ok(FoundCrate::Itself) => quote! { crate }, @@ -16,29 +63,68 @@ fn axvisor_api_crate() -> TokenStream { } } -/// The namespace used for AxVisor APIs when calling `crate_interface` macros. +/// Get the namespace identifier used for AxVisor APIs. +/// +/// All AxVisor APIs share a common namespace to avoid conflicts with other +/// uses of `crate_interface`. fn axvisor_api_namespace() -> Ident { const AXVISOR_API_NS: &str = "AxVisorApi"; Ident::new(AXVISOR_API_NS, Span::call_site()) } +/// Macro to assert that an attribute has no arguments. macro_rules! assert_empty_attr { ($attr:expr) => { if !$attr.is_empty() { return (quote_spanned! { TokenStream::from($attr).span() => compile_error!("This attribute does not accept any arguments") - }).into(); + }) + .into(); } }; } /// Define an AxVisor API interface. /// -/// This macro is applied to a trait definition. It generates the necessary -/// boilerplate code to register the trait as an AxVisor API interface. No -/// arguments are accepted. +/// This attribute macro is applied to a trait definition to register it as +/// an AxVisor API interface. It generates caller functions for each method +/// in the trait, allowing the API to be called as regular functions. +/// +/// # Usage +/// +/// ```rust,ignore +/// use axvisor_api::api_def; +/// +/// #[api_def] +/// pub trait MyApiIf { +/// /// Get a value. +/// fn get_value() -> u32; +/// +/// /// Set a value. +/// fn set_value(value: u32); +/// } +/// +/// // After the macro expansion, you can call: +/// // my_module::get_value() +/// // my_module::set_value(42) +/// ``` +/// +/// # Generated Code +/// +/// The macro generates: +/// 1. The original trait definition with `crate_interface::def_interface` +/// attribute. +/// 2. Free-standing caller functions for each trait method at the same +/// module level. +/// +/// # Attributes +/// +/// This macro does not accept any arguments. /// -/// This macro uses `crate_interface::def_interface` internally. +/// # Implementation +/// +/// This macro uses `crate_interface::def_interface` internally with the +/// `gen_caller` option to generate the caller functions. #[proc_macro_attribute] pub fn api_def(attr: TokenStream1, input: TokenStream1) -> TokenStream1 { assert_empty_attr!(attr); @@ -56,10 +142,42 @@ pub fn api_def(attr: TokenStream1, input: TokenStream1) -> TokenStream1 { /// Implement an AxVisor API interface. /// -/// This macro is applied to an `impl` block that implements a trait previously -/// defined with `api_def`. It generates the necessary boilerplate code to -/// register the implementation as an AxVisor API implementation. No arguments -/// are accepted. +/// This attribute macro is applied to an `impl` block that implements a +/// trait previously defined with [`api_def`]. It registers the implementation +/// so that calls to the API functions are resolved to this implementation +/// at link time. +/// +/// # Usage +/// +/// ```rust,ignore +/// use axvisor_api::{api_def, api_impl}; +/// +/// #[api_def] +/// pub trait MyApiIf { +/// fn get_value() -> u32; +/// } +/// +/// struct MyApiImpl; +/// +/// #[api_impl] +/// impl MyApiIf for MyApiImpl { +/// fn get_value() -> u32 { +/// 42 +/// } +/// } +/// ``` +/// +/// # Requirements +/// +/// - The implemented trait must have been defined with [`api_def`]. +/// - The implementing type should be an empty struct (marker type). +/// - Only one implementation per API trait is allowed in the final binary. +/// +/// # Attributes +/// +/// This macro does not accept any arguments. +/// +/// # Implementation /// /// This macro uses `crate_interface::impl_interface` internally. #[proc_macro_attribute] diff --git a/src/arch.rs b/src/arch.rs index c11b030e7..4332142ea 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -1,25 +1,122 @@ -//! Architecture-specific APIs. +//! Architecture-specific APIs for the AxVisor hypervisor. +//! +//! This module provides APIs that are specific to certain CPU architectures. +//! Currently, it mainly contains AArch64 GIC (Generic Interrupt Controller) +//! related operations. +//! +//! # Supported Architectures +//! +//! - **AArch64**: GIC distributor and redistributor operations for virtual +//! interrupt injection and GIC initialization. +//! +//! # Usage +//! +//! The API functions in this module are conditionally compiled based on the +//! target architecture. On non-AArch64 platforms, this module is essentially +//! empty. +//! +//! # Implementation +//! +//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute +//! macro on an impl block: +//! +//! ```rust,ignore +//! struct ArchIfImpl; +//! +//! #[axvisor_api::api_impl] +//! impl axvisor_api::arch::ArchIf for ArchIfImpl { +//! // Implement the required functions... +//! } +//! ``` use super::{memory::PhysAddr, vmm::InterruptVector}; /// The API trait for architecture-specific functionalities. +/// +/// This trait defines the interface for architecture-specific operations +/// required by the hypervisor. Implementations should be provided by the +/// host system or HAL layer. #[crate::api_def] pub trait ArchIf { - /// Inject a virtual interrupt to the current virtual CPU. + /// Inject a virtual interrupt to the current virtual CPU using hardware + /// virtualization support. + /// + /// This function uses the GIC virtualization interface to directly inject + /// an interrupt into the guest without causing a VM exit. + /// + /// # Arguments + /// + /// * `vector` - The interrupt vector number to inject. + /// + /// # Platform Support + /// + /// This function is only available on AArch64 platforms with GICv2/v3 + /// virtualization extensions. #[cfg(target_arch = "aarch64")] fn hardware_inject_virtual_interrupt(vector: InterruptVector); - /// Get the TYPER register of the GIC distributor. Used in virtual GIC initialization. + /// Read the TYPER (Type Register) of the GIC distributor. + /// + /// The TYPER register provides information about the GIC implementation, + /// including the maximum number of SPIs supported and the number of + /// implemented CPU interfaces. + /// + /// # Returns + /// + /// The 32-bit value of the GICD_TYPER register. + /// + /// # Platform Support + /// + /// This function is only available on AArch64 platforms. #[cfg(target_arch = "aarch64")] fn read_vgicd_typer() -> u32; - /// Get the IIDR register of the GIC distributor. Used in virtual GIC initialization. + + /// Read the IIDR (Implementer Identification Register) of the GIC + /// distributor. + /// + /// The IIDR register provides identification information about the GIC + /// implementation, including the implementer, revision, and variant. + /// + /// # Returns + /// + /// The 32-bit value of the GICD_IIDR register. + /// + /// # Platform Support + /// + /// This function is only available on AArch64 platforms. #[cfg(target_arch = "aarch64")] fn read_vgicd_iidr() -> u32; - /// Get the base address of the GIC distributor in the host system. + /// Get the base physical address of the GIC distributor in the host system. + /// + /// The GIC distributor is responsible for interrupt prioritization and + /// distribution to CPU interfaces. + /// + /// # Returns + /// + /// The physical address of the GICD (GIC Distributor) registers. + /// + /// # Platform Support + /// + /// This function is only available on AArch64 platforms. #[cfg(target_arch = "aarch64")] fn get_host_gicd_base() -> PhysAddr; - /// Get the base address of the GIC redistributor in the host system. + + /// Get the base physical address of the GIC redistributor in the host + /// system. + /// + /// The GIC redistributor (GICv3+) handles per-PE (Processing Element) + /// interrupt configuration and provides the LPI (Locality-specific + /// Peripheral Interrupt) configuration interface. + /// + /// # Returns + /// + /// The physical address of the GICR (GIC Redistributor) registers. + /// + /// # Platform Support + /// + /// This function is only available on AArch64 platforms with GICv3 or + /// later. #[cfg(target_arch = "aarch64")] fn get_host_gicr_base() -> PhysAddr; } diff --git a/src/host.rs b/src/host.rs index 5d27eabc1..cee440c5c 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,8 +1,55 @@ -//! Host system related APIs. +//! Host system related APIs for the AxVisor hypervisor. +//! +//! This module provides APIs for querying information about the host system +//! on which the hypervisor is running. +//! +//! # Overview +//! +//! The host system APIs provide essential information about the underlying +//! hardware that the hypervisor needs to manage virtual machines effectively. +//! +//! # Available APIs +//! +//! - [`get_host_cpu_num`] - Get the total number of CPUs in the host system. +//! +//! # Implementation +//! +//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute +//! macro on an impl block: +//! +//! ```rust,ignore +//! struct HostIfImpl; +//! +//! #[axvisor_api::api_impl] +//! impl axvisor_api::host::HostIf for HostIfImpl { +//! fn get_host_cpu_num() -> usize { +//! // Return the number of CPUs from your platform +//! 4 +//! } +//! } +//! ``` /// The API trait for host system functionalities. +/// +/// This trait defines the interface for querying host system information. +/// Implementations should be provided by the host system or HAL layer. #[crate::api_def] pub trait HostIf { - /// Get the total number of cpus in the host system. + /// Get the total number of CPUs (logical processors) in the host system. + /// + /// This function returns the number of CPUs available to the hypervisor, + /// which is typically the same as the number of physical or logical + /// processors in the system. + /// + /// # Returns + /// + /// The number of CPUs in the host system. + /// + /// # Example + /// + /// ```rust,ignore + /// let cpu_count = axvisor_api::host::get_host_cpu_num(); + /// println!("Host has {} CPUs", cpu_count); + /// ``` fn get_host_cpu_num() -> usize; } diff --git a/src/memory.rs b/src/memory.rs index cc196821f..9d74bf3a1 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,26 +1,160 @@ -//! Memory allocation and address translation APIs. +//! Memory allocation and address translation APIs for the AxVisor hypervisor. +//! +//! This module provides APIs for physical memory management, including frame +//! allocation/deallocation and physical-virtual address translation. +//! +//! # Overview +//! +//! The memory APIs are fundamental to the hypervisor's operation, enabling: +//! - Physical frame allocation for guest memory +//! - Contiguous frame allocation for DMA and other hardware requirements +//! - Address translation between physical and virtual addresses +//! +//! # Re-exports +//! +//! This module re-exports [`PhysAddr`] and [`VirtAddr`] from the `memory_addr` +//! crate for convenience. +//! +//! # Types +//! +//! - [`PhysFrame`] - A physical frame that is automatically deallocated when +//! dropped. +//! +//! # Implementation +//! +//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute +//! macro on an impl block: +//! +//! ```rust,ignore +//! struct MemoryIfImpl; +//! +//! #[axvisor_api::api_impl] +//! impl axvisor_api::memory::MemoryIf for MemoryIfImpl { +//! fn alloc_frame() -> Option { +//! // Allocate a physical frame from your allocator +//! } +//! // ... implement other functions +//! } +//! ``` pub use memory_addr::{PhysAddr, VirtAddr}; /// The API trait for memory allocation and address translation functionalities. +/// +/// This trait defines the core memory management interface required by the +/// hypervisor. Implementations should be provided by the host system or HAL +/// layer. #[crate::api_def] pub trait MemoryIf { - /// Allocate a frame. + /// Allocate a single physical frame (4KB page). + /// + /// # Returns + /// + /// - `Some(PhysAddr)` - The physical address of the allocated frame. + /// - `None` - If allocation fails (e.g., out of memory). + /// + /// # Example + /// + /// ```rust,ignore + /// use axvisor_api::memory::{alloc_frame, dealloc_frame}; + /// + /// if let Some(frame) = alloc_frame() { + /// // Use the frame... + /// dealloc_frame(frame); + /// } + /// ``` fn alloc_frame() -> Option; - /// Allocate a number of contiguous frames, with a specified alignment. + + /// Allocate a number of contiguous physical frames with a specified + /// alignment. + /// + /// This function is useful for allocating memory for DMA buffers or other + /// hardware that requires contiguous physical memory. + /// + /// # Arguments + /// + /// * `num_frames` - The number of contiguous frames to allocate. + /// * `frame_align_pow2` - The alignment requirement as a power of 2 + /// (e.g., 0 for 4KB alignment, 1 for 8KB alignment). + /// + /// # Returns + /// + /// - `Some(PhysAddr)` - The physical address of the first allocated frame. + /// - `None` - If allocation fails. fn alloc_contiguous_frames(num_frames: usize, frame_align_pow2: usize) -> Option; - /// Deallocate a frame allocated previously by [`alloc_frame`]. + + /// Deallocate a frame previously allocated by [`alloc_frame`]. + /// + /// # Arguments + /// + /// * `addr` - The physical address of the frame to deallocate. + /// + /// # Safety + /// + /// The caller must ensure that: + /// - The address was previously returned by [`alloc_frame`]. + /// - The frame has not been deallocated yet. + /// - No references to the frame's memory exist after deallocation. fn dealloc_frame(addr: PhysAddr); - /// Deallocate a number of contiguous frames allocated previously by + + /// Deallocate contiguous frames previously allocated by /// [`alloc_contiguous_frames`]. + /// + /// # Arguments + /// + /// * `first_addr` - The physical address of the first frame. + /// * `num_frames` - The number of frames to deallocate. + /// + /// # Safety + /// + /// The caller must ensure that: + /// - The address and count match a previous [`alloc_contiguous_frames`] + /// call. + /// - The frames have not been deallocated yet. + /// - No references to the frames' memory exist after deallocation. fn dealloc_contiguous_frames(first_addr: PhysAddr, num_frames: usize); + /// Convert a physical address to a virtual address. + /// + /// This function performs the physical-to-virtual address translation + /// based on the host's memory mapping. + /// + /// # Arguments + /// + /// * `addr` - The physical address to convert. + /// + /// # Returns + /// + /// The corresponding virtual address. + /// + /// # Panics + /// + /// May panic if the physical address is not mapped. fn phys_to_virt(addr: PhysAddr) -> VirtAddr; + /// Convert a virtual address to a physical address. + /// + /// This function performs the virtual-to-physical address translation + /// based on the host's memory mapping. + /// + /// # Arguments + /// + /// * `addr` - The virtual address to convert. + /// + /// # Returns + /// + /// The corresponding physical address. + /// + /// # Panics + /// + /// May panic if the virtual address is not mapped. fn virt_to_phys(addr: VirtAddr) -> PhysAddr; } /// [`AxMmHal`](axaddrspace::AxMmHal) implementation by axvisor_api. +/// +/// This struct provides an implementation of the `AxMmHal` trait from the +/// `axaddrspace` crate, delegating to the axvisor_api memory functions. #[doc(hidden)] pub struct AxMmHalApiImpl; @@ -43,4 +177,20 @@ impl axaddrspace::AxMmHal for AxMmHalApiImpl { } /// A physical frame which will be automatically deallocated when dropped. +/// +/// This type alias provides a convenient RAII wrapper around physical frame +/// allocation. When a `PhysFrame` is dropped, it automatically deallocates +/// the underlying physical memory. +/// +/// # Example +/// +/// ```rust,ignore +/// use axvisor_api::memory::PhysFrame; +/// +/// fn allocate_guest_memory() -> Option { +/// PhysFrame::alloc() +/// } +/// +/// // The frame will be automatically deallocated when it goes out of scope +/// ``` pub type PhysFrame = axaddrspace::PhysFrame; diff --git a/src/time.rs b/src/time.rs index 1e8129fcc..65a40ee54 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,53 +1,201 @@ -//! Time and timer APIs. +//! Time and timer APIs for the AxVisor hypervisor. +//! +//! This module provides APIs for time measurement and timer management, +//! which are essential for implementing virtual timers and time-related +//! virtualization features. +//! +//! # Overview +//! +//! The time APIs provide: +//! - Current time and tick count queries +//! - Conversion between ticks, nanoseconds, and duration +//! - Timer registration and cancellation +//! +//! # Types +//! +//! - [`TimeValue`] - A time value represented as [`Duration`]. +//! - [`Nanos`] - Nanoseconds count (u64). +//! - [`Ticks`] - Tick count (u64). +//! - [`CancelToken`] - Token used to cancel a registered timer. +//! +//! # Helper Functions +//! +//! In addition to the core API trait, this module provides helper functions: +//! - [`current_time_nanos`] - Get the current time in nanoseconds. +//! - [`current_time`] - Get the current time as a [`TimeValue`]. +//! - [`ticks_to_time`] - Convert ticks to [`TimeValue`]. +//! - [`time_to_ticks`] - Convert [`TimeValue`] to ticks. +//! +//! # Implementation +//! +//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute +//! macro on an impl block: +//! +//! ```rust,ignore +//! struct TimeIfImpl; +//! +//! #[axvisor_api::api_impl] +//! impl axvisor_api::time::TimeIf for TimeIfImpl { +//! fn current_ticks() -> Ticks { +//! // Read the hardware timer counter +//! } +//! // ... implement other functions +//! } +//! ``` extern crate alloc; use alloc::boxed::Box; use core::time::Duration; -/// Time value. +/// Time value type. +/// +/// Represents a point in time or a duration as a [`Duration`]. pub type TimeValue = Duration; -/// Nanoseconds count. + +/// Nanoseconds count type. +/// +/// Used for high-precision time measurements in nanoseconds. pub type Nanos = u64; -/// Tick count. + +/// Tick count type. +/// +/// Represents the raw hardware timer counter value. pub type Ticks = u64; -/// Cancel token, used to cancel a scheduled timer event. + +/// Cancel token type for timer cancellation. +/// +/// This token is returned when registering a timer and can be used to cancel +/// the timer before it fires. pub type CancelToken = usize; /// The API trait for time and timer functionalities. +/// +/// This trait defines the core time management interface required by the +/// hypervisor. Implementations should be provided by the host system or HAL +/// layer. #[crate::api_def] pub trait TimeIf { - /// Get the current tick count. + /// Get the current tick count from the hardware timer. + /// + /// The tick count is a monotonically increasing counter that can be + /// converted to time using [`ticks_to_nanos`]. + /// + /// # Returns + /// + /// The current hardware timer counter value. fn current_ticks() -> Ticks; - /// Convert ticks to nanoseconds. + + /// Convert a tick count to nanoseconds. + /// + /// # Arguments + /// + /// * `ticks` - The tick count to convert. + /// + /// # Returns + /// + /// The equivalent time in nanoseconds. fn ticks_to_nanos(ticks: Ticks) -> Nanos; - /// Convert nanoseconds to ticks. + + /// Convert nanoseconds to a tick count. + /// + /// # Arguments + /// + /// * `nanos` - The nanoseconds to convert. + /// + /// # Returns + /// + /// The equivalent tick count. fn nanos_to_ticks(nanos: Nanos) -> Ticks; - /// Register a timer. + + /// Register a timer that will fire at the specified deadline. + /// + /// When the deadline is reached, the callback function will be called + /// with the actual time at which it was invoked. + /// + /// # Arguments + /// + /// * `deadline` - The time at which the timer should fire. + /// * `callback` - The function to call when the timer fires. It receives + /// the actual time as an argument. + /// + /// # Returns + /// + /// A [`CancelToken`] that can be used to cancel the timer with + /// [`cancel_timer`]. + /// + /// # Example + /// + /// ```rust,ignore + /// use axvisor_api::time::{register_timer, current_time, TimeValue}; + /// use core::time::Duration; + /// + /// let deadline = current_time() + Duration::from_millis(100); + /// let token = register_timer(deadline, Box::new(|actual_time| { + /// println!("Timer fired at {:?}", actual_time); + /// })); + /// ``` fn register_timer( deadline: TimeValue, callback: Box, ) -> CancelToken; - /// Cancel a timer. + + /// Cancel a previously registered timer. + /// + /// If the timer has already fired, this function has no effect. + /// + /// # Arguments + /// + /// * `token` - The cancel token returned by [`register_timer`]. fn cancel_timer(token: CancelToken); } /// Get the current time in nanoseconds. +/// +/// This is a convenience function that combines [`current_ticks`] and +/// [`ticks_to_nanos`]. +/// +/// # Returns +/// +/// The current time in nanoseconds since an unspecified epoch. pub fn current_time_nanos() -> Nanos { ticks_to_nanos(current_ticks()) } -/// Get the current time. +/// Get the current time as a [`TimeValue`]. +/// +/// This is a convenience function that returns the current time as a +/// [`Duration`]. +/// +/// # Returns +/// +/// The current time as a [`TimeValue`] (Duration). pub fn current_time() -> TimeValue { Duration::from_nanos(current_time_nanos()) } -/// Convert ticks to time. +/// Convert ticks to a [`TimeValue`]. +/// +/// # Arguments +/// +/// * `ticks` - The tick count to convert. +/// +/// # Returns +/// +/// The equivalent time as a [`TimeValue`] (Duration). pub fn ticks_to_time(ticks: Ticks) -> TimeValue { Duration::from_nanos(ticks_to_nanos(ticks)) } -/// Convert time to ticks. +/// Convert a [`TimeValue`] to ticks. +/// +/// # Arguments +/// +/// * `time` - The time value to convert. +/// +/// # Returns +/// +/// The equivalent tick count. pub fn time_to_ticks(time: TimeValue) -> Ticks { nanos_to_ticks(time.as_nanos() as Nanos) } diff --git a/src/vmm.rs b/src/vmm.rs index 5d3ee2537..621b0633e 100644 --- a/src/vmm.rs +++ b/src/vmm.rs @@ -1,37 +1,183 @@ -//! Virtual machine management APIs. +//! Virtual machine management APIs for the AxVisor hypervisor. +//! +//! This module provides APIs for managing virtual machines (VMs) and virtual +//! CPUs (vCPUs), including querying VM/vCPU information and interrupt +//! injection. +//! +//! # Overview +//! +//! The VMM (Virtual Machine Monitor) APIs enable: +//! - Querying the current VM and vCPU context +//! - Getting information about VMs and their vCPUs +//! - Injecting interrupts into virtual CPUs +//! - Timer expiration notifications +//! +//! # Types +//! +//! - [`VMId`] - Virtual machine identifier. +//! - [`VCpuId`] - Virtual CPU identifier. +//! - [`InterruptVector`] - Interrupt vector number. +//! +//! # Helper Functions +//! +//! In addition to the core API trait, this module provides helper functions: +//! - [`current_vm_vcpu_num`] - Get the vCPU count of the current VM. +//! - [`current_vm_active_vcpus`] - Get the active vCPU mask of the current VM. +//! +//! # Implementation +//! +//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute +//! macro on an impl block: +//! +//! ```rust,ignore +//! struct VmmIfImpl; +//! +//! #[axvisor_api::api_impl] +//! impl axvisor_api::vmm::VmmIf for VmmIfImpl { +//! fn current_vm_id() -> VMId { +//! // Return the current VM's ID +//! } +//! // ... implement other functions +//! } +//! ``` -/// Virtual machine ID. +/// Virtual machine identifier type. +/// +/// Each virtual machine is assigned a unique identifier that can be used +/// to reference it in API calls. pub type VMId = usize; -/// Virtual CPU ID. + +/// Virtual CPU identifier type. +/// +/// Each vCPU within a VM is assigned a unique identifier (0-indexed). pub type VCpuId = usize; -/// Interrupt vector. + +/// Interrupt vector type. +/// +/// Represents the interrupt vector number to be injected into a guest. pub type InterruptVector = u8; /// The API trait for virtual machine management functionalities. +/// +/// This trait defines the core VM management interface required by the +/// hypervisor components. Implementations should be provided by the VMM +/// layer. #[crate::api_def] pub trait VmmIf { - /// Get the ID of the current virtual machine. + /// Get the identifier of the current virtual machine. + /// + /// This function returns the VM ID of the VM that the calling context + /// belongs to. + /// + /// # Returns + /// + /// The current VM's identifier. fn current_vm_id() -> VMId; - /// Get the ID of the current virtual CPU. + + /// Get the identifier of the current virtual CPU. + /// + /// This function returns the vCPU ID within the current VM context. + /// + /// # Returns + /// + /// The current vCPU's identifier (0-indexed within the VM). fn current_vcpu_id() -> VCpuId; + /// Get the number of virtual CPUs in a virtual machine. + /// + /// # Arguments + /// + /// * `vm_id` - The identifier of the virtual machine to query. + /// + /// # Returns + /// + /// - `Some(count)` - The number of vCPUs in the specified VM. + /// - `None` - If the VM ID is invalid. fn vcpu_num(vm_id: VMId) -> Option; - /// Get the mask of active virtual CPUs in a virtual machine. + + /// Get the bitmask of active virtual CPUs in a virtual machine. + /// + /// Each bit in the returned value represents a vCPU, where bit N is set + /// if vCPU N is active (online and running). + /// + /// # Arguments + /// + /// * `vm_id` - The identifier of the virtual machine to query. + /// + /// # Returns + /// + /// - `Some(mask)` - The active vCPU bitmask for the specified VM. + /// - `None` - If the VM ID is invalid. fn active_vcpus(vm_id: VMId) -> Option; - /// Inject an interrupt to a virtual CPU. + + /// Inject an interrupt into a specific virtual CPU. + /// + /// This function queues an interrupt to be delivered to the specified + /// vCPU when it is next scheduled. + /// + /// # Arguments + /// + /// * `vm_id` - The identifier of the target virtual machine. + /// * `vcpu_id` - The identifier of the target virtual CPU. + /// * `vector` - The interrupt vector to inject. + /// + /// # Example + /// + /// ```rust,ignore + /// use axvisor_api::vmm::{inject_interrupt, current_vm_id}; + /// + /// // Inject timer interrupt (vector 0x20) to vCPU 0 of the current VM + /// inject_interrupt(current_vm_id(), 0, 0x20); + /// ``` fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector); - /// Notify that a virtual CPU timer has expired. + + /// Notify that a virtual CPU's timer has expired. + /// + /// This function is called when a vCPU's virtual timer expires and needs + /// to be handled. + /// + /// # Arguments + /// + /// * `vm_id` - The identifier of the virtual machine. + /// * `vcpu_id` - The identifier of the virtual CPU whose timer expired. + /// + /// # Note /// - /// TODO: determine whether we can skip this function. + /// This API may be revised in future versions as the timer virtualization + /// design evolves. fn notify_vcpu_timer_expired(vm_id: VMId, vcpu_id: VCpuId); } /// Get the number of virtual CPUs in the current virtual machine. +/// +/// This is a convenience function that combines [`current_vm_id`] and +/// [`vcpu_num`]. +/// +/// # Returns +/// +/// The number of vCPUs in the current VM. +/// +/// # Panics +/// +/// Panics if called outside of a valid VM context (when [`current_vm_id`] +/// returns an invalid ID). pub fn current_vm_vcpu_num() -> usize { vcpu_num(current_vm_id()).unwrap() } -/// Get the mask of active virtual CPUs in the current virtual machine. +/// Get the bitmask of active virtual CPUs in the current virtual machine. +/// +/// This is a convenience function that combines [`current_vm_id`] and +/// [`active_vcpus`]. +/// +/// # Returns +/// +/// The active vCPU bitmask for the current VM. +/// +/// # Panics +/// +/// Panics if called outside of a valid VM context (when [`current_vm_id`] +/// returns an invalid ID). pub fn current_vm_active_vcpus() -> usize { active_vcpus(current_vm_id()).unwrap() } From bffeb94a157e0eb11535598bf63e674ad23f3507 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 30 Jan 2026 21:35:23 +0800 Subject: [PATCH 040/132] passthrough gic to launch aarch64 --- configs/vms/nimbos-aarch64-qemu-smp1.toml | 1 + kernel/src/hal/arch/aarch64/api.rs | 38 +++++++++++++++++------ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/configs/vms/nimbos-aarch64-qemu-smp1.toml b/configs/vms/nimbos-aarch64-qemu-smp1.toml index 17dac0d43..e280c206b 100644 --- a/configs/vms/nimbos-aarch64-qemu-smp1.toml +++ b/configs/vms/nimbos-aarch64-qemu-smp1.toml @@ -73,6 +73,7 @@ passthrough_devices = [ # Base-GPA Length. passthrough_addresses = [ #[0x28041000, 0x100_0000] + [0x0800_0000, 0x100_0000] ] # Devices that are not desired to be passed through to the guest diff --git a/kernel/src/hal/arch/aarch64/api.rs b/kernel/src/hal/arch/aarch64/api.rs index 5071517d9..c2db97ebc 100644 --- a/kernel/src/hal/arch/aarch64/api.rs +++ b/kernel/src/hal/arch/aarch64/api.rs @@ -1,14 +1,15 @@ -#[axvisor_api::api_mod_impl(axvisor_api::arch)] -mod arch_api_impl { - use core::panic; +use axvisor_api::arch::ArchIf; +use std::os::arceos::modules::axhal::{self, mem::virt_to_phys}; - use axvisor_api::memory::virt_to_phys; +struct ArchImpl; - extern fn hardware_inject_virtual_interrupt(irq: axvisor_api::vmm::InterruptVector) { +#[axvisor_api::api_impl] +impl ArchIf for ArchImpl { + fn hardware_inject_virtual_interrupt(irq: axvisor_api::vmm::InterruptVector) { crate::hal::arch::inject_interrupt(irq as _); } - extern fn read_vgicd_typer() -> u32 { + fn read_vgicd_typer() -> u32 { let mut gic = rdrive::get_one::() .expect("Failed to get GIC driver") .lock() @@ -24,7 +25,7 @@ mod arch_api_impl { panic!("No GIC driver found"); } - extern fn read_vgicd_iidr() -> u32 { + fn read_vgicd_iidr() -> u32 { // use axstd::os::arceos::modules::axhal::irq::MyVgic; // MyVgic::get_gicd().lock().get_iidr() let mut gic = rdrive::get_one::() @@ -43,7 +44,7 @@ mod arch_api_impl { panic!("No GIC driver found"); } - extern fn get_host_gicd_base() -> memory_addr::PhysAddr { + fn get_host_gicd_base() -> memory_addr::PhysAddr { let mut gic = rdrive::get_one::() .expect("Failed to get GIC driver") .lock() @@ -61,7 +62,7 @@ mod arch_api_impl { panic!("No GIC driver found"); } - extern fn get_host_gicr_base() -> memory_addr::PhysAddr { + fn get_host_gicr_base() -> memory_addr::PhysAddr { let mut gic = rdrive::get_one::() .expect("Failed to get GIC driver") .lock() @@ -72,4 +73,23 @@ mod arch_api_impl { } panic!("No GICv3 driver found"); } + + fn fetch_irq() -> u64 { + /// TODO: better implementation + let mut gic = rdrive::get_one::() + .expect("Failed to get GIC driver") + .lock() + .unwrap(); + if let Some(gic) = gic.typed_mut::() { + return u32::from(gic.cpu_interface().ack()) as _ + } + if let Some(gic) = gic.typed_mut::() { + return gic.cpu_interface().ack1().to_u32() as _ + } + panic!("No GIC driver found"); + } + + fn handle_irq() { + axhal::irq::irq_handler(0); + } } From b8e4cf625644759ed5f50d5d886fc445c2c48101 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 30 Jan 2026 21:43:22 +0800 Subject: [PATCH 041/132] preparing for riscv64 --- .cargo/config.toml | 13 ++++++++++--- configs/board/qemu-riscv64.toml | 10 ++++++++++ kernel/src/hal/arch/riscv64/cache.rs | 5 +++++ kernel/src/hal/arch/riscv64/mod.rs | 4 ++++ kernel/src/hal/mod.rs | 1 + scripts/ostool/qemu-riscv64.toml | 19 +++++++++++++++++++ xtask/src/cargo.rs | 3 +++ 7 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 configs/board/qemu-riscv64.toml create mode 100644 kernel/src/hal/arch/riscv64/cache.rs create mode 100644 kernel/src/hal/arch/riscv64/mod.rs create mode 100644 scripts/ostool/qemu-riscv64.toml diff --git a/.cargo/config.toml b/.cargo/config.toml index 676b0e8a5..e2ab52763 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,9 +5,6 @@ rustflags = [ "-Clink-args=-Tlink.x", ] -[target.'cfg(target_os = "none")'] -runner = "cargo osrun" - [target.x86_64-unknown-none] rustflags = [ "-Clink-args=-no-pie", @@ -15,6 +12,16 @@ rustflags = [ "-Clink-args=-Tlink.x", ] +[target.riscv64gc-unknown-none-elf] +rustflags = [ + "-Clink-args=-no-pie", + "-Clink-args=-znostart-stop-gc", + # "-Clink-args=-Tlinker.lds.S", +] + +[target.'cfg(target_os = "none")'] +runner = "cargo osrun" + [alias] xtask = "run --package xtask --" diff --git a/configs/board/qemu-riscv64.toml b/configs/board/qemu-riscv64.toml new file mode 100644 index 000000000..2e2f1858f --- /dev/null +++ b/configs/board/qemu-riscv64.toml @@ -0,0 +1,10 @@ +cargo_args = [] +features = [ + # "axstd/myplat", + "ept-level-4", + "fs", +] +log = "Info" +target = "riscv64gc-unknown-none-elf" +to_bin = false +vm_configs = [] \ No newline at end of file diff --git a/kernel/src/hal/arch/riscv64/cache.rs b/kernel/src/hal/arch/riscv64/cache.rs new file mode 100644 index 000000000..1a905a9d0 --- /dev/null +++ b/kernel/src/hal/arch/riscv64/cache.rs @@ -0,0 +1,5 @@ +use memory_addr::VirtAddr; + +use crate::hal::CacheOp; + +pub fn dcache_range(_op: CacheOp, _addr: VirtAddr, _size: usize) {} diff --git a/kernel/src/hal/arch/riscv64/mod.rs b/kernel/src/hal/arch/riscv64/mod.rs new file mode 100644 index 000000000..ab92f90b2 --- /dev/null +++ b/kernel/src/hal/arch/riscv64/mod.rs @@ -0,0 +1,4 @@ +pub mod cache; + +pub fn hardware_check() {} +pub fn inject_interrupt(_vector: u8) {} diff --git a/kernel/src/hal/mod.rs b/kernel/src/hal/mod.rs index 78b229096..7575fde56 100644 --- a/kernel/src/hal/mod.rs +++ b/kernel/src/hal/mod.rs @@ -7,6 +7,7 @@ use page_table_multiarch::PagingHandler; #[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/mod.rs")] #[cfg_attr(target_arch = "x86_64", path = "arch/x86_64/mod.rs")] +#[cfg_attr(target_arch = "riscv64", path = "arch/riscv64/mod.rs")] pub mod arch; use crate::{hal::arch::hardware_check, vmm}; diff --git a/scripts/ostool/qemu-riscv64.toml b/scripts/ostool/qemu-riscv64.toml new file mode 100644 index 000000000..f30276efb --- /dev/null +++ b/scripts/ostool/qemu-riscv64.toml @@ -0,0 +1,19 @@ +args = [ + "-nographic", + "-machine", + "virt", + "-smp", + "1", + "-bios", + "default", + "-device", + "virtio-blk-device,drive=disk0", + "-drive", + "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", + "-m", + "4G", +] +fail_regex = [] +success_regex = [] +to_bin = true +uefi = false diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index a821bdf5a..3b8c84885 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -11,6 +11,8 @@ impl Context { Arch::Aarch64 } else if build_config.target.contains("x86_64") { Arch::X86_64 + } else if build_config.target.contains("riscv64") { + Arch::Riscv64 } else { return Err(anyhow::anyhow!( "Unsupported target architecture: {}", @@ -64,4 +66,5 @@ impl Context { enum Arch { Aarch64, X86_64, + Riscv64, } From 6a6a3f664a346885eabd1031c48141da48283301 Mon Sep 17 00:00:00 2001 From: ZCShou <72115@163.com> Date: Mon, 2 Feb 2026 13:39:18 +0800 Subject: [PATCH 042/132] ci: refactor workflows for modular CI/CD pipeline --- .github/workflows/check.yml | 42 +++++++++ .github/workflows/ci.yml | 76 ----------------- .github/workflows/deploy.yml | 66 ++++++++++++++ .github/workflows/release.yml | 156 ++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 21 +++++ 5 files changed, 285 insertions(+), 76 deletions(-) create mode 100644 .github/workflows/check.yml delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 000000000..f33d22121 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,42 @@ +name: Quality Checks + +on: + workflow_call: + +jobs: + check: + name: Quality Checks + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - x86_64-unknown-none + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + components: rust-src, clippy, rustfmt + targets: ${{ matrix.target }} + + - name: Check rust version + run: rustc --version --verbose + + - name: Check code format + run: cargo fmt --all -- --check + + - name: Build + run: cargo build --target ${{ matrix.target }} --all-features + + - name: Run clippy + run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings + + - name: Build documentation + env: + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + run: cargo doc --no-deps --target ${{ matrix.target }} --all-features diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index c5aa8b9c3..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: CI - -on: [push, pull_request] - -jobs: - ci: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - rust-toolchain: [nightly-2025-05-20, nightly] - targets: [x86_64-unknown-none] - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: ${{ matrix.rust-toolchain }} - components: rust-src, clippy, rustfmt - targets: ${{ matrix.targets }} - - name: Check rust version - run: rustc --version --verbose - - name: Check code format - run: cargo fmt --all -- --check - - name: Clippy - run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default - - name: Build - run: cargo build --target ${{ matrix.targets }} --all-features - - name: Check documentation - run: cargo doc --target ${{ matrix.targets }} --all-features --no-deps - env: - RUSTDOCFLAGS: "-D rustdoc::broken_intra_doc_links -D missing-docs" - - unit_test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - rust-toolchain: [nightly-2025-05-20, nightly] - targets: [x86_64-unknown-linux-gnu] - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: ${{ matrix.rust-toolchain }} - components: rust-src - - name: Check rust version - run: rustc --version --verbose - - name: Unit test - run: cargo test --target ${{ matrix.targets }} --all-features -- --nocapture - - doc: - runs-on: ubuntu-latest - strategy: - fail-fast: false - permissions: - contents: write - env: - default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: nightly-2025-05-20 - - name: Build docs - continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - name: Deploy to Github Pages - if: ${{ github.ref == env.default-branch }} - uses: JamesIves/github-pages-deploy-action@v4 - with: - single-commit: true - branch: gh-pages - folder: target/doc diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..99f5a00d1 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,66 @@ +name: Deploy + +on: + push: + branches: + - '**' + tags-ignore: + - 'v*' + - 'v*-pre.*' + pull_request: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + quality-check: + uses: ./.github/workflows/check.yml + + test: + uses: ./.github/workflows/test.yml + + build-doc: + name: Build documentation + runs-on: ubuntu-latest + needs: quality-check + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - name: Build docs + env: + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + run: | + cargo doc --no-deps --all-features + printf '' > target/doc/index.html + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: target/doc + + deploy-doc: + name: Deploy to GitHub Pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build-doc + if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..ea5d5489b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,156 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + - 'v*.*.*-pre.*' + +permissions: + contents: write + +jobs: + check: + uses: ./.github/workflows/check.yml + + test: + uses: ./.github/workflows/test.yml + needs: check + + create-release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: check + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate tag and branch (HEAD-based) + shell: bash + run: | + set -e + + TAG="${{ github.ref_name }}" + TAG_COMMIT=$(git rev-list -n 1 "$TAG") + + git fetch origin main dev + + MAIN_HEAD=$(git rev-parse origin/main) + DEV_HEAD=$(git rev-parse origin/dev) + + echo "Tag: $TAG" + echo "Tag commit: $TAG_COMMIT" + echo "main HEAD: $MAIN_HEAD" + echo "dev HEAD: $DEV_HEAD" + + if [[ "$TAG" == *-pre.* ]]; then + if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then + echo "❌ prerelease tag must be created from dev HEAD" + exit 1 + fi + echo "✅ prerelease tag validated on dev" + else + if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then + echo "❌ stable release tag must be created from main HEAD" + exit 1 + fi + echo "✅ stable release tag validated on main" + fi + + - name: Verify version consistency + run: | + # Extract version from git tag (remove 'v' prefix) + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + # Extract version from Cargo.toml + CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') + echo "Git tag version: $TAG_VERSION" + echo "Cargo.toml version: $CARGO_VERSION" + if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then + echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" + exit 1 + fi + echo "Version check passed!" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + draft: false + prerelease: ${{ contains(github.ref_name, '-pre.') }} + body: | + ## ${{ github.ref_name }} + + - [Documentation](https://docs.rs/axhvc) + - [crates.io](https://crates.io/crates/axhvc) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-crates: + name: Publish to crates.io + runs-on: ubuntu-latest + needs: check + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate tag and branch (HEAD-based) + shell: bash + run: | + set -e + + TAG="${{ github.ref_name }}" + TAG_COMMIT=$(git rev-list -n 1 "$TAG") + + git fetch origin main dev + + MAIN_HEAD=$(git rev-parse origin/main) + DEV_HEAD=$(git rev-parse origin/dev) + + echo "Tag: $TAG" + echo "Tag commit: $TAG_COMMIT" + echo "main HEAD: $MAIN_HEAD" + echo "dev HEAD: $DEV_HEAD" + + if [[ "$TAG" == *-pre.* ]]; then + if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then + echo "❌ prerelease tag must be created from dev HEAD" + exit 1 + fi + echo "✅ prerelease tag validated on dev" + else + if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then + echo "❌ stable release tag must be created from main HEAD" + exit 1 + fi + echo "✅ stable release tag validated on main" + fi + + - name: Verify version consistency + run: | + # Extract version from git tag (remove 'v' prefix) + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + # Extract version from Cargo.toml + CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') + echo "Git tag version: $TAG_VERSION" + echo "Cargo.toml version: $CARGO_VERSION" + if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then + echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" + exit 1 + fi + echo "Version check passed!" + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - name: Dry run publish + run: cargo publish --dry-run + + - name: Publish to crates.io + run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..cd99d902f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,21 @@ +name: Test + +on: + workflow_call: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - name: Run tests + run: cargo test --all-features -- --nocapture + + - name: Run doc tests + run: cargo test --doc From 209816228217e76277aa0bcfeed13f50660c63fb Mon Sep 17 00:00:00 2001 From: YanLien Date: Mon, 2 Feb 2026 17:13:01 +0800 Subject: [PATCH 043/132] refactor(riscv): update dependencies and restructure interrupt injection Major changes: - Update dependencies to use official arceos-hypervisor repositories (next branch) - axdevice, axvmconfig, riscv_vcpu, riscv_vplic, arm_vgic - Replace range-alloc with range-alloc-arceos - Update axvisor_api and axvisor_api_proc to crates.io versions - Refactor RISC-V interrupt injection mechanism - Replace axvisor_api::api_mod_impl with crate_interface - Define InjectIrqIf trait in platform layer - Implement trait in kernel layer for better separation of concerns - Platform-specific improvements - Adjust DTB load address for linux-riscv64 (0x9200_0000 -> 0x9300_0000) - Remove ept-level-4 feature from qemu-riscv64 config - Add success regex pattern "Hello, world!" for CI tests - Code quality improvements - Fix formatting and remove trailing whitespace - Reorganize imports for consistency - Update image SHA256 checksums - Build system updates - Add axplat-riscv64-qemu-virt dependency to kernel - Update xtask formatting and structure --- .github/workflows/qemu-riscv64.toml | 7 +- Cargo.lock | 109 +++++------------- Cargo.toml | 20 +--- configs/board/qemu-riscv64.toml | 1 - configs/vms/arceos-riscv64-qemu-smp1.toml | 1 - configs/vms/linux-riscv64-qemu-smp1.toml | 2 +- kernel/Cargo.toml | 3 +- kernel/src/hal/arch/riscv64/api.rs | 8 +- kernel/src/hal/arch/riscv64/mod.rs | 9 +- modules/axfs/src/fs/fatfs.rs | 2 +- modules/axfs/src/lib.rs | 2 +- modules/axfs/src/partition.rs | 4 +- .../driver/src/soc/rockchip/clk/rk3568-clk.rs | 2 +- platform/riscv64-qemu-virt/Cargo.toml | 1 + platform/riscv64-qemu-virt/build.rs | 6 +- platform/riscv64-qemu-virt/src/boot.rs | 2 +- platform/riscv64-qemu-virt/src/console.rs | 2 +- platform/riscv64-qemu-virt/src/init.rs | 2 +- platform/riscv64-qemu-virt/src/irq.rs | 14 ++- platform/riscv64-qemu-virt/src/lib.rs | 5 +- platform/riscv64-qemu-virt/src/mem.rs | 2 +- platform/riscv64-qemu-virt/src/power.rs | 2 +- platform/riscv64-qemu-virt/src/time.rs | 2 +- xtask/src/cargo.rs | 4 +- xtask/src/image.rs | 60 +++++----- xtask/src/main.rs | 10 -- xtask/src/menuconfig.rs | 5 +- 27 files changed, 120 insertions(+), 167 deletions(-) diff --git a/.github/workflows/qemu-riscv64.toml b/.github/workflows/qemu-riscv64.toml index f201c2366..b228b6f1b 100644 --- a/.github/workflows/qemu-riscv64.toml +++ b/.github/workflows/qemu-riscv64.toml @@ -12,12 +12,13 @@ args = [ "virtio-blk-device,drive=disk0", "-drive", "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-append", - "root=/dev/vda rw init=/init", "-m", "8g", ] fail_regex = [] -success_regex = ["test pass!"] +success_regex = [ + "Hello, world!", + "test pass!", +] to_bin = true uefi = false \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 8d1b4cfea..88694ba49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,24 +261,6 @@ dependencies = [ "tock-registers 0.10.1", ] -[[package]] -name = "arm_vgic" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/arm_vgic.git#81338d6dd8a9dab04b91c9c0da3d6d84e7768be6" -dependencies = [ - "aarch64-cpu", - "aarch64_sysreg", - "axaddrspace", - "axdevice_base", - "axerrno 0.1.2", - "axvisor_api", - "bitmaps", - "log", - "memory_addr", - "spin 0.9.8", - "tock-registers 0.10.1", -] - [[package]] name = "arrayvec" version = "0.7.6" @@ -414,8 +396,9 @@ dependencies = [ [[package]] name = "axdevice" version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/axdevice.git?branch=next#c83350ee95d08b64225600f77102b682539010bf" dependencies = [ - "arm_vgic 0.1.0 (git+https://github.com/arceos-hypervisor/arm_vgic.git)", + "arm_vgic", "axaddrspace", "axdevice_base", "axerrno 0.1.2", @@ -423,26 +406,8 @@ dependencies = [ "cfg-if", "log", "memory_addr", - "range-alloc", - "riscv_vplic 0.1.0 (git+https://github.com/arceos-hypervisor/riscv_vplic.git?rev=8bc5213)", - "spin 0.9.8", -] - -[[package]] -name = "axdevice" -version = "0.1.0" -source = "git+https://github.com/liulog/axdevice?branch=master#50ecc20e3d4fe82480b041c769bad18845ce80c5" -dependencies = [ - "arm_vgic 0.1.0 (git+https://github.com/arceos-hypervisor/arm_vgic.git)", - "axaddrspace", - "axdevice_base", - "axerrno 0.1.2", - "axvmconfig", - "cfg-if", - "log", - "memory_addr", - "range-alloc", - "riscv_vplic 0.1.0 (git+https://github.com/liulog/riscv_vplic.git)", + "range-alloc-arceos", + "riscv_vplic", "spin 0.9.8", ] @@ -896,6 +861,7 @@ dependencies = [ "axcpu", "axplat", "axvisor_api", + "crate_interface", "kspin", "lazyinit", "log", @@ -1118,17 +1084,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axvcpu" -version = "0.1.1" -dependencies = [ - "axaddrspace", - "axerrno 0.1.2", - "axvisor_api", - "memory_addr", - "percpu", -] - [[package]] name = "axvcpu" version = "0.1.1" @@ -1163,14 +1118,15 @@ dependencies = [ "arm-gic-driver", "axaddrspace", "axconfig", - "axdevice 0.1.0 (git+https://github.com/liulog/axdevice?branch=master)", + "axdevice", "axdevice_base", "axdriver", "axerrno 0.2.2", "axhvc", + "axplat-riscv64-qemu-virt 0.3.0", "axruntime", "axstd", - "axvcpu 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "axvcpu 0.1.1", "axvisor_api", "axvm", "bitflags 2.10.0", @@ -1195,7 +1151,7 @@ dependencies = [ "rdif-intc", "rdrive", "riscv_vcpu", - "riscv_vplic 0.1.0 (git+https://github.com/arceos-hypervisor/riscv_vplic.git?rev=8bc5213)", + "riscv_vplic", "spin 0.9.8", "syn 2.0.113", "timer_list", @@ -1206,6 +1162,8 @@ dependencies = [ [[package]] name = "axvisor_api" version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa7233b2a1338dc06a80e2779b572b4df02007ea128ef7b235b66fc3eeac0ca6" dependencies = [ "axaddrspace", "axvisor_api_proc", @@ -1216,6 +1174,8 @@ dependencies = [ [[package]] name = "axvisor_api_proc" version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a64eb4410ae8357ac8c01c2fb201e57d7aeeb5436ed4d0f5774bfa11cc5902" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1226,14 +1186,15 @@ dependencies = [ [[package]] name = "axvm" version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/axvm.git?branch=next#31ff86e4704a045cc85ded4613b4296c6ea183cb" dependencies = [ "arm_vcpu", - "arm_vgic 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "arm_vgic", "axaddrspace", - "axdevice 0.1.0 (git+https://github.com/liulog/axdevice?branch=master)", + "axdevice", "axdevice_base", "axerrno 0.2.2", - "axvcpu 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "axvcpu 0.1.1", "axvmconfig", "cfg-if", "cpumask", @@ -1250,6 +1211,7 @@ dependencies = [ [[package]] name = "axvmconfig" version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/axvmconfig.git?branch=next#75e7ae893bcca452fd006ae4c17b21715c95e4e0" dependencies = [ "axerrno 0.1.2", "clap", @@ -2380,7 +2342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4216,7 +4178,7 @@ dependencies = [ "once_cell", "socket2 0.6.1", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4300,9 +4262,10 @@ dependencies = [ ] [[package]] -name = "range-alloc" -version = "0.1.4" -source = "git+https://github.com/arceos-hypervisor/range-alloc.git#fc826e54dab9072be5358a1b0e7fc34503d6630d" +name = "range-alloc-arceos" +version = "0.1.4-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63358c741a7a22b7b99bc27dcdf5b79339b132bd4380920fed7f7219ece58dd" [[package]] name = "ratatui" @@ -4697,10 +4660,11 @@ dependencies = [ [[package]] name = "riscv_vcpu" version = "0.1.2" +source = "git+https://github.com/arceos-hypervisor/riscv_vcpu.git?branch=master#dda3b4a82255fd06c343051b6cc0d8854a2df2b0" dependencies = [ "axaddrspace", "axerrno 0.1.2", - "axvcpu 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "axvcpu 0.1.1", "axvisor_api", "bit_field", "bitflags 2.10.0", @@ -4734,21 +4698,6 @@ dependencies = [ "spin 0.9.8", ] -[[package]] -name = "riscv_vplic" -version = "0.1.0" -source = "git+https://github.com/liulog/riscv_vplic.git#5ed1fb6bb06d85675ba8f60813d32744b512b23d" -dependencies = [ - "axaddrspace", - "axdevice_base", - "axerrno 0.1.2", - "axvisor_api", - "bitmaps", - "log", - "riscv-h", - "spin 0.9.8", -] - [[package]] name = "rk3568_clk" version = "0.1.0" @@ -4895,7 +4844,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5636,7 +5585,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.3", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6771,7 +6720,7 @@ dependencies = [ "axaddrspace", "axdevice_base", "axerrno 0.1.2", - "axvcpu 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "axvcpu 0.1.1", "axvisor_api", "bit_field", "bitflags 2.10.0", diff --git a/Cargo.toml b/Cargo.toml index 35820b0d7..90d751f48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ rdrive = "0.18" vm-fdt = {git = "https://github.com/bullhh/vm-fdt.git", default-features = false, features = ["alloc"]} -axdevice = {git = "https://github.com/arceos-hypervisor/axdevice.git"} +axdevice = {git = "https://github.com/arceos-hypervisor/axdevice.git", branch = "next"} axdevice_base = "0.1" axvisor_api = "0.1" driver = {path = "modules/driver"} @@ -89,23 +89,9 @@ axplat-x86-qemu-q35 = {path = "platform/x86-qemu-q35"} axplat-riscv64-qemu-virt = {path = "platform/riscv64-qemu-virt"} axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "next"} -[patch.crates-io] -axvmconfig = { path = "crates/axvmconfig" } -axvisor_api = {path = "../axvisor_api"} -# axvcpu = { path = "./crates/axvcpu" } - -[patch."https://github.com/arceos-hypervisor/axvmconfig.git"] -axvmconfig = {path = "crates/axvmconfig"} - [patch."https://github.com/arceos-org/arceos.git"] axconfig = { path = "modules/axconfig" } axruntime = { path = "modules/axruntime" } -[patch."https://github.com/arceos-hypervisor/axvm.git"] -axvm = {path = "crates/axvm"} - -[patch."https://github.com/liulog/riscv_vcpu.git"] -riscv_vcpu = { path = "crates/riscv_vcpu" } - -[patch."https://github.com/arceos-hypervisor/axdevice.git"] -axdevice = {git = "https://github.com/liulog/axdevice", branch = "master"} +[patch.crates-io] +axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "next"} diff --git a/configs/board/qemu-riscv64.toml b/configs/board/qemu-riscv64.toml index 5e3d701b4..fbd1ac359 100644 --- a/configs/board/qemu-riscv64.toml +++ b/configs/board/qemu-riscv64.toml @@ -1,7 +1,6 @@ cargo_args = [] features = [ "axstd/myplat", - "ept-level-4", "axstd/bus-mmio", # "fs", ] diff --git a/configs/vms/arceos-riscv64-qemu-smp1.toml b/configs/vms/arceos-riscv64-qemu-smp1.toml index c80c906d2..ff4a5ca1f 100644 --- a/configs/vms/arceos-riscv64-qemu-smp1.toml +++ b/configs/vms/arceos-riscv64-qemu-smp1.toml @@ -70,4 +70,3 @@ emu_devices = [ ] interrupt_mode = "passthrough" - diff --git a/configs/vms/linux-riscv64-qemu-smp1.toml b/configs/vms/linux-riscv64-qemu-smp1.toml index cf7ee30a6..7aeacebbf 100644 --- a/configs/vms/linux-riscv64-qemu-smp1.toml +++ b/configs/vms/linux-riscv64-qemu-smp1.toml @@ -29,7 +29,7 @@ kernel_load_addr = 0x9000_0000 # The file path of the device tree blob (DTB). dtb_path = "/path/tmp/configs/linux-riscv64-qemu-smp1.dtb" # The load address of the device tree blob (DTB). -dtb_load_addr = 0x9200_0000 +dtb_load_addr = 0x9300_0000 # Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). # For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 3a7787201..a94c0f7d9 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -63,8 +63,9 @@ aarch64-cpu-ext = "0.1" arm-gic-driver = {version = "0.15.5", features = ["rdif"]} [target.'cfg(target_arch = "riscv64")'.dependencies] +axplat-riscv64-qemu-virt.workspace = true riscv_vplic = { git = "https://github.com/arceos-hypervisor/riscv_vplic.git", rev = "8bc5213" } -riscv_vcpu = { git = "https://github.com/liulog/riscv_vcpu.git", branch = "master" } +riscv_vcpu = { git = "https://github.com/arceos-hypervisor/riscv_vcpu.git", branch = "master" } [build-dependencies] axconfig.workspace = true diff --git a/kernel/src/hal/arch/riscv64/api.rs b/kernel/src/hal/arch/riscv64/api.rs index 34779dc7c..e45c5704b 100644 --- a/kernel/src/hal/arch/riscv64/api.rs +++ b/kernel/src/hal/arch/riscv64/api.rs @@ -1,6 +1,8 @@ -#[axvisor_api::api_mod_impl(axvisor_api::arch)] -mod arch_api_impl { - extern fn inject_virtual_interrupt(irq: usize) { +struct VmInterruptIfImpl; + +#[crate_interface::impl_interface] +impl axplat_riscv64_qemu_virt::irq::InjectIrqIf for VmInterruptIfImpl { + fn inject_virtual_interrupt(irq: usize) { crate::hal::arch::inject_interrupt(irq); } } diff --git a/kernel/src/hal/arch/riscv64/mod.rs b/kernel/src/hal/arch/riscv64/mod.rs index ff1f75892..2c14c0155 100644 --- a/kernel/src/hal/arch/riscv64/mod.rs +++ b/kernel/src/hal/arch/riscv64/mod.rs @@ -8,15 +8,18 @@ use axvisor_api::vmm::current_vm_id; pub fn hardware_check() { // TODO: implement hardware checks for RISC-V64 // check page table level like aarch64 - } pub fn inject_interrupt(irq_id: usize) { debug!("injecting interrupt id: {}", irq_id); // Get the instance of the vplic, and then inject virtual interrupt. - let vplic = get_vm_by_id(current_vm_id()).unwrap().get_devices().find_mmio_dev(GuestPhysAddr::from_usize(axruntime::plic_base())).unwrap(); - + let vplic = get_vm_by_id(current_vm_id()) + .unwrap() + .get_devices() + .find_mmio_dev(GuestPhysAddr::from_usize(axruntime::plic_base())) + .unwrap(); + // Calulate the pending register offset and value. let reg_offset = riscv_vplic::PLIC_PENDING_OFFSET + (irq_id / 32) * 4; let addr = GuestPhysAddr::from_usize(axruntime::plic_base() + reg_offset); diff --git a/modules/axfs/src/fs/fatfs.rs b/modules/axfs/src/fs/fatfs.rs index da0b6f11d..b62d25cb2 100644 --- a/modules/axfs/src/fs/fatfs.rs +++ b/modules/axfs/src/fs/fatfs.rs @@ -4,8 +4,8 @@ use core::cell::OnceCell; use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult}; use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; -use spin::Mutex; use fatfs::{Dir, File, LossyOemCpConverter, NullTimeProvider, Read, Seek, SeekFrom, Write}; +use spin::Mutex; use crate::dev::{Disk, Partition}; diff --git a/modules/axfs/src/lib.rs b/modules/axfs/src/lib.rs index 11692a023..697b21b5b 100644 --- a/modules/axfs/src/lib.rs +++ b/modules/axfs/src/lib.rs @@ -19,7 +19,7 @@ use alloc::{ sync::Arc, vec::Vec, }; -use axdriver::{prelude::*, AxDeviceContainer}; +use axdriver::{AxDeviceContainer, prelude::*}; /// Initializes filesystems by block devices. pub fn init_filesystems(mut blk_devs: AxDeviceContainer, bootargs: Option<&str>) { diff --git a/modules/axfs/src/partition.rs b/modules/axfs/src/partition.rs index 05f0d3a8c..f6487ea1c 100644 --- a/modules/axfs/src/partition.rs +++ b/modules/axfs/src/partition.rs @@ -225,7 +225,7 @@ fn parse_gpt_partitions(disk: &mut Disk) -> AxResult> { for j in 0..36 { name_utf16[j] = entry.partition_name[j]; } - + // Find the null terminator let mut name_len = 36; for j in 0..36 { @@ -234,7 +234,7 @@ fn parse_gpt_partitions(disk: &mut Disk) -> AxResult> { break; } } - + // Convert only the valid portion let name_slice = &name_utf16[..name_len]; let name_str = String::from_utf16_lossy(name_slice); diff --git a/modules/driver/src/soc/rockchip/clk/rk3568-clk.rs b/modules/driver/src/soc/rockchip/clk/rk3568-clk.rs index 6607dd92c..efa3b1ca0 100644 --- a/modules/driver/src/soc/rockchip/clk/rk3568-clk.rs +++ b/modules/driver/src/soc/rockchip/clk/rk3568-clk.rs @@ -125,4 +125,4 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError plat_dev.register(clk); Ok(()) -} \ No newline at end of file +} diff --git a/platform/riscv64-qemu-virt/Cargo.toml b/platform/riscv64-qemu-virt/Cargo.toml index 9d07de9e7..8bfe9dc17 100644 --- a/platform/riscv64-qemu-virt/Cargo.toml +++ b/platform/riscv64-qemu-virt/Cargo.toml @@ -27,6 +27,7 @@ axconfig-macros = "0.2" axcpu = { workspace = true } axplat = { workspace = true } axvisor_api = { workspace = true } +crate_interface = { workspace = true } [package.metadata.docs.rs] targets = ["riscv64gc-unknown-none-elf"] diff --git a/platform/riscv64-qemu-virt/build.rs b/platform/riscv64-qemu-virt/build.rs index 86c6182a5..80a4c120e 100644 --- a/platform/riscv64-qemu-virt/build.rs +++ b/platform/riscv64-qemu-virt/build.rs @@ -9,8 +9,10 @@ fn main() { let ld_content = include_str!("linker.lds.S"); let ld_content = ld_content.replace("%ARCH%", "riscv"); - let ld_content = - ld_content.replace("%KERNEL_BASE%", &format!("{:#x}", 0xffff_ffc0_8020_0000usize)); + let ld_content = ld_content.replace( + "%KERNEL_BASE%", + &format!("{:#x}", 0xffff_ffc0_8020_0000usize), + ); let ld_content = ld_content.replace("%SMP%", &format!("{smp}",)); // target///build/axvisor-xxxx/out diff --git a/platform/riscv64-qemu-virt/src/boot.rs b/platform/riscv64-qemu-virt/src/boot.rs index 12a7fc473..e3a690a82 100644 --- a/platform/riscv64-qemu-virt/src/boot.rs +++ b/platform/riscv64-qemu-virt/src/boot.rs @@ -73,7 +73,7 @@ unsafe extern "C" fn _start() -> ! { /// The earliest entry point for secondary CPUs. #[cfg(feature = "smp")] #[unsafe(naked)] -pub(crate) unsafe extern "C" fn _start_secondary() -> ! { +pub(crate) unsafe extern fn _start_secondary() -> ! { // a0 = hartid // a1 = SP core::arch::naked_asm!(" diff --git a/platform/riscv64-qemu-virt/src/console.rs b/platform/riscv64-qemu-virt/src/console.rs index 63f4799ad..037f5cc6a 100644 --- a/platform/riscv64-qemu-virt/src/console.rs +++ b/platform/riscv64-qemu-virt/src/console.rs @@ -51,4 +51,4 @@ impl ConsoleIf for ConsoleIfImpl { fn irq_num() -> Option { Some(crate::config::devices::UART_IRQ) } -} \ No newline at end of file +} diff --git a/platform/riscv64-qemu-virt/src/init.rs b/platform/riscv64-qemu-virt/src/init.rs index 3f191049b..f90db4e90 100644 --- a/platform/riscv64-qemu-virt/src/init.rs +++ b/platform/riscv64-qemu-virt/src/init.rs @@ -37,4 +37,4 @@ impl InitIf for InitIfImpl { crate::irq::init_percpu(); crate::time::init_percpu(); } -} \ No newline at end of file +} diff --git a/platform/riscv64-qemu-virt/src/irq.rs b/platform/riscv64-qemu-virt/src/irq.rs index a05f23589..6e06037dc 100644 --- a/platform/riscv64-qemu-virt/src/irq.rs +++ b/platform/riscv64-qemu-virt/src/irq.rs @@ -15,6 +15,14 @@ use sbi_rt::HartMask; use crate::config::{devices::PLIC_PADDR, plat::PHYS_VIRT_OFFSET}; +/// Use call_interface with the trait path as known to crate_interface +/// Interface for injecting virtual interrupts to guest VMs. +/// This trait is defined and implemented in the kernel (axvisor). +#[crate_interface::def_interface] +pub trait InjectIrqIf { + fn inject_virtual_interrupt(irq: usize); +} + /// `Interrupt` bit in `scause` pub(super) const INTC_IRQ_BASE: usize = 1 << (usize::BITS - 1); @@ -212,10 +220,10 @@ impl IrqIf for IrqIfImpl { }; drop(plic); // Inject the virtual interrupt to the guest VM - axvisor_api::arch::inject_virtual_interrupt(irq.get() as usize); + crate_interface::call_interface!(InjectIrqIf::inject_virtual_interrupt, irq.get() as usize); // trace!("IRQ: external {irq}"); - // IRQ_HANDLER_TABLE.handle(irq.get() as usize); + // IRQ_HANDLER_TABLE.handle(irq.get() as usize); // Only for irqs that belong to axvisor, complete the IRQ. // plic.complete(this_context(), irq); Some(irq.get() as usize) @@ -253,4 +261,4 @@ impl IrqIf for IrqIfImpl { } } } -} \ No newline at end of file +} diff --git a/platform/riscv64-qemu-virt/src/lib.rs b/platform/riscv64-qemu-virt/src/lib.rs index 067407bb1..32d538179 100644 --- a/platform/riscv64-qemu-virt/src/lib.rs +++ b/platform/riscv64-qemu-virt/src/lib.rs @@ -5,11 +5,12 @@ extern crate log; #[macro_use] extern crate axplat; +#[cfg(feature = "irq")] +pub mod irq; + mod boot; mod console; mod init; -#[cfg(feature = "irq")] -mod irq; mod mem; mod power; mod time; diff --git a/platform/riscv64-qemu-virt/src/mem.rs b/platform/riscv64-qemu-virt/src/mem.rs index 09930ff14..13000df04 100644 --- a/platform/riscv64-qemu-virt/src/mem.rs +++ b/platform/riscv64-qemu-virt/src/mem.rs @@ -55,4 +55,4 @@ impl MemIf for MemIfImpl { crate::config::plat::KERNEL_ASPACE_SIZE, ) } -} \ No newline at end of file +} diff --git a/platform/riscv64-qemu-virt/src/power.rs b/platform/riscv64-qemu-virt/src/power.rs index bf6c5aaf5..db9e2e0c6 100644 --- a/platform/riscv64-qemu-virt/src/power.rs +++ b/platform/riscv64-qemu-virt/src/power.rs @@ -29,4 +29,4 @@ impl PowerIf for PowerImpl { axcpu::asm::halt(); } } -} \ No newline at end of file +} diff --git a/platform/riscv64-qemu-virt/src/time.rs b/platform/riscv64-qemu-virt/src/time.rs index 121d1e544..5448f73ec 100644 --- a/platform/riscv64-qemu-virt/src/time.rs +++ b/platform/riscv64-qemu-virt/src/time.rs @@ -68,4 +68,4 @@ impl TimeIf for TimeIfImpl { fn set_oneshot_timer(deadline_ns: u64) { sbi_rt::set_timer(Self::nanos_to_ticks(deadline_ns)); } -} \ No newline at end of file +} diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index 93cd532b8..3b8c84885 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -1,5 +1,5 @@ -use std::{fs, path::PathBuf}; use ostool::build::CargoRunnerKind; +use std::{fs, path::PathBuf}; use crate::ctx::Context; @@ -13,7 +13,7 @@ impl Context { Arch::X86_64 } else if build_config.target.contains("riscv64") { Arch::Riscv64 - }else { + } else { return Err(anyhow::anyhow!( "Unsupported target architecture: {}", build_config.target diff --git a/xtask/src/image.rs b/xtask/src/image.rs index 4d9eb76bb..044350d10 100644 --- a/xtask/src/image.rs +++ b/xtask/src/image.rs @@ -46,25 +46,21 @@ pub struct ImageArgs { pub enum ImageCommands { /// List all available images Ls, - /// Download the specified image and automatically extract it Download { /// Name of the image to download image_name: String, - /// Output directory for the downloaded image #[arg(short, long)] output_dir: Option, - /// Do not extract after download #[arg(long, help = "Do not extract after download")] no_extract: bool, }, - /// Remove the specified image from temp directory Rm { /// Name of the image to remove - image_name: String + image_name: String, }, } @@ -82,133 +78,140 @@ impl Image { pub const EVM3588_ARCEOS: Self = Self { name: "evm3588_arceos", description: "ArceOS for EVM3588 development board", - sha256: "c9f197408f14f2cd9d3b9d2e077a9e91d233479713cb24d5280f7dc5562ae800", + sha256: "b1627aa216a98ffa56ce2295252e4d293125184ca394d5e409a1c711bd9a8eff", arch: "aarch64", }; pub const EVM3588_LINUX: Self = Self { name: "evm3588_linux", description: "Linux for EVM3588 development board", - sha256: "cc12be121e75b0eb6588a774106582ee7c7b279895d73558f31ce34712a8fea3", + sha256: "bce8a1a0ed22215f6d98456574009ad4678c6dfda7c5fe8dfcdf15d59f858ef1", arch: "aarch64", }; pub const ORANGEPI_ARCEOS: Self = Self { name: "orangepi_arceos", description: "ArceOS for Orange Pi development board", - sha256: "2a95477e1e18d9ca95f666de93cd8ba53ffafb3f285fbdf4fde1e0cdfb0d8f1d", + sha256: "cdef0112156bf1d630f8d46d1fd60f45790f99cb8a656a8b716a9efe7958dbc1", arch: "aarch64", }; pub const ORANGEPI_LINUX: Self = Self { name: "orangepi_linux", description: "Linux for Orange Pi development board", - sha256: "7a1fd69f10dd223988c436ea461bed15ddae4351fc7a47fb7b3fee9792afac86", + sha256: "f50a6eaf730a5e962f0ded5474b32cc0b6905e1ee796ac77a1cf83d0576ffbb9", arch: "aarch64", }; pub const PHYTIUMPI_ARCEOS: Self = Self { name: "phytiumpi_arceos", description: "ArceOS for Phytium Pi development board", - sha256: "c774824e36319f2f20575e488861a61c6ef7a5d2e5f219edd03a2c3c29ca3d05", + sha256: "8b7b76d60f2868eeaf6b5d28c067bef6307372f31b56b11bbc7999c635d614e5", arch: "aarch64", }; pub const PHYTIUMPI_LINUX: Self = Self { name: "phytiumpi_linux", description: "Linux for Phytium Pi development board", - sha256: "78a27021b76b6d20a5420938473cf92ac59dc4674d528295b75ecfabdf9bea69", + sha256: "792d5a6645f773226f6758133b2be4887eceaf713321dcfec7fdffe4412bc46d", + arch: "aarch64", + }; + + pub const PHYTIUMPI_RTTHREAD: Self = Self { + name: "phytiumpi_rtthread", + description: "RT-Thread for Phytium Pi development board", + sha256: "96ec4bfce212a183b0ca3c6330a815c9e273b393da753d871498672c38815dd8", arch: "aarch64", }; pub const QEMU_AARCH64_ARCEOS: Self = Self { name: "qemu_aarch64_arceos", description: "ArceOS for QEMU aarch64 virtualization", - sha256: "56c1f517444dcd6668f0d4bc280543d6f236728c4ec5b81e7e5b5a06cf012690", + sha256: "f2422b89de216e595256b1d18b9305cc1f6d1f3b4d3e50016c50ac9126bf804b", arch: "aarch64", }; pub const QEMU_AARCH64_LINUX: Self = Self { name: "qemu_aarch64_linux", description: "Linux for QEMU aarch64 virtualization", - sha256: "ffccd4f89ee84def89ab66e23249d30723fd4a9af7896d7ef4d6f6d75d34225b", + sha256: "9e2a19e00a72962c4aac53ad8d6172ca473a93d321e3bb3f49a979264754e397", arch: "aarch64", }; pub const QEMU_AARCH64_NIMBOS: Self = Self { name: "qemu_aarch64_nimbos", description: "NIMBOS for QEMU aarch64 virtualization", - sha256: "283681356af35e141bcf050dd56aa698966477289c21ac49941bb68d9a9ad1b8", + sha256: "77d50bc1b5df282e31ed3780dd2d09691026836144c0ce6f46ef99301f5aaea8", arch: "aarch64", }; pub const QEMU_RISCV64_ARCEOS: Self = Self { name: "qemu_riscv64_arceos", description: "ArceOS for QEMU riscv64 virtualization", - sha256: "19248561c242a06a893a6a4debfc05ba5ca3e438347814c10351eecef88e54be", + sha256: "1c6a63c1ff1713aa7aaac61a07c2c43e94cace404a8f9e5808275d5335526b0b", arch: "riscv64", }; pub const QEMU_RISCV64_LINUX: Self = Self { name: "qemu_riscv64_linux", description: "Linux for QEMU riscv64 virtualization", - sha256: "34a355907bf3b05ea3949207fd98aad05d91c21d3b724d345f54576ef6e12eba", + sha256: "86ec223dcdd0cd39210e961d1d6cac0179f03c9a179b6514cd787410bf766d61", arch: "riscv64", }; pub const QEMU_RISCV64_NIMBOS: Self = Self { name: "qemu_riscv64_nimbos", description: "NIMBOS for QEMU riscv64 virtualization", - sha256: "064f75df290905687221b2554dd4e4efc077a6a95cafcbf7f98e2181441c24e3", + sha256: "ce75be7bb5dc6ca1df3a23a055100adbd309da0cc65e0dc669a35f81e369249f", arch: "riscv64", }; pub const QEMU_X86_64_ARCEOS: Self = Self { name: "qemu_x86_64_arceos", description: "ArceOS for QEMU x86_64 virtualization", - sha256: "ee9bdd4f6ae3ef2ee807ac712b82318329eeb6d1cffdf737f98a393ad730b5d9", + sha256: "64538c2330e7658a381902af448cda72e14d9b7971995eb6c4ec5cf8bcaa622b", arch: "x86_64", }; pub const QEMU_X86_64_LINUX: Self = Self { name: "qemu_x86_64_linux", description: "Linux for QEMU x86_64 virtualization", - sha256: "1a27da24b02f836b259462d5c73dc550553ea708d24af299378137beedc46c51", + sha256: "8a04e36174dd0b0ea386c261d1fee59d47cfbd2e5f0f2a48c53f99c4deead0a5", arch: "x86_64", }; pub const QEMU_X86_64_NIMBOS: Self = Self { name: "qemu_x86_64_nimbos", description: "NIMBOS for QEMU x86_64 virtualization", - sha256: "55d73898f9f98fca80e15387b1e5149ba6bbf74d3631281ea1ece75de3529078", + sha256: "dddfa6067af453d49f2925fe75514779549041ba78bb390a54aab1c836ed6c63", arch: "x86_64", }; pub const ROC_RK3568_PC_ARCEOS: Self = Self { name: "roc-rk3568-pc_arceos", description: "ArceOS for ROC-RK3568-PC development board", - sha256: "4dd2f727c2a46ff1e64632616c308c9504ef5ddb4b519acf3f69c928e4475ca7", + sha256: "5ec4721d449d302ce6e00ec6efeb8e5284e1ed2051e4a417229a1464d991469c", arch: "aarch64", }; pub const ROC_RK3568_PC_LINUX: Self = Self { name: "roc-rk3568-pc_linux", description: "Linux for ROC-RK3568-PC development board", - sha256: "73feb8b84473603252dbadc4c81446f9a68098bd899fd524ec26f68761a35cf8", + sha256: "1c47b0fa63f47c6e02450a4c4bac59d74c486a55f72b032c1cf5f4a432e2973e", arch: "aarch64", }; pub const TAC_E400_PLC_ARCEOS: Self = Self { name: "tac-e400-plc_arceos", description: "ArceOS for TAC-E400-PLC industrial control board", - sha256: "a2504506c81871c84ba421a94f77028f067c5589886f37c0c389a545d7e57aeb", + sha256: "0b249ff8daeb1070b4518f09ef46b05266322594907afd0ed2b21b83810ca6ab", arch: "aarch64", }; pub const TAC_E400_PLC_LINUX: Self = Self { name: "tac-e400-plc_linux", description: "Linux for TAC-E400-PLC industrial control board", - sha256: "920743161a73da228e714d71f55d8ba77b91ed37092d4f80e774f4e809b34403", + sha256: "a639c5bdb2d1ed07907085e2d181be2d5f2f5433f7fe64bcae8d8d86ddf657f7", arch: "aarch64", }; @@ -221,6 +224,7 @@ impl Image { Self::ORANGEPI_LINUX, Self::PHYTIUMPI_ARCEOS, Self::PHYTIUMPI_LINUX, + Self::PHYTIUMPI_RTTHREAD, Self::QEMU_AARCH64_ARCEOS, Self::QEMU_RISCV64_ARCEOS, Self::QEMU_X86_64_ARCEOS, @@ -433,12 +437,16 @@ async fn image_download(image_name: &str, output_dir: Option, extract: b Ok(false) => { // Remove the invalid downloaded file let _ = fs::remove_file(&output_path); - return Err(anyhow!("Download completed but file SHA256 verification failed")); + return Err(anyhow!( + "Download completed but file SHA256 verification failed" + )); } Err(e) => { // Remove the potentially corrupted downloaded file let _ = fs::remove_file(&output_path); - return Err(anyhow!("Download completed but error verifying downloaded file: {e}")); + return Err(anyhow!( + "Download completed but error verifying downloaded file: {e}" + )); } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 68390dc7c..3c5631abf 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -55,15 +55,12 @@ struct QemuArgs { /// Path to custom build configuration file (TOML format) #[arg(long)] build_config: Option, - /// Path to custom QEMU configuration file #[arg(long)] qemu_config: Option, - /// Comma-separated list of VM configuration files #[arg(long)] vmconfigs: Vec, - #[command(flatten)] build: BuildArgs, } @@ -73,23 +70,18 @@ struct ClippyArgs { /// Only check specific packages (comma separated) #[arg(long)] packages: Option, - /// Only check specific targets (comma separated) #[arg(long)] targets: Option, - /// Continue on error instead of exiting immediately #[arg(long)] continue_on_error: bool, - /// Dry run - show what would be checked without running clippy #[arg(long)] dry_run: bool, - /// Automatically fix clippy warnings where possible #[arg(long)] fix: bool, - /// Allow fixing when the working directory is dirty (has uncommitted changes) #[arg(long)] allow_dirty: bool, @@ -100,11 +92,9 @@ struct UbootArgs { /// Path to custom build configuration file (TOML format) #[arg(long)] build_config: Option, - /// Path to custom U-Boot configuration file #[arg(long)] uboot_config: Option, - /// Comma-separated list of VM configuration files #[arg(long)] vmconfigs: Vec, diff --git a/xtask/src/menuconfig.rs b/xtask/src/menuconfig.rs index 199b440f4..0460dc474 100644 --- a/xtask/src/menuconfig.rs +++ b/xtask/src/menuconfig.rs @@ -10,7 +10,10 @@ impl Context { let config_path = self.ctx.paths.workspace.join(".build.toml"); if config_path.exists() { - println!("\nCurrent .build.toml configuration file: {}", config_path.display()); + println!( + "\nCurrent .build.toml configuration file: {}", + config_path.display() + ); } else { println!("\nNo .build.toml configuration file found, will use default configuration"); } From 8a321fd079d19200f5bc8d848f1dfed01a7a69fa Mon Sep 17 00:00:00 2001 From: ZCShou <72115@163.com> Date: Tue, 3 Feb 2026 13:27:03 +0800 Subject: [PATCH 044/132] feat: refactor CI workflows and add config file --- .github/config.json | 13 ++ .github/workflows/check.yml | 39 ++++-- .github/workflows/deploy.yml | 71 ++++++++--- .github/workflows/release.yml | 226 ++++++++++++++++------------------ .github/workflows/test.yml | 39 +++++- 5 files changed, 238 insertions(+), 150 deletions(-) create mode 100644 .github/config.json diff --git a/.github/config.json b/.github/config.json new file mode 100644 index 000000000..f347e101e --- /dev/null +++ b/.github/config.json @@ -0,0 +1,13 @@ +{ + "targets": [ + "aarch64-unknown-none-softfloat", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-none", + "riscv64gc-unknown-none-elf" + ], + "rust_components": [ + "rust-src", + "clippy", + "rustfmt" + ] +} diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a6ee3b08e..330fa15e9 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,20 +1,42 @@ name: Quality Checks on: + push: + branches: + - '**' + tags-ignore: + - '**' + pull_request: workflow_call: jobs: + load-config: + name: Load CI Configuration + runs-on: ubuntu-latest + outputs: + targets: ${{ steps.config.outputs.targets }} + rust_components: ${{ steps.config.outputs.rust_components }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Load configuration + id: config + run: | + TARGETS=$(jq -c '.targets' .github/config.json) + COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) + + echo "targets=$TARGETS" >> $GITHUB_OUTPUT + echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT + check: - name: Quality Checks + name: Check runs-on: ubuntu-latest + needs: load-config strategy: fail-fast: false matrix: - target: - - x86_64-unknown-linux-gnu - - x86_64-unknown-none - - riscv64gc-unknown-none-elf - - aarch64-unknown-none-softfloat + target: ${{ fromJson(needs.load-config.outputs.targets) }} steps: - name: Checkout code @@ -23,8 +45,7 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly with: - toolchain: nightly-2025-05-20 - components: rust-src, clippy, rustfmt + components: ${{ needs.load-config.outputs.rust_components }} targets: ${{ matrix.target }} - name: Check rust version @@ -37,7 +58,7 @@ jobs: run: cargo build --target ${{ matrix.target }} --all-features - name: Run clippy - run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings -A clippy::new_without_default + run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings - name: Build documentation env: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d43864156..882ba9310 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,12 +2,8 @@ name: Deploy on: push: - branches: - - '**' - tags-ignore: - - 'v*' - - 'v*-pre.*' - pull_request: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' permissions: contents: read @@ -23,45 +19,90 @@ env: RUST_BACKTRACE: 1 jobs: - quality-check: + verify-tag: + name: Verify Tag + runs-on: ubuntu-latest + outputs: + should_deploy: ${{ steps.check.outputs.should_deploy }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check if tag is on main or master branch + id: check + run: | + git fetch origin main master || true + BRANCHES=$(git branch -r --contains ${{ github.ref }}) + + if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then + echo "✓ Tag is on main or master branch" + echo "should_deploy=true" >> $GITHUB_OUTPUT + else + echo "✗ Tag is not on main or master branch, skipping deployment" + echo "Tag is on: $BRANCHES" + echo "should_deploy=false" >> $GITHUB_OUTPUT + fi + + - name: Verify version consistency + if: steps.check.outputs.should_deploy == 'true' + run: | + # Extract version from git tag (remove 'v' prefix) + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + # Extract version from Cargo.toml + CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') + echo "Git tag version: $TAG_VERSION" + echo "Cargo.toml version: $CARGO_VERSION" + if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then + echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" + exit 1 + fi + echo "✓ Version check passed!" + + check: uses: ./.github/workflows/check.yml + needs: verify-tag + if: needs.verify-tag.outputs.should_deploy == 'true' test: uses: ./.github/workflows/test.yml + needs: verify-tag + if: needs.verify-tag.outputs.should_deploy == 'true' - build-doc: + build: name: Build documentation runs-on: ubuntu-latest - needs: quality-check + needs: [verify-tag, check, test] + if: needs.verify-tag.outputs.should_deploy == 'true' steps: - name: Checkout code uses: actions/checkout@v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly - with: - toolchain: nightly-2025-05-20 - name: Build docs env: RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs run: | cargo doc --no-deps --all-features - printf '' > target/doc/index.html + printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: target/doc - deploy-doc: + deploy: name: Deploy to GitHub Pages environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest - needs: build-doc - if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + needs: [verify-tag, build] + if: needs.verify-tag.outputs.should_deploy == 'true' steps: - name: Deploy to GitHub Pages id: deployment diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c92d84fa6..2e857b48b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,100 +3,96 @@ name: Release on: push: tags: - - 'v*.*.*' - - 'v*.*.*-pre.*' + - 'v[0-9]+.[0-9]+.[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' permissions: contents: write -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - jobs: - check: - uses: ./.github/workflows/check.yml - - test: - uses: ./.github/workflows/test.yml - needs: check - - create-release: - name: Create GitHub Release + verify-tag: + name: Verify Tag runs-on: ubuntu-latest - needs: check - + outputs: + should_release: ${{ steps.check.outputs.should_release }} + is_prerelease: ${{ steps.check.outputs.is_prerelease }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - - - name: Validate tag and branch (HEAD-based) - shell: bash + + - name: Check tag type and branch + id: check run: | - set -e - + git fetch origin main master dev || true + TAG="${{ github.ref_name }}" - TAG_COMMIT=$(git rev-list -n 1 "$TAG") - - git fetch origin main dev - - MAIN_HEAD=$(git rev-parse origin/main) - DEV_HEAD=$(git rev-parse origin/dev) - - echo "Tag: $TAG" - echo "Tag commit: $TAG_COMMIT" - echo "main HEAD: $MAIN_HEAD" - echo "dev HEAD: $DEV_HEAD" - - if [[ "$TAG" == *-pre.* ]]; then - if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then - echo "❌ prerelease tag must be created from dev HEAD" - exit 1 + BRANCHES=$(git branch -r --contains ${{ github.ref }}) + + echo "Tag: $TAG" + echo "Branches containing this tag: $BRANCHES" + + # Check if it's a prerelease tag + if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[0-9]+$ ]]; then + echo "📦 Detected prerelease tag" + echo "is_prerelease=true" >> $GITHUB_OUTPUT + + if echo "$BRANCHES" | grep -q 'origin/dev'; then + echo "✓ Prerelease tag is on dev branch" + echo "should_release=true" >> $GITHUB_OUTPUT + else + echo "✗ Prerelease tag must be on dev branch, skipping release" + echo "should_release=false" >> $GITHUB_OUTPUT fi - echo "✅ prerelease tag validated on dev" - else - if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then - echo "❌ stable release tag must be created from main HEAD" - exit 1 + elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "📦 Detected stable release tag" + echo "is_prerelease=false" >> $GITHUB_OUTPUT + + if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then + echo "✓ Stable release tag is on main or master branch" + echo "should_release=true" >> $GITHUB_OUTPUT + else + echo "✗ Stable release tag must be on main or master branch, skipping release" + echo "should_release=false" >> $GITHUB_OUTPUT fi - echo "✅ stable release tag validated on main" + else + echo "✗ Unknown tag format, skipping release" + echo "is_prerelease=false" >> $GITHUB_OUTPUT + echo "should_release=false" >> $GITHUB_OUTPUT fi - + - name: Verify version consistency + if: steps.check.outputs.should_release == 'true' run: | # Extract version from git tag (remove 'v' prefix) TAG_VERSION="${{ github.ref_name }}" TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml using cargo metadata - CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version') + # Extract version from Cargo.toml + CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') echo "Git tag version: $TAG_VERSION" echo "Cargo.toml version: $CARGO_VERSION" if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" exit 1 fi - echo "Version check passed!" + echo "✓ Version check passed!" - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ contains(github.ref_name, '-pre.') }} - body: | - ## ${{ github.ref_name }} + check: + uses: ./.github/workflows/check.yml + needs: verify-tag + if: needs.verify-tag.outputs.should_release == 'true' - - [Documentation](https://docs.rs/axvisor_api) - - [crates.io](https://crates.io/crates/axvisor_api) - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + test: + uses: ./.github/workflows/test.yml + needs: [verify-tag, check] + if: needs.verify-tag.outputs.should_release == 'true' - publish-crates: - name: Publish to crates.io + release: + name: Create GitHub Release runs-on: ubuntu-latest - needs: check + needs: [verify-tag, check] + if: needs.verify-tag.outputs.should_release == 'true' steps: - name: Checkout code @@ -104,71 +100,61 @@ jobs: with: fetch-depth: 0 - - name: Validate tag and branch (HEAD-based) - shell: bash + - name: Generate release notes + id: release_notes run: | - set -e - - TAG="${{ github.ref_name }}" - TAG_COMMIT=$(git rev-list -n 1 "$TAG") - - git fetch origin main dev - - MAIN_HEAD=$(git rev-parse origin/main) - DEV_HEAD=$(git rev-parse origin/dev) - - echo "Tag: $TAG" - echo "Tag commit: $TAG_COMMIT" - echo "main HEAD: $MAIN_HEAD" - echo "dev HEAD: $DEV_HEAD" - - if [[ "$TAG" == *-pre.* ]]; then - if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then - echo "❌ prerelease tag must be created from dev HEAD" - exit 1 - fi - echo "✅ prerelease tag validated on dev" + CURRENT_TAG="${{ github.ref_name }}" + + # Get previous tag + PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) + + if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then + echo "No previous tag found, this is the first release" + CHANGELOG="Initial release" else - if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then - echo "❌ stable release tag must be created from main HEAD" - exit 1 + echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" + + # Generate changelog with commit messages + CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") + + if [ -z "$CHANGELOG" ]; then + CHANGELOG="No changes" fi - echo "✅ stable release tag validated on main" - fi - - - name: Verify version consistency - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml using cargo metadata - CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 fi - echo "Version check passed!" + + # Write changelog to output file (multi-line) + { + echo "changelog<> $GITHUB_OUTPUT - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 with: - toolchain: nightly-2025-05-20 + draft: false + prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} + body: | + ## Changes + ${{ steps.release_notes.outputs.changelog }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Workspace crates must be published in dependency order - - name: Dry run publish (axvisor_api_proc) - run: cargo publish --dry-run -p axvisor_api_proc + publish: + name: Publish to crates.io + runs-on: ubuntu-latest + needs: [verify-tag, check] + if: needs.verify-tag.outputs.should_release == 'true' - - name: Dry run publish (axvisor_api) - run: cargo publish --dry-run -p axvisor_api + steps: + - name: Checkout code + uses: actions/checkout@v4 - - name: Publish axvisor_api_proc to crates.io - run: cargo publish -p axvisor_api_proc --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly - # Wait for crates.io to index the proc-macro crate - - name: Wait for crates.io indexing - run: sleep 30 + - name: Dry run publish + run: cargo publish --dry-run - - name: Publish axvisor_api to crates.io - run: cargo publish -p axvisor_api --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + - name: Publish to crates.io + run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d8abfaf8a..dc3b293d9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,23 +1,50 @@ name: Test on: + push: + branches: + - '**' + tags-ignore: + - '**' + pull_request: workflow_call: jobs: + load-config: + name: Load CI Configuration + runs-on: ubuntu-latest + outputs: + targets: ${{ steps.config.outputs.targets }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Load configuration + id: config + run: | + TARGETS=$(jq -c '.targets' .github/config.json) + echo "targets=$TARGETS" >> $GITHUB_OUTPUT + test: name: Test runs-on: ubuntu-latest + needs: load-config + strategy: + fail-fast: false + matrix: + target: ${{ fromJson(needs.load-config.outputs.targets) }} + steps: - name: Checkout code uses: actions/checkout@v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly - with: - toolchain: nightly-2025-05-20 - - name: Run tests - run: cargo test --all-features -- --nocapture + # - name: Run tests + # run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture - - name: Run doc tests - run: cargo test --doc --all-features + # - name: Run doc tests + # run: cargo test --target ${{ matrix.target }} --doc + - name: Run tests + run: echo "Tests are skipped!" From e08a49543b52d06f64dab84a0dd82cfec7768e17 Mon Sep 17 00:00:00 2001 From: ZCShou <72115@163.com> Date: Tue, 3 Feb 2026 13:28:53 +0800 Subject: [PATCH 045/132] chore: update license to Apache-2.0 and add SPDX headers (#9) --- LICENSE.Apache2 => LICENSE | 2 +- LICENSE.GPLv3 | 674 ------------------------------------ LICENSE.MulanPSL2 | 127 ------- LICENSE.MulanPubL2 | 183 ---------- README.md | 4 + axvisor_api_proc/README.md | 6 +- axvisor_api_proc/src/lib.rs | 14 + examples/example.rs | 14 + src/arch.rs | 14 + src/host.rs | 14 + src/lib.rs | 14 + src/memory.rs | 14 + src/test.rs | 14 + src/time.rs | 14 + src/vmm.rs | 14 + 15 files changed, 136 insertions(+), 986 deletions(-) rename LICENSE.Apache2 => LICENSE (99%) delete mode 100644 LICENSE.GPLv3 delete mode 100644 LICENSE.MulanPSL2 delete mode 100644 LICENSE.MulanPubL2 diff --git a/LICENSE.Apache2 b/LICENSE similarity index 99% rename from LICENSE.Apache2 rename to LICENSE index 261eeb9e9..b1a313f58 100644 --- a/LICENSE.Apache2 +++ b/LICENSE @@ -48,7 +48,7 @@ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner + submitted to the Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent diff --git a/LICENSE.GPLv3 b/LICENSE.GPLv3 deleted file mode 100644 index f288702d2..000000000 --- a/LICENSE.GPLv3 +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. 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 -them 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 prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. 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. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey 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; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If 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 convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU 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 that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - 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. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -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. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - 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 -state 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 3 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, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program 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, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU 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. But first, please read -. diff --git a/LICENSE.MulanPSL2 b/LICENSE.MulanPSL2 deleted file mode 100644 index 5729580d9..000000000 --- a/LICENSE.MulanPSL2 +++ /dev/null @@ -1,127 +0,0 @@ - 木兰宽松许可证, 第2版 - - 木兰宽松许可证, 第2版 - 2020年1月 http://license.coscl.org.cn/MulanPSL2 - - - 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: - - 0. 定义 - - “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 - - “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 - - “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 - - “法人实体”是指提交贡献的机构及其“关联实体”。 - - “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 - - 1. 授予版权许可 - - 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 - - 2. 授予专利许可 - - 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 - - 3. 无商标许可 - - “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 - - 4. 分发限制 - - 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 - - 5. 免责声明与责任限制 - - “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 - - 6. 语言 - “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 - - 条款结束 - - 如何将木兰宽松许可证,第2版,应用到您的软件 - - 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: - - 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; - - 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; - - 3, 请将如下声明文本放入每个源文件的头部注释中。 - - Copyright (c) [Year] [name of copyright holder] - [Software Name] is licensed under Mulan PSL v2. - You can use this software according to the terms and conditions of the Mulan PSL v2. - You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - See the Mulan PSL v2 for more details. - - - Mulan Permissive Software License,Version 2 - - Mulan Permissive Software License,Version 2 (Mulan PSL v2) - January 2020 http://license.coscl.org.cn/MulanPSL2 - - Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: - - 0. Definition - - Software means the program and related documents which are licensed under this License and comprise all Contribution(s). - - Contribution means the copyrightable work licensed by a particular Contributor under this License. - - Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. - - Legal Entity means the entity making a Contribution and all its Affiliates. - - Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. - - 1. Grant of Copyright License - - Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. - - 2. Grant of Patent License - - Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. - - 3. No Trademark License - - No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. - - 4. Distribution Restriction - - You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. - - 5. Disclaimer of Warranty and Limitation of Liability - - THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - - 6. Language - - THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. - - END OF THE TERMS AND CONDITIONS - - How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software - - To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: - - i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; - - ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; - - iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. - - - Copyright (c) [Year] [name of copyright holder] - [Software Name] is licensed under Mulan PSL v2. - You can use this software according to the terms and conditions of the Mulan PSL v2. - You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - See the Mulan PSL v2 for more details. diff --git a/LICENSE.MulanPubL2 b/LICENSE.MulanPubL2 deleted file mode 100644 index 2fcd23f58..000000000 --- a/LICENSE.MulanPubL2 +++ /dev/null @@ -1,183 +0,0 @@ - 木兰公共许可证, 第2版 - -木兰公共许可证, 第2版 - -2021年5月 http://license.coscl.org.cn/MulanPubL-2.0 - -您对“贡献”的复制、使用、修改及分发受木兰公共许可证,第2版(以下简称“本许可证”)的如下条款的约束: - -0. 定义 - -“贡献” 是指由“贡献者”许可在“本许可证”下的受版权法保护的作品,包括最初“贡献者”许可在“本许可证”下的作品及后续“贡献者”许可在“本许可证”下的“衍生作品”。 - -“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 - -“法人实体” 是指提交贡献的机构及其“关联实体”。 - -“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的“控制”是指拥有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 - -“衍生作品” 是指基于“贡献”创作的作品,具体包括对全部或部分“贡献”进行修改、重写、翻译、注释、组合或与之链接(包括动态链接或静态链接)而形成的作品。仅与“贡献”进行进程间通信或系统调用的作品是独立作品,不属于“衍生作品”。 - -“对应源代码” 是指生成、安装和(对于可执行作品)运行目标代码所需的所有源文件和与之关联的接口定义文件,以及控制这些活动的脚本,但不包括编译环境、编译工具、云服务平台(如果有)。 - -“分发” 是指通过任何媒介向他人提供“贡献”或“衍生作品”的行为,以及利用“贡献”或“衍生作品”通过网络远程给用户提供服务的行为,例如:通过利用“贡献”或“衍生作品”搭建的云服务平台提供在线服务的行为。 - -1. 授予版权许可 - -每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、“分发”其“贡献”或“衍生作品”,不论修改与否。 - -2. 授予专利许可 - -每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销的情形除外)专利许可,供您使用、制造、委托制造、销售、许诺销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”中的专利权利要求,而不包括仅因您对“贡献”的修改而将必然会侵犯到的专利权利要求。如果您或您的“关联实体”直接或间接地,就“贡献”对任何人发起专利侵权诉讼(包括在诉讼中提出反诉请求或交叉请求)或发起其他专利维权行动,则“贡献者”根据“本许可证”授予您的专利许可自您发起专利诉讼或专利维权行动之日终止。 - -3. 无商标许可 - -“贡献者”在“本许可证”下不提供对其商品名称、商标、服务标识或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用的情形除外。 - -4. 分发限制 - -您可以将您接收到的“贡献”或您的“衍生作品”以源程序形式或可执行形式重新“分发”,但必须满足下列条件: - -(1)您必须向接收者提供“本许可证”的副本,并保留“贡献”中的版权、商标、专利及免责声明;并且, - -(2)如果您“分发”您接收到的“贡献”,您必须使用“本许可证”提供该“贡献”的源代码副本;如果您 “分发”您的“衍生作品”,您必须: - -(i)随“衍生作品”提供使用“本许可证”“分发”的您的“衍生作品”的“对应源代码”。如果您通过下载链接提供前述“对应源代码”,则您应将下载链接地址置于“衍生作品”或其随附文档中的明显位置,有效期自该“衍生作品”“分发”之日起不少于三年,并确保接收者可以获得“对应源代码”;或者, - -(ii)随“衍生作品”向接收者提供一个书面要约,表明您愿意提供根据“本许可证”“分发”的您“衍生作品”的“对应源代码”。该书面要约应置于“衍生作品”中的明显位置,并确保接收者根据书面要约可获取“对应源代码”的时间从您接到该请求之日起不得超过三个月,且有效期自该“衍生作品”“分发”之日起不少于三年。 - -5. 违约与终止 - -如果您违反“本许可证”,任何“贡献者”有权书面通知您终止其根据“本许可证”授予您的许可。该“贡献者”授予您的许可自您接到其终止通知之日起终止。仅在如下两种情形下,即使您收到“贡献者”的通知也并不终止其授予您的许可: - -(1)您在接到该终止通知之前已停止所有违反行为; - -(2)您是首次收到该“贡献者”根据“本许可证”发出的书面终止通知,并且您在收到该通知后30天内已停止所有违反行为。 - -只要您下游的接收者遵守“本许可证”的相关规定,即使您在“本许可证”下被授予的许可终止,不影响下游的接收者根据“本许可证”享有的权利。 - -6. 例外 - -如果您将“贡献”与采用GNU AFFERO GENERAL PUBLIC LICENSE Version 3(以下简称“AGPLv3”)或其后续版本的作品结合形成新的“衍生作品”,且根据“AGPLv3”或其后续版本的要求您有义务将新形成的“衍生作品”以“AGPLv3”或其后续版本进行许可的,您可以根据“AGPLv3”或其后续版本进行许可,只要您在“分发”该“衍生作品”的同时向接收者提供“本许可证”的副本,并保留“贡献”中的版权、商标、专利及免责声明。但任何“贡献者”不会因您选择“AGPLv3”或其后续版本而授予该“衍生作品”的接收者更多权利。 - -7. 免责声明与责任限制 - -“贡献”在提供时不带有任何明示或默示的担保。在任何情况下,“贡献者”或版权人不对任何人因使用“贡献”而引发的任何直接或间接损失承担任何责任,不论该等损失因何种原因导致或者基于何种法律理论,即使其曾被告知有该等损失的可能性。 - -8. 语言 - -“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何不一致,以中文版为准。 - -条款结束 - -如何将木兰公共许可证,第2版,应用到您的软件 - -如果您希望将木兰公共许可证,第2版,应用到您的软件,为了方便接收者查阅,建议您完成如下三步: - -1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; - -2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; - -3, 请将如下声明文本放入每个源文件的头部注释中。 - -Copyright (c) [Year] [name of copyright holder] -[Software Name] is licensed under Mulan PubL v2. -You can use this software according to the terms and conditions of the Mulan PubL v2. -You may obtain a copy of Mulan PubL v2 at: - http://license.coscl.org.cn/MulanPubL-2.0 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PubL v2 for more details. - - - Mulan Public License,Version 2 - -Mulan Public License,Version 2 (Mulan PubL v2) - -May 2021 http://license.coscl.org.cn/MulanPubL-2.0 - -Your reproduction, use, modification and Distribution of the Contribution shall be subject to Mulan Public License, Version 2 (this License) with following terms and conditions: - -0. Definition - -Contribution means the copyrightable work licensed by a particular Contributor under this License, including the work licensed by the initial Contributor under this License and its Derivative Work licensed by any subsequent Contributor under this License. - -Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. - -Legal Entity means the entity making a Contribution and all its Affiliates. - -Affiliates mmeans entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. - -Derivative Work means works created based on Contribution, specifically including works formed by modifying, rewriting, translating, annotating, combining or linking to all or part of Contribution (including dynamic linking or static linking). Works which only communicate with Contribution through inter-process communication or system call, are independent works, rather than Derivative Work. - -Corresponding Source Code means all the source code needed to generate, install, and (for an executable work) run the object code including the interface definition files associated with source files for the work, and scripts to control those activities, excluding of compilation environment and compilation tools, cloud services platform (if any). - -Distribute (or Distribution) means the act of making the Contribution or Derivative Work available to others through any medium, and using the Contribution or Derivative Work to provide online services to users, such as the act of providing online services through a cloud service platform built using Contributions or Derivative Works. - -1. Grant of Copyright License - -Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or Distribute its Contribution or Derivative Work, with modification or not. - -2. Grant of Patent License - -Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to use, make, have made, sell, offer for sale, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, excluding of any patent claims solely be infringed by your modification. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that any Contribution infringes patents, then any patent license granted to you under this License for the Contribution shall terminate as of the date such litigation or activity is filed or taken. - -3. No Trademark License - -No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. - -4. Distribution Restriction - -You may Distribute the Contribution you received or your Derivative Work, whether in source or executable forms, provided that you meet the following conditions: - -1) You must provide recipients with a copy of this License and retain copyright, trademark, patent and disclaimer statements in the Contribution; and, - -2) If you Distribute the Contribution you received, you must provide copies of the Contribution’s source code under this License; - -If you Distribute your Derivative Work, you have to: - -(i) accompanying the Derivative work, provide recipients with Corresponding Source Code of your Derivative Work under this License. If you provide the Corresponding Source Code through a download link, you should place such link address prominently in the Derivative Work or its accompanying documents, and be valid no less than three years from your Distribution of the particular Derivative Work, and ensure that the recipients can acquire the Corresponding Source Code through the link; or, - -(ii) accompanying the Derivative Work, provide recipients with a written offer indicating your willingness to provide the Corresponding Source Code of the Derivative Work licensed under this License. Such written offer shall be placed prominently in the Derivative Work or its accompanying documents. Without reasonable excuse, the recipient shall be able to acquire the Corresponding Source code of the Derivative work for no more than three months from your receipt of a valid request, and be valid no less than three years from your Distribution of the particular Derivative Work. - -5. Breach and Termination - -If you breach this License, any Contributor has the right to notify you in writing to terminate its license granted to you under this License. The license granted to you by such Contributor terminates upon your receipt of such notice of termination. Notwithstanding the foregoing, your license will not be terminated even if you receive a notice of termination from Contributor, provided that: - -1) you have cured all the breaches prior to receiving such notice of termination; or, - -2) it’s your first time to receive a notice of termination from such Contributor pursuant to this License, and you have cured all the breaches within 30 days of receipt of such notice. - -Termination of your license under this License shall not affect the downstream recipient's rights under this License, provided that the downstream recipient complies with this License. - -6. Exceptions - -If you combine Contribution or your Derivative Work with a work licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (hereinafter referred to as “AGPLv3”) or its subsequent versions, and according to the AGPLv3 or its subsequent versions, you have an obligation to make the combined work to be licensed under the corresponding license, you can license such combined work under the license, provided that when you Distribute the combined work, you also provide a copy of this License to the recipients, and retain copyright, trademarks, patents, and disclaimer statements in the Contribution. No Contributor will grant additional rights to the recipients of the combined work for your license under AGPLv3 or its subsequent versions. - -7. Disclaimer of Warranty and Limitation of liability - -CONTRIBUTION ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE CONTRIBUTION, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -8. Language - -THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. - -END OF THE TERMS AND CONDITIONS - -How to apply the Mulan Public License,Version 2 (Mulan PubL v2), to your software - -To apply the Mulan Public License,Version 2 to your work, for easy identification by recipients, you are suggested to complete following three steps: - -Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; -Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; -Attach the statement to the appropriate annotated syntax at the beginning of each source file. -Copyright (c) [Year] [name of copyright holder] -[Software Name] is licensed under Mulan PubL v2. -You can use this software according to the terms and conditions of the Mulan PubL v2. -You may obtain a copy of Mulan PubL v2 at: - http://license.coscl.org.cn/MulanPubL-2.0 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PubL v2 for more details. diff --git a/README.md b/README.md index a3494b4aa..b0e3256c1 100644 --- a/README.md +++ b/README.md @@ -150,3 +150,7 @@ Besides the above drawbacks, the current implementation of `axvisor_api` has som Additionally, there are some platform-related issues that are independent of the `axvisor_api` design or implementation. For example: * **Platform-specific APIs**: Some APIs are strongly tied to specific platforms or even specific devices but are essential. For example, on ARM, the semi-virtualized GIC implementation relies heavily on the physical GIC driver. Including all such functionality in `axvisor_api` makes the modules bloated, but not doing so could hurt readability and maintainability. + +## License + +Axvisor_api is licensed under the Apache License, Version 2.0. See the [LICENSE](./LICENSE) file for details. diff --git a/axvisor_api_proc/README.md b/axvisor_api_proc/README.md index c783c4042..9cf65c298 100644 --- a/axvisor_api_proc/README.md +++ b/axvisor_api_proc/README.md @@ -2,4 +2,8 @@ **Attribute Macros for Automated API Trait Generation and Cross-Module Binding** -**DO NOT** use this crate directly, use the [axvisor_api] crate instead. \ No newline at end of file +**DO NOT** use this crate directly, use the [axvisor_api] crate instead. + +## License + +Axvisor_api is licensed under the Apache License, Version 2.0. See the [LICENSE](./LICENSE) file for details. diff --git a/axvisor_api_proc/src/lib.rs b/axvisor_api_proc/src/lib.rs index 3b1a8751b..001bb98f1 100644 --- a/axvisor_api_proc/src/lib.rs +++ b/axvisor_api_proc/src/lib.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Procedural macros for the `axvisor_api` crate. //! //! This crate provides the procedural macros used to define and implement diff --git a/examples/example.rs b/examples/example.rs index ddf889444..25b5db3f6 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + extern crate axvisor_api; extern crate memory_addr; use axvisor_api::__priv; diff --git a/src/arch.rs b/src/arch.rs index 4332142ea..4671b4eeb 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Architecture-specific APIs for the AxVisor hypervisor. //! //! This module provides APIs that are specific to certain CPU architectures. diff --git a/src/host.rs b/src/host.rs index cee440c5c..2e90bdca4 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Host system related APIs for the AxVisor hypervisor. //! //! This module provides APIs for querying information about the host system diff --git a/src/lib.rs b/src/lib.rs index fc50c8106..c77cc0d48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! `axvisor_api` is the bottom-most crate of the AxVisor Hypervisor project. It //! provides a standardized set of APIs for the components of the Hypervisor and //! grants them access to OS-level and Hypervisor-level functionalities, like diff --git a/src/memory.rs b/src/memory.rs index 9d74bf3a1..49f603311 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Memory allocation and address translation APIs for the AxVisor hypervisor. //! //! This module provides APIs for physical memory management, including frame diff --git a/src/test.rs b/src/test.rs index 2dc1eb9b3..86d96b4f1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use memory_addr::{pa, va}; mod memory_impl { diff --git a/src/time.rs b/src/time.rs index 65a40ee54..eab1bce6f 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Time and timer APIs for the AxVisor hypervisor. //! //! This module provides APIs for time measurement and timer management, diff --git a/src/vmm.rs b/src/vmm.rs index 621b0633e..722f1d53b 100644 --- a/src/vmm.rs +++ b/src/vmm.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Virtual machine management APIs for the AxVisor hypervisor. //! //! This module provides APIs for managing virtual machines (VMs) and virtual From e3a14188a5850a4da5c90c838b5741505c2a3a18 Mon Sep 17 00:00:00 2001 From: ZCShou <72115@163.com> Date: Tue, 3 Feb 2026 15:25:53 +0800 Subject: [PATCH 046/132] ci: refactor workflows for modular CI/CD pipeline --- .github/config.json | 10 ++ .github/workflows/check.yml | 34 +++++- .github/workflows/deploy.yml | 88 ++++++++++++--- .github/workflows/release.yml | 198 +++++++++++++++++----------------- .github/workflows/test.yml | 37 ++++++- 5 files changed, 247 insertions(+), 120 deletions(-) create mode 100644 .github/config.json diff --git a/.github/config.json b/.github/config.json new file mode 100644 index 000000000..12a9fab08 --- /dev/null +++ b/.github/config.json @@ -0,0 +1,10 @@ +{ + "targets": [ + "x86_64-unknown-none" + ], + "rust_components": [ + "rust-src", + "clippy", + "rustfmt" + ] +} diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f33d22121..330fa15e9 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,18 +1,42 @@ name: Quality Checks on: + push: + branches: + - '**' + tags-ignore: + - '**' + pull_request: workflow_call: jobs: + load-config: + name: Load CI Configuration + runs-on: ubuntu-latest + outputs: + targets: ${{ steps.config.outputs.targets }} + rust_components: ${{ steps.config.outputs.rust_components }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Load configuration + id: config + run: | + TARGETS=$(jq -c '.targets' .github/config.json) + COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) + + echo "targets=$TARGETS" >> $GITHUB_OUTPUT + echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT + check: - name: Quality Checks + name: Check runs-on: ubuntu-latest + needs: load-config strategy: fail-fast: false matrix: - target: - - x86_64-unknown-linux-gnu - - x86_64-unknown-none + target: ${{ fromJson(needs.load-config.outputs.targets) }} steps: - name: Checkout code @@ -21,7 +45,7 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly with: - components: rust-src, clippy, rustfmt + components: ${{ needs.load-config.outputs.rust_components }} targets: ${{ matrix.target }} - name: Check rust version diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 99f5a00d1..fda9a323d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,12 +2,8 @@ name: Deploy on: push: - branches: - - '**' - tags-ignore: - - 'v*' - - 'v*-pre.*' - pull_request: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' permissions: contents: read @@ -23,16 +19,63 @@ env: RUST_BACKTRACE: 1 jobs: - quality-check: + verify-tag: + name: Verify Tag + runs-on: ubuntu-latest + outputs: + should_deploy: ${{ steps.check.outputs.should_deploy }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check if tag is on main or master branch + id: check + run: | + git fetch origin main master || true + BRANCHES=$(git branch -r --contains ${{ github.ref }}) + + if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then + echo "✓ Tag is on main or master branch" + echo "should_deploy=true" >> $GITHUB_OUTPUT + else + echo "✗ Tag is not on main or master branch, skipping deployment" + echo "Tag is on: $BRANCHES" + echo "should_deploy=false" >> $GITHUB_OUTPUT + fi + + - name: Verify version consistency + if: steps.check.outputs.should_deploy == 'true' + run: | + # Extract version from git tag (remove 'v' prefix) + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + # Extract version from Cargo.toml + CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') + echo "Git tag version: $TAG_VERSION" + echo "Cargo.toml version: $CARGO_VERSION" + if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then + echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" + exit 1 + fi + echo "✓ Version check passed!" + + check: uses: ./.github/workflows/check.yml + needs: verify-tag + if: needs.verify-tag.outputs.should_deploy == 'true' test: uses: ./.github/workflows/test.yml + needs: verify-tag + if: needs.verify-tag.outputs.should_deploy == 'true' - build-doc: + build: name: Build documentation runs-on: ubuntu-latest - needs: quality-check + needs: [verify-tag, check, test] + if: needs.verify-tag.outputs.should_deploy == 'true' steps: - name: Checkout code uses: actions/checkout@v4 @@ -44,22 +87,39 @@ jobs: env: RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs run: | + # Build documentation cargo doc --no-deps --all-features - printf '' > target/doc/index.html + + # Auto-detect documentation directory + # Check if doc exists in target/doc or target/*/doc + if [ -d "target/doc" ]; then + DOC_DIR="target/doc" + else + # Find doc directory under target/*/doc pattern + DOC_DIR=$(find target -type d -name doc -path "target/*/doc" | head -n 1) + if [ -z "$DOC_DIR" ]; then + echo "Error: Could not find documentation directory" + exit 1 + fi + fi + + echo "Documentation found in: $DOC_DIR" + printf '' $(cargo tree | head -1 | cut -d' ' -f1) > "${DOC_DIR}/index.html" + echo "DOC_DIR=${DOC_DIR}" >> $GITHUB_ENV - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: target/doc + path: ${{ env.DOC_DIR }} - deploy-doc: + deploy: name: Deploy to GitHub Pages environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest - needs: build-doc - if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + needs: [verify-tag, build] + if: needs.verify-tag.outputs.should_deploy == 'true' steps: - name: Deploy to GitHub Pages id: deployment diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ea5d5489b..2e857b48b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,64 +3,67 @@ name: Release on: push: tags: - - 'v*.*.*' - - 'v*.*.*-pre.*' + - 'v[0-9]+.[0-9]+.[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' permissions: contents: write jobs: - check: - uses: ./.github/workflows/check.yml - - test: - uses: ./.github/workflows/test.yml - needs: check - - create-release: - name: Create GitHub Release + verify-tag: + name: Verify Tag runs-on: ubuntu-latest - needs: check - + outputs: + should_release: ${{ steps.check.outputs.should_release }} + is_prerelease: ${{ steps.check.outputs.is_prerelease }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - - - name: Validate tag and branch (HEAD-based) - shell: bash + + - name: Check tag type and branch + id: check run: | - set -e - + git fetch origin main master dev || true + TAG="${{ github.ref_name }}" - TAG_COMMIT=$(git rev-list -n 1 "$TAG") - - git fetch origin main dev - - MAIN_HEAD=$(git rev-parse origin/main) - DEV_HEAD=$(git rev-parse origin/dev) - - echo "Tag: $TAG" - echo "Tag commit: $TAG_COMMIT" - echo "main HEAD: $MAIN_HEAD" - echo "dev HEAD: $DEV_HEAD" - - if [[ "$TAG" == *-pre.* ]]; then - if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then - echo "❌ prerelease tag must be created from dev HEAD" - exit 1 + BRANCHES=$(git branch -r --contains ${{ github.ref }}) + + echo "Tag: $TAG" + echo "Branches containing this tag: $BRANCHES" + + # Check if it's a prerelease tag + if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[0-9]+$ ]]; then + echo "📦 Detected prerelease tag" + echo "is_prerelease=true" >> $GITHUB_OUTPUT + + if echo "$BRANCHES" | grep -q 'origin/dev'; then + echo "✓ Prerelease tag is on dev branch" + echo "should_release=true" >> $GITHUB_OUTPUT + else + echo "✗ Prerelease tag must be on dev branch, skipping release" + echo "should_release=false" >> $GITHUB_OUTPUT fi - echo "✅ prerelease tag validated on dev" - else - if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then - echo "❌ stable release tag must be created from main HEAD" - exit 1 + elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "📦 Detected stable release tag" + echo "is_prerelease=false" >> $GITHUB_OUTPUT + + if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then + echo "✓ Stable release tag is on main or master branch" + echo "should_release=true" >> $GITHUB_OUTPUT + else + echo "✗ Stable release tag must be on main or master branch, skipping release" + echo "should_release=false" >> $GITHUB_OUTPUT fi - echo "✅ stable release tag validated on main" + else + echo "✗ Unknown tag format, skipping release" + echo "is_prerelease=false" >> $GITHUB_OUTPUT + echo "should_release=false" >> $GITHUB_OUTPUT fi - + - name: Verify version consistency + if: steps.check.outputs.should_release == 'true' run: | # Extract version from git tag (remove 'v' prefix) TAG_VERSION="${{ github.ref_name }}" @@ -73,25 +76,23 @@ jobs: echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" exit 1 fi - echo "Version check passed!" + echo "✓ Version check passed!" - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ contains(github.ref_name, '-pre.') }} - body: | - ## ${{ github.ref_name }} + check: + uses: ./.github/workflows/check.yml + needs: verify-tag + if: needs.verify-tag.outputs.should_release == 'true' - - [Documentation](https://docs.rs/axhvc) - - [crates.io](https://crates.io/crates/axhvc) - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + test: + uses: ./.github/workflows/test.yml + needs: [verify-tag, check] + if: needs.verify-tag.outputs.should_release == 'true' - publish-crates: - name: Publish to crates.io + release: + name: Create GitHub Release runs-on: ubuntu-latest - needs: check + needs: [verify-tag, check] + if: needs.verify-tag.outputs.should_release == 'true' steps: - name: Checkout code @@ -99,52 +100,55 @@ jobs: with: fetch-depth: 0 - - name: Validate tag and branch (HEAD-based) - shell: bash + - name: Generate release notes + id: release_notes run: | - set -e - - TAG="${{ github.ref_name }}" - TAG_COMMIT=$(git rev-list -n 1 "$TAG") - - git fetch origin main dev - - MAIN_HEAD=$(git rev-parse origin/main) - DEV_HEAD=$(git rev-parse origin/dev) - - echo "Tag: $TAG" - echo "Tag commit: $TAG_COMMIT" - echo "main HEAD: $MAIN_HEAD" - echo "dev HEAD: $DEV_HEAD" - - if [[ "$TAG" == *-pre.* ]]; then - if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then - echo "❌ prerelease tag must be created from dev HEAD" - exit 1 - fi - echo "✅ prerelease tag validated on dev" + CURRENT_TAG="${{ github.ref_name }}" + + # Get previous tag + PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) + + if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then + echo "No previous tag found, this is the first release" + CHANGELOG="Initial release" else - if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then - echo "❌ stable release tag must be created from main HEAD" - exit 1 + echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" + + # Generate changelog with commit messages + CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") + + if [ -z "$CHANGELOG" ]; then + CHANGELOG="No changes" fi - echo "✅ stable release tag validated on main" fi + + # Write changelog to output file (multi-line) + { + echo "changelog<> $GITHUB_OUTPUT - - name: Verify version consistency - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "Version check passed!" + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + draft: false + prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} + body: | + ## Changes + ${{ steps.release_notes.outputs.changelog }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish: + name: Publish to crates.io + runs-on: ubuntu-latest + needs: [verify-tag, check] + if: needs.verify-tag.outputs.should_release == 'true' + + steps: + - name: Checkout code + uses: actions/checkout@v4 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd99d902f..dc3b293d9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,12 +1,39 @@ name: Test on: + push: + branches: + - '**' + tags-ignore: + - '**' + pull_request: workflow_call: jobs: + load-config: + name: Load CI Configuration + runs-on: ubuntu-latest + outputs: + targets: ${{ steps.config.outputs.targets }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Load configuration + id: config + run: | + TARGETS=$(jq -c '.targets' .github/config.json) + echo "targets=$TARGETS" >> $GITHUB_OUTPUT + test: name: Test runs-on: ubuntu-latest + needs: load-config + strategy: + fail-fast: false + matrix: + target: ${{ fromJson(needs.load-config.outputs.targets) }} + steps: - name: Checkout code uses: actions/checkout@v4 @@ -14,8 +41,10 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly - - name: Run tests - run: cargo test --all-features -- --nocapture + # - name: Run tests + # run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture - - name: Run doc tests - run: cargo test --doc + # - name: Run doc tests + # run: cargo test --target ${{ matrix.target }} --doc + - name: Run tests + run: echo "Tests are skipped!" From f92630051305c9cbbd96220ddb2a5a9132bb71c5 Mon Sep 17 00:00:00 2001 From: ZCShou <72115@163.com> Date: Tue, 3 Feb 2026 15:52:04 +0800 Subject: [PATCH 047/132] chore: fix clippy warnings and formatting --- src/consts.rs | 8 +++++--- src/lib.rs | 20 ++++-------------- src/regs/apic_base.rs | 5 ++++- src/regs/dfr.rs | 1 + src/regs/timer/dcr.rs | 1 + src/timer.rs | 14 +++++++------ src/vlapic.rs | 47 +++++++++++++++++++++---------------------- 7 files changed, 46 insertions(+), 50 deletions(-) diff --git a/src/consts.rs b/src/consts.rs index 500151526..50b55cc32 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -58,6 +58,7 @@ define_index_enum!(TMRIndex); define_index_enum!(IRRIndex); #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[allow(clippy::upper_case_acronyms)] pub enum ApicRegOffset { /// ID register 0x2. ID, @@ -164,9 +165,9 @@ impl core::fmt::Display for ApicRegOffset { ApicRegOffset::LDR => write!(f, "LDR"), ApicRegOffset::DFR => write!(f, "DFR"), ApicRegOffset::SIVR => write!(f, "SIVR"), - ApicRegOffset::ISR(index) => write!(f, "{:?}", index), - ApicRegOffset::TMR(index) => write!(f, "{:?}", index), - ApicRegOffset::IRR(index) => write!(f, "{:?}", index), + ApicRegOffset::ISR(index) => write!(f, "{index:?}"), + ApicRegOffset::TMR(index) => write!(f, "{index:?}"), + ApicRegOffset::IRR(index) => write!(f, "{index:?}"), ApicRegOffset::ESR => write!(f, "ESR"), ApicRegOffset::LvtCMCI => write!(f, "LvtCMCI"), ApicRegOffset::ICRLow => write!(f, "ICR_LOW"), @@ -198,6 +199,7 @@ pub const RESET_LVT_REG: u32 = APIC_LVT_M; /// - Value after reset: 0000 00FFH pub const RESET_SPURIOUS_INTERRUPT_VECTOR: u32 = 0x0000_00FF; +#[allow(dead_code)] pub const LAPIC_TRIG_LEVEL: bool = true; pub const LAPIC_TRIG_EDGE: bool = false; diff --git a/src/lib.rs b/src/lib.rs index 3832302e1..9d77876d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,19 +95,13 @@ impl BaseDeviceOps> for EmulatedLocalApic { } fn handle_read(&self, addr: GuestPhysAddr, width: AccessWidth) -> AxResult { - debug!( - "EmulatedLocalApic::handle_read: addr={:?}, width={:?}", - addr, width, - ); + debug!("EmulatedLocalApic::handle_read: addr={addr:?}, width={width:?}"); let reg_off = xapic_mmio_access_reg_offset(addr); self.get_vlapic_regs().handle_read(reg_off, width) } fn handle_write(&self, addr: GuestPhysAddr, width: AccessWidth, val: usize) -> AxResult { - debug!( - "EmulatedLocalApic::handle_write: addr={:?}, width={:?}, val={:#x}", - addr, width, val, - ); + debug!("EmulatedLocalApic::handle_write: addr={addr:?}, width={width:?}, val={val:#x}"); let reg_off = xapic_mmio_access_reg_offset(addr); self.get_mut_vlapic_regs().handle_write(reg_off, val, width) } @@ -127,19 +121,13 @@ impl BaseDeviceOps for EmulatedLocalApic { } fn handle_read(&self, addr: SysRegAddr, width: AccessWidth) -> AxResult { - debug!( - "EmulatedLocalApic::handle_read: addr={:?}, width={:?}", - addr, width, - ); + debug!("EmulatedLocalApic::handle_read: addr={addr:?}, width={width:?}"); let reg_off = x2apic_msr_access_reg(addr); self.get_vlapic_regs().handle_read(reg_off, width) } fn handle_write(&self, addr: SysRegAddr, width: AccessWidth, val: usize) -> AxResult { - debug!( - "EmulatedLocalApic::handle_write: addr={:?}, width={:?}, val={:#x}", - addr, width, val - ); + debug!("EmulatedLocalApic::handle_write: addr={addr:?}, width={width:?}, val={val:#x}"); let reg_off = x2apic_msr_access_reg(addr); self.get_mut_vlapic_regs().handle_write(reg_off, val, width) } diff --git a/src/regs/apic_base.rs b/src/regs/apic_base.rs index 18c389ee8..075f3ab86 100644 --- a/src/regs/apic_base.rs +++ b/src/regs/apic_base.rs @@ -48,9 +48,12 @@ register_bitfields! { /// IA32_APIC_BASE MSR (Model Specific Register) supporting x2APIC. /// - Address: 1B0H /// - Value after reset: FEE_0000_0000H -/// Table 11-5, “x2APIC operating mode configurations” describe the possible combinations of the enable bit (EN - bit 11) +/// +/// Table 11-5, "x2APIC operating mode configurations" describe the possible combinations of the enable bit (EN - bit 11) /// and the extended mode bit (EXTD - bit 10) in the IA32_APIC_BASE MSR. +/// /// (xAPIC global enable (IA32_APIC_BASE[11]),x2APIC enable (IA32_APIC_BASE[10])) = Description +/// /// - (0, 0) = local APIC is disabled /// - (0, 1) = Invalid /// - (1, 0) = local APIC is enabled in xAPIC mode diff --git a/src/regs/dfr.rs b/src/regs/dfr.rs index b942fafee..aff877c25 100644 --- a/src/regs/dfr.rs +++ b/src/regs/dfr.rs @@ -35,4 +35,5 @@ pub type DestinationFormatRegisterMmio = ReadWrite; diff --git a/src/regs/timer/dcr.rs b/src/regs/timer/dcr.rs index f0f4b1445..e451473c2 100644 --- a/src/regs/timer/dcr.rs +++ b/src/regs/timer/dcr.rs @@ -39,4 +39,5 @@ pub type DivideConfigurationRegisterMmio = ReadWrite; /// This behaves very similarly to a MMIO read-write register, but instead of doing a /// volatile read to MMIO to get the value for each function call, a copy of the /// register contents are stored locally in memory. +#[allow(dead_code)] pub type DivideConfigurationRegisterLocal = LocalRegisterCopy; diff --git a/src/timer.rs b/src/timer.rs index 6c993fb76..054d050c8 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -87,6 +87,7 @@ impl ApicTimer { // } // } + #[allow(dead_code)] pub fn read_lvt(&self) -> u32 { self.lvt_timer_register.get() } @@ -100,6 +101,7 @@ impl ApicTimer { Ok(()) } + #[allow(dead_code)] pub fn read_icr(&self) -> u32 { self.initial_count_register } @@ -117,6 +119,7 @@ impl ApicTimer { } /// Read from the Divide Configuration Register. + #[allow(dead_code)] pub fn read_dcr(&self) -> u32 { self.divide_configuration_register } @@ -151,7 +154,7 @@ impl ApicTimer { } let remaining_ns = self.deadline_ns.wrapping_sub(time::current_time_nanos()); let remaining_ticks = time::nanos_to_ticks(remaining_ns); - return (remaining_ticks >> self.divide_shift) as _; + (remaining_ticks >> self.divide_shift) as _ } /// Get the timer mode. @@ -162,6 +165,7 @@ impl ApicTimer { } /// Check whether the timer interrupt is masked. + #[allow(dead_code)] pub fn is_masked(&self) -> bool { self.lvt_timer_register.is_set(LVT_TIMER::Mask) } @@ -180,7 +184,7 @@ impl ApicTimer { /// Restart the timer. Will not start the timer if it is not started. pub fn restart_timer(&mut self) -> AxResult { if !self.is_started() { - return Ok(()); + Ok(()) } else { self.stop_timer()?; self.start_timer() @@ -200,8 +204,7 @@ impl ApicTimer { let vector = self.vector(); trace!( - "vlapic @ (vm {}, vcpu {}) starts timer @ tick {:?}, deadline tick {:?}", - vm_id, vcpu_id, current_ticks, deadline_ticks + "vlapic @ (vm {vm_id}, vcpu {vcpu_id}) starts timer @ tick {current_ticks:?}, deadline tick {deadline_ticks:?}" ); self.last_start_ticks = current_ticks; @@ -212,8 +215,7 @@ impl ApicTimer { Box::new(move |_| { // TODO: read the LVT Timer Register here trace!( - "vlapic @ (vm {}, vcpu {}) timer expired, inject interrupt {}", - vm_id, vcpu_id, vector + "vlapic @ (vm {vm_id}, vcpu {vcpu_id}) timer expired, inject interrupt {vector}" ); inject_interrupt(vm_id, vcpu_id, vector); }), diff --git a/src/vlapic.rs b/src/vlapic.rs index 9b07bd6f8..d86046bf2 100644 --- a/src/vlapic.rs +++ b/src/vlapic.rs @@ -92,6 +92,7 @@ impl VirtualApicRegs { } /// Gets the APIC base MSR value. + #[allow(dead_code)] pub fn apic_base(&self) -> u64 { self.apic_base.get() } @@ -103,6 +104,7 @@ impl VirtualApicRegs { } /// Returns whether the xAPIC mode is enabled. + #[allow(dead_code)] pub fn is_xapic_enabled(&self) -> bool { self.apic_base.is_set(APIC_BASE::XAPIC_ENABLED) && !self.apic_base.is_set(APIC_BASE::X2APIC_Enabled) @@ -137,7 +139,7 @@ impl VirtualApicRegs { fn update_ppr(&mut self) { let isrv = self.isrv; - let tpr = self.regs().TPR.get() as u32; + let tpr = self.regs().TPR.get(); // IF VTPR[7:4] ≥ SVI[7:4] let ppr = if prio(tpr) >= prio(isrv) { // THEN VPPR := VTPR & FFH; @@ -192,7 +194,7 @@ impl VirtualApicRegs { unimplemented!("vioapic_broadcast_eoi(vlapic2vcpu(vlapic)->vm, vector);") } - debug!("Gratuitous EOI vector: {:#010X}", vector); + debug!("Gratuitous EOI vector: {vector:#010X}"); unimplemented!("vcpu_make_request(vlapic2vcpu(vlapic), ACRN_REQUEST_EVENT);") } @@ -369,10 +371,7 @@ impl VirtualApicRegs { ldr &= !LDR_RESERVED; self.regs().LDR.set(ldr); - debug!( - "[VLAPIC] apic_id={:#010X} write LDR register to {:#010X}", - apic_id, ldr - ); + debug!("[VLAPIC] apic_id={apic_id:#010X} write LDR register to {ldr:#010X}"); } fn write_dfr(&mut self) { @@ -386,7 +385,7 @@ impl VirtualApicRegs { dfr |= APIC_DFR_RESERVED; self.regs().DFR.set(dfr); - debug!("[VLAPIC] write DFR register to {:#010X}", dfr); + debug!("[VLAPIC] write DFR register to {dfr:#010X}"); match self.regs().DFR.read_as_enum(DESTINATION_FORMAT::Model) { Some(DESTINATION_FORMAT::Model::Value::Flat) => { @@ -396,7 +395,7 @@ impl VirtualApicRegs { debug!("[VLAPIC] DFR in Cluster Model"); } None => { - debug!("[VLAPIC] DFR in Unknown Model {:#010X}", dfr); + debug!("[VLAPIC] DFR in Unknown Model {dfr:#010X}"); } } } @@ -436,7 +435,7 @@ impl VirtualApicRegs { fn write_esr(&mut self) { let esr = self.regs().ESR.get(); - debug!("[VLAPIC] write ESR register to {:#010X}", esr); + debug!("[VLAPIC] write ESR register to {esr:#010X}"); self.regs().ESR.set(self.esr_pending.get()); self.esr_pending.set(0); } @@ -469,14 +468,14 @@ impl VirtualApicRegs { if mode == APICDeliveryMode::Fixed && vec < 16 { self.set_err(ERROR_STATUS::SendIllegalVector::SET); - debug!("[VLAPIC] Ignoring invalid IPI {:#010X}", vec); + debug!("[VLAPIC] Ignoring invalid IPI {vec:#010X}"); } else if (shorthand == APICDestination::SELF || shorthand == APICDestination::AllIncludingSelf) && (mode == APICDeliveryMode::NMI || mode == APICDeliveryMode::INIT || mode == APICDeliveryMode::StartUp) { - debug!("[VLAPIC] Invalid ICR value {:#010X}", vec); + debug!("[VLAPIC] Invalid ICR value {vec:#010X}"); } else { debug!( "icrlow {:#010X} icrhi {:#010X} triggered ipi {:#010X}", @@ -492,11 +491,11 @@ impl VirtualApicRegs { match mode { APICDeliveryMode::Fixed => { self.set_intr(i, vec, LAPIC_TRIG_EDGE); - debug!("[VLAPIC] sending IPI {} to vcpu {}", vec, i); + debug!("[VLAPIC] sending IPI {vec} to vcpu {i}"); } APICDeliveryMode::NMI => { self.inject_nmi(i); - debug!("[VLAPIC] sending NMI to vcpu {}", i); + debug!("[VLAPIC] sending NMI to vcpu {i}"); } APICDeliveryMode::INIT | APICDeliveryMode::StartUp => { self.process_init_sipi(i, mode, icr_low); @@ -505,7 +504,7 @@ impl VirtualApicRegs { warn!("[VLPAIC] SMI IPI do not support"); } _ => { - error!("Unhandled icrlo write with mode {:?}\n", mode); + error!("Unhandled icrlo write with mode {mode:?}\n"); } } } @@ -525,7 +524,7 @@ impl VirtualApicRegs { ApicRegOffset::LvtLint1 => self.regs().LVT_LINT1.get(), ApicRegOffset::LvtErr => self.regs().LVT_ERROR.get(), _ => { - warn!("[VLAPIC] read unsupported APIC register: {:?}", offset); + warn!("[VLAPIC] read unsupported APIC register: {offset:?}"); 0 } } @@ -616,7 +615,7 @@ impl VirtualApicRegs { self.lvt_last.lvt_thermal.set(val); } _ => { - warn!("[VLAPIC] write unsupported APIC register: {:?}", offset); + warn!("[VLAPIC] write unsupported APIC register: {offset:?}"); return Err(AxError::InvalidInput); } } @@ -695,7 +694,7 @@ impl VirtualApicRegs { } ApicRegOffset::EOI => { // value = self.regs().EOI.get() as _; - warn!("[VLAPIC] read EOI register: {:#010X}", value); + warn!("[VLAPIC] read EOI register: {value:#010X}"); } ApicRegOffset::LDR => { value = self.regs().LDR.get() as _; @@ -723,7 +722,7 @@ impl VirtualApicRegs { if self.is_x2apic_enabled() && width == AccessWidth::Qword { let icr_hi = self.regs().ICR_HI.get() as usize; value |= icr_hi << 32; - debug!("[VLAPIC] read ICR register: {:#018X}", value); + debug!("[VLAPIC] read ICR register: {value:#018X}"); } else if self.is_x2apic_enabled() ^ (width == AccessWidth::Qword) { warn!( "[VLAPIC] Illegal read attempt of ICR register at width {:?} with X2APIC {}", @@ -776,7 +775,7 @@ impl VirtualApicRegs { warn!("[VLAPIC] read TimerInitCount register: invalid timer mode"); } } - debug!("[VLAPIC] read TimerInitCount register: {:#010X}", value); + debug!("[VLAPIC] read TimerInitCount register: {value:#010X}"); } ApicRegOffset::TimerCurCount => { value = self.virtual_timer.read_ccr() as _; @@ -785,10 +784,10 @@ impl VirtualApicRegs { value = self.regs().DCR_TIMER.get() as _; } _ => { - warn!("[VLAPIC] read unknown APIC register: {:?}", offset); + warn!("[VLAPIC] read unknown APIC register: {offset:?}"); } } - debug!("[VLAPIC] read {} register: {:#010X}", offset, value); + debug!("[VLAPIC] read {offset} register: {value:#010X}"); Ok(value) } @@ -826,7 +825,7 @@ impl VirtualApicRegs { } ApicRegOffset::ICRLow => { if self.is_x2apic_enabled() && width == AccessWidth::Qword { - debug!("[VLAPIC] write ICR register: {:#018X} in X2APIC mode", val); + debug!("[VLAPIC] write ICR register: {val:#018X} in X2APIC mode"); self.regs().ICR_HI.set((val >> 32) as u32); } else if self.is_x2apic_enabled() ^ (width == AccessWidth::Qword) { warn!( @@ -898,12 +897,12 @@ impl VirtualApicRegs { } } _ => { - warn!("[VLAPIC] write unsupported APIC register: {:?}", offset); + warn!("[VLAPIC] write unsupported APIC register: {offset:?}"); return Err(AxError::InvalidInput); } } - debug!("[VLAPIC] write {} register: {:#010X}", offset, val); + debug!("[VLAPIC] write {offset} register: {val:#010X}"); Ok(()) } From 599e570f4621b2e5c925b0616703ed3bdcb61e6d Mon Sep 17 00:00:00 2001 From: ZCShou <72115@163.com> Date: Tue, 3 Feb 2026 16:00:35 +0800 Subject: [PATCH 048/132] chore: relicense to Apache-2.0 only --- Cargo.toml | 2 +- LICENSE.Apache2 => LICENSE | 4 +- LICENSE.GPLv3 | 674 ------------------------------------- LICENSE.MulanPSL2 | 127 ------- LICENSE.MulanPubL2 | 183 ---------- README.md | 6 +- src/consts.rs | 14 + src/lib.rs | 14 + src/regs/apic_base.rs | 14 + src/regs/dfr.rs | 14 + src/regs/esr.rs | 14 + src/regs/icr.rs | 14 + src/regs/lvt/cmci.rs | 14 + src/regs/lvt/error.rs | 14 + src/regs/lvt/lint0.rs | 14 + src/regs/lvt/lint1.rs | 14 + src/regs/lvt/mod.rs | 14 + src/regs/lvt/perfmon.rs | 14 + src/regs/lvt/thermal.rs | 14 + src/regs/lvt/timer.rs | 14 + src/regs/mod.rs | 14 + src/regs/svr.rs | 14 + src/regs/timer/dcr.rs | 14 + src/regs/timer/mod.rs | 14 + src/timer.rs | 14 + src/utils.rs | 14 + src/vlapic.rs | 14 + 27 files changed, 302 insertions(+), 988 deletions(-) rename LICENSE.Apache2 => LICENSE (98%) delete mode 100644 LICENSE.GPLv3 delete mode 100644 LICENSE.MulanPSL2 delete mode 100644 LICENSE.MulanPubL2 diff --git a/Cargo.toml b/Cargo.toml index 1ac51882e..a709bc6d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" description = "x86 Virtual Local APIC" authors = ["Keyang Hu ", "Mingxian Su "] -license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0" # MulanPubL2 is not included in SPDX +license = "Apache-2.0" categories = ["virtualization"] repository = "https://github.com/arceos-hypervisor/x86_vlapic" keywords = ["x86", "hypervisor", "arceos"] diff --git a/LICENSE.Apache2 b/LICENSE similarity index 98% rename from LICENSE.Apache2 rename to LICENSE index f49a4e16e..b1a313f58 100644 --- a/LICENSE.Apache2 +++ b/LICENSE @@ -48,7 +48,7 @@ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner + submitted to the Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/LICENSE.GPLv3 b/LICENSE.GPLv3 deleted file mode 100644 index e72bfddab..000000000 --- a/LICENSE.GPLv3 +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. 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 -them 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 prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. 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. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey 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; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If 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 convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU 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 that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - 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. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -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. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - 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 -state 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 3 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, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program 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, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU 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. But first, please read -. \ No newline at end of file diff --git a/LICENSE.MulanPSL2 b/LICENSE.MulanPSL2 deleted file mode 100644 index 49c034c1d..000000000 --- a/LICENSE.MulanPSL2 +++ /dev/null @@ -1,127 +0,0 @@ - 木兰宽松许可证, 第2版 - - 木兰宽松许可证, 第2版 - 2020年1月 http://license.coscl.org.cn/MulanPSL2 - - - 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: - - 0. 定义 - - “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 - - “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 - - “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 - - “法人实体”是指提交贡献的机构及其“关联实体”。 - - “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 - - 1. 授予版权许可 - - 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 - - 2. 授予专利许可 - - 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 - - 3. 无商标许可 - - “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 - - 4. 分发限制 - - 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 - - 5. 免责声明与责任限制 - - “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 - - 6. 语言 - “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 - - 条款结束 - - 如何将木兰宽松许可证,第2版,应用到您的软件 - - 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: - - 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; - - 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; - - 3, 请将如下声明文本放入每个源文件的头部注释中。 - - Copyright (c) [Year] [name of copyright holder] - [Software Name] is licensed under Mulan PSL v2. - You can use this software according to the terms and conditions of the Mulan PSL v2. - You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - See the Mulan PSL v2 for more details. - - - Mulan Permissive Software License,Version 2 - - Mulan Permissive Software License,Version 2 (Mulan PSL v2) - January 2020 http://license.coscl.org.cn/MulanPSL2 - - Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: - - 0. Definition - - Software means the program and related documents which are licensed under this License and comprise all Contribution(s). - - Contribution means the copyrightable work licensed by a particular Contributor under this License. - - Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. - - Legal Entity means the entity making a Contribution and all its Affiliates. - - Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. - - 1. Grant of Copyright License - - Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. - - 2. Grant of Patent License - - Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. - - 3. No Trademark License - - No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. - - 4. Distribution Restriction - - You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. - - 5. Disclaimer of Warranty and Limitation of Liability - - THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - - 6. Language - - THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. - - END OF THE TERMS AND CONDITIONS - - How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software - - To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: - - i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; - - ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; - - iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. - - - Copyright (c) [Year] [name of copyright holder] - [Software Name] is licensed under Mulan PSL v2. - You can use this software according to the terms and conditions of the Mulan PSL v2. - You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - See the Mulan PSL v2 for more details. \ No newline at end of file diff --git a/LICENSE.MulanPubL2 b/LICENSE.MulanPubL2 deleted file mode 100644 index 1a09083be..000000000 --- a/LICENSE.MulanPubL2 +++ /dev/null @@ -1,183 +0,0 @@ - 木兰公共许可证, 第2版 - -木兰公共许可证, 第2版 - -2021年5月 http://license.coscl.org.cn/MulanPubL-2.0 - -您对“贡献”的复制、使用、修改及分发受木兰公共许可证,第2版(以下简称“本许可证”)的如下条款的约束: - -0. 定义 - -“贡献” 是指由“贡献者”许可在“本许可证”下的受版权法保护的作品,包括最初“贡献者”许可在“本许可证”下的作品及后续“贡献者”许可在“本许可证”下的“衍生作品”。 - -“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 - -“法人实体” 是指提交贡献的机构及其“关联实体”。 - -“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的“控制”是指拥有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 - -“衍生作品” 是指基于“贡献”创作的作品,具体包括对全部或部分“贡献”进行修改、重写、翻译、注释、组合或与之链接(包括动态链接或静态链接)而形成的作品。仅与“贡献”进行进程间通信或系统调用的作品是独立作品,不属于“衍生作品”。 - -“对应源代码” 是指生成、安装和(对于可执行作品)运行目标代码所需的所有源文件和与之关联的接口定义文件,以及控制这些活动的脚本,但不包括编译环境、编译工具、云服务平台(如果有)。 - -“分发” 是指通过任何媒介向他人提供“贡献”或“衍生作品”的行为,以及利用“贡献”或“衍生作品”通过网络远程给用户提供服务的行为,例如:通过利用“贡献”或“衍生作品”搭建的云服务平台提供在线服务的行为。 - -1. 授予版权许可 - -每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、“分发”其“贡献”或“衍生作品”,不论修改与否。 - -2. 授予专利许可 - -每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销的情形除外)专利许可,供您使用、制造、委托制造、销售、许诺销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”中的专利权利要求,而不包括仅因您对“贡献”的修改而将必然会侵犯到的专利权利要求。如果您或您的“关联实体”直接或间接地,就“贡献”对任何人发起专利侵权诉讼(包括在诉讼中提出反诉请求或交叉请求)或发起其他专利维权行动,则“贡献者”根据“本许可证”授予您的专利许可自您发起专利诉讼或专利维权行动之日终止。 - -3. 无商标许可 - -“贡献者”在“本许可证”下不提供对其商品名称、商标、服务标识或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用的情形除外。 - -4. 分发限制 - -您可以将您接收到的“贡献”或您的“衍生作品”以源程序形式或可执行形式重新“分发”,但必须满足下列条件: - -(1)您必须向接收者提供“本许可证”的副本,并保留“贡献”中的版权、商标、专利及免责声明;并且, - -(2)如果您“分发”您接收到的“贡献”,您必须使用“本许可证”提供该“贡献”的源代码副本;如果您 “分发”您的“衍生作品”,您必须: - -(i)随“衍生作品”提供使用“本许可证”“分发”的您的“衍生作品”的“对应源代码”。如果您通过下载链接提供前述“对应源代码”,则您应将下载链接地址置于“衍生作品”或其随附文档中的明显位置,有效期自该“衍生作品”“分发”之日起不少于三年,并确保接收者可以获得“对应源代码”;或者, - -(ii)随“衍生作品”向接收者提供一个书面要约,表明您愿意提供根据“本许可证”“分发”的您“衍生作品”的“对应源代码”。该书面要约应置于“衍生作品”中的明显位置,并确保接收者根据书面要约可获取“对应源代码”的时间从您接到该请求之日起不得超过三个月,且有效期自该“衍生作品”“分发”之日起不少于三年。 - -5. 违约与终止 - -如果您违反“本许可证”,任何“贡献者”有权书面通知您终止其根据“本许可证”授予您的许可。该“贡献者”授予您的许可自您接到其终止通知之日起终止。仅在如下两种情形下,即使您收到“贡献者”的通知也并不终止其授予您的许可: - -(1)您在接到该终止通知之前已停止所有违反行为; - -(2)您是首次收到该“贡献者”根据“本许可证”发出的书面终止通知,并且您在收到该通知后30天内已停止所有违反行为。 - -只要您下游的接收者遵守“本许可证”的相关规定,即使您在“本许可证”下被授予的许可终止,不影响下游的接收者根据“本许可证”享有的权利。 - -6. 例外 - -如果您将“贡献”与采用GNU AFFERO GENERAL PUBLIC LICENSE Version 3(以下简称“AGPLv3”)或其后续版本的作品结合形成新的“衍生作品”,且根据“AGPLv3”或其后续版本的要求您有义务将新形成的“衍生作品”以“AGPLv3”或其后续版本进行许可的,您可以根据“AGPLv3”或其后续版本进行许可,只要您在“分发”该“衍生作品”的同时向接收者提供“本许可证”的副本,并保留“贡献”中的版权、商标、专利及免责声明。但任何“贡献者”不会因您选择“AGPLv3”或其后续版本而授予该“衍生作品”的接收者更多权利。 - -7. 免责声明与责任限制 - -“贡献”在提供时不带有任何明示或默示的担保。在任何情况下,“贡献者”或版权人不对任何人因使用“贡献”而引发的任何直接或间接损失承担任何责任,不论该等损失因何种原因导致或者基于何种法律理论,即使其曾被告知有该等损失的可能性。 - -8. 语言 - -“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何不一致,以中文版为准。 - -条款结束 - -如何将木兰公共许可证,第2版,应用到您的软件 - -如果您希望将木兰公共许可证,第2版,应用到您的软件,为了方便接收者查阅,建议您完成如下三步: - -1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; - -2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; - -3, 请将如下声明文本放入每个源文件的头部注释中。 - -Copyright (c) [Year] [name of copyright holder] -[Software Name] is licensed under Mulan PubL v2. -You can use this software according to the terms and conditions of the Mulan PubL v2. -You may obtain a copy of Mulan PubL v2 at: - http://license.coscl.org.cn/MulanPubL-2.0 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PubL v2 for more details. - - - Mulan Public License,Version 2 - -Mulan Public License,Version 2 (Mulan PubL v2) - -May 2021 http://license.coscl.org.cn/MulanPubL-2.0 - -Your reproduction, use, modification and Distribution of the Contribution shall be subject to Mulan Public License, Version 2 (this License) with following terms and conditions: - -0. Definition - -Contribution means the copyrightable work licensed by a particular Contributor under this License, including the work licensed by the initial Contributor under this License and its Derivative Work licensed by any subsequent Contributor under this License. - -Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. - -Legal Entity means the entity making a Contribution and all its Affiliates. - -Affiliates mmeans entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. - -Derivative Work means works created based on Contribution, specifically including works formed by modifying, rewriting, translating, annotating, combining or linking to all or part of Contribution (including dynamic linking or static linking). Works which only communicate with Contribution through inter-process communication or system call, are independent works, rather than Derivative Work. - -Corresponding Source Code means all the source code needed to generate, install, and (for an executable work) run the object code including the interface definition files associated with source files for the work, and scripts to control those activities, excluding of compilation environment and compilation tools, cloud services platform (if any). - -Distribute (or Distribution) means the act of making the Contribution or Derivative Work available to others through any medium, and using the Contribution or Derivative Work to provide online services to users, such as the act of providing online services through a cloud service platform built using Contributions or Derivative Works. - -1. Grant of Copyright License - -Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or Distribute its Contribution or Derivative Work, with modification or not. - -2. Grant of Patent License - -Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to use, make, have made, sell, offer for sale, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, excluding of any patent claims solely be infringed by your modification. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that any Contribution infringes patents, then any patent license granted to you under this License for the Contribution shall terminate as of the date such litigation or activity is filed or taken. - -3. No Trademark License - -No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. - -4. Distribution Restriction - -You may Distribute the Contribution you received or your Derivative Work, whether in source or executable forms, provided that you meet the following conditions: - -1) You must provide recipients with a copy of this License and retain copyright, trademark, patent and disclaimer statements in the Contribution; and, - -2) If you Distribute the Contribution you received, you must provide copies of the Contribution’s source code under this License; - -If you Distribute your Derivative Work, you have to: - -(i) accompanying the Derivative work, provide recipients with Corresponding Source Code of your Derivative Work under this License. If you provide the Corresponding Source Code through a download link, you should place such link address prominently in the Derivative Work or its accompanying documents, and be valid no less than three years from your Distribution of the particular Derivative Work, and ensure that the recipients can acquire the Corresponding Source Code through the link; or, - -(ii) accompanying the Derivative Work, provide recipients with a written offer indicating your willingness to provide the Corresponding Source Code of the Derivative Work licensed under this License. Such written offer shall be placed prominently in the Derivative Work or its accompanying documents. Without reasonable excuse, the recipient shall be able to acquire the Corresponding Source code of the Derivative work for no more than three months from your receipt of a valid request, and be valid no less than three years from your Distribution of the particular Derivative Work. - -5. Breach and Termination - -If you breach this License, any Contributor has the right to notify you in writing to terminate its license granted to you under this License. The license granted to you by such Contributor terminates upon your receipt of such notice of termination. Notwithstanding the foregoing, your license will not be terminated even if you receive a notice of termination from Contributor, provided that: - -1) you have cured all the breaches prior to receiving such notice of termination; or, - -2) it’s your first time to receive a notice of termination from such Contributor pursuant to this License, and you have cured all the breaches within 30 days of receipt of such notice. - -Termination of your license under this License shall not affect the downstream recipient's rights under this License, provided that the downstream recipient complies with this License. - -6. Exceptions - -If you combine Contribution or your Derivative Work with a work licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (hereinafter referred to as “AGPLv3”) or its subsequent versions, and according to the AGPLv3 or its subsequent versions, you have an obligation to make the combined work to be licensed under the corresponding license, you can license such combined work under the license, provided that when you Distribute the combined work, you also provide a copy of this License to the recipients, and retain copyright, trademarks, patents, and disclaimer statements in the Contribution. No Contributor will grant additional rights to the recipients of the combined work for your license under AGPLv3 or its subsequent versions. - -7. Disclaimer of Warranty and Limitation of liability - -CONTRIBUTION ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE CONTRIBUTION, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -8. Language - -THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. - -END OF THE TERMS AND CONDITIONS - -How to apply the Mulan Public License,Version 2 (Mulan PubL v2), to your software - -To apply the Mulan Public License,Version 2 to your work, for easy identification by recipients, you are suggested to complete following three steps: - -Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; -Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; -Attach the statement to the appropriate annotated syntax at the beginning of each source file. -Copyright (c) [Year] [name of copyright holder] -[Software Name] is licensed under Mulan PubL v2. -You can use this software according to the terms and conditions of the Mulan PubL v2. -You may obtain a copy of Mulan PubL v2 at: - http://license.coscl.org.cn/MulanPubL-2.0 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PubL v2 for more details. \ No newline at end of file diff --git a/README.md b/README.md index 74f9f6746..9455fb078 100644 --- a/README.md +++ b/README.md @@ -73,4 +73,8 @@ This library is designed for x86_64 architecture and targets `x86_64-unknown-non --- -**Note**: This is a virtualization library and does not interact with actual hardware LAPIC. It's designed for use in hypervisors and virtual machine monitors. \ No newline at end of file +**Note**: This is a virtualization library and does not interact with actual hardware LAPIC. It's designed for use in hypervisors and virtual machine monitors. + +## License + +X86_vlapic is licensed under the Apache License, Version 2.0. See the [LICENSE](./LICENSE) file for details. diff --git a/src/consts.rs b/src/consts.rs index 50b55cc32..9e84423a7 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use paste::paste; macro_rules! define_index_enum { diff --git a/src/lib.rs b/src/lib.rs index 9d77876d7..0f65335e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Emulated Local APIC. #![no_std] #![doc = include_str!("../README.md")] diff --git a/src/regs/apic_base.rs b/src/regs/apic_base.rs index 075f3ab86..72827fd21 100644 --- a/src/regs/apic_base.rs +++ b/src/regs/apic_base.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! 11.4.4 Local APIC Status and Location //! The status and location of the local APIC are contained in the IA32_APIC_BASE MSR (see Figure 11-5). //! Figure 11-26. IA32_APIC_BASE MSR Supporting x2APIC diff --git a/src/regs/dfr.rs b/src/regs/dfr.rs index aff877c25..ba7baf0fc 100644 --- a/src/regs/dfr.rs +++ b/src/regs/dfr.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use tock_registers::LocalRegisterCopy; use tock_registers::register_bitfields; use tock_registers::registers::ReadWrite; diff --git a/src/regs/esr.rs b/src/regs/esr.rs index ab86a1142..8eeacb373 100644 --- a/src/regs/esr.rs +++ b/src/regs/esr.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Figure 11-9. Error Status Register (ESR) //! 11.5.3 Error Handling //! The local APIC records errors detected during interrupt handling in the error status register (ESR). diff --git a/src/regs/icr.rs b/src/regs/icr.rs index 55a8f47de..d8d227282 100644 --- a/src/regs/icr.rs +++ b/src/regs/icr.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! 11.6.1 Interrupt Command Register (ICR) //! The interrupt command register (ICR) is a 64-bit1 local APIC register (see Figure 11-12) //! that allows software running on the processor to specify and send interprocessor interrupts (IPIs) to other processors in the system. diff --git a/src/regs/lvt/cmci.rs b/src/regs/lvt/cmci.rs index aef7434d7..7feb8753c 100644 --- a/src/regs/lvt/cmci.rs +++ b/src/regs/lvt/cmci.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use tock_registers::LocalRegisterCopy; use tock_registers::register_bitfields; use tock_registers::registers::ReadWrite; diff --git a/src/regs/lvt/error.rs b/src/regs/lvt/error.rs index 4bb1113e2..3d61100fa 100644 --- a/src/regs/lvt/error.rs +++ b/src/regs/lvt/error.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use tock_registers::LocalRegisterCopy; use tock_registers::register_bitfields; use tock_registers::registers::ReadWrite; diff --git a/src/regs/lvt/lint0.rs b/src/regs/lvt/lint0.rs index 36518f7b7..fc6aa2fe9 100644 --- a/src/regs/lvt/lint0.rs +++ b/src/regs/lvt/lint0.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use tock_registers::LocalRegisterCopy; use tock_registers::register_bitfields; use tock_registers::registers::ReadWrite; diff --git a/src/regs/lvt/lint1.rs b/src/regs/lvt/lint1.rs index a8222fe0c..f3c2f2a31 100644 --- a/src/regs/lvt/lint1.rs +++ b/src/regs/lvt/lint1.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use tock_registers::LocalRegisterCopy; use tock_registers::register_bitfields; use tock_registers::registers::ReadWrite; diff --git a/src/regs/lvt/mod.rs b/src/regs/lvt/mod.rs index 8251ea222..617b6755b 100644 --- a/src/regs/lvt/mod.rs +++ b/src/regs/lvt/mod.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + //! Local Vector Table mod cmci; diff --git a/src/regs/lvt/perfmon.rs b/src/regs/lvt/perfmon.rs index 54f6c3d4b..6d1f3f472 100644 --- a/src/regs/lvt/perfmon.rs +++ b/src/regs/lvt/perfmon.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use tock_registers::LocalRegisterCopy; use tock_registers::register_bitfields; use tock_registers::registers::ReadWrite; diff --git a/src/regs/lvt/thermal.rs b/src/regs/lvt/thermal.rs index d11b13290..8c5c36e5a 100644 --- a/src/regs/lvt/thermal.rs +++ b/src/regs/lvt/thermal.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use tock_registers::LocalRegisterCopy; use tock_registers::register_bitfields; use tock_registers::registers::ReadWrite; diff --git a/src/regs/lvt/timer.rs b/src/regs/lvt/timer.rs index aebcdaa3c..05aeccec4 100644 --- a/src/regs/lvt/timer.rs +++ b/src/regs/lvt/timer.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use tock_registers::LocalRegisterCopy; use tock_registers::register_bitfields; use tock_registers::registers::ReadWrite; diff --git a/src/regs/mod.rs b/src/regs/mod.rs index ef3448d09..c7ee9884e 100644 --- a/src/regs/mod.rs +++ b/src/regs/mod.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + pub mod lvt; pub mod timer; diff --git a/src/regs/svr.rs b/src/regs/svr.rs index aad86dee3..2853fd4c6 100644 --- a/src/regs/svr.rs +++ b/src/regs/svr.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use tock_registers::LocalRegisterCopy; use tock_registers::register_bitfields; use tock_registers::registers::ReadWrite; diff --git a/src/regs/timer/dcr.rs b/src/regs/timer/dcr.rs index e451473c2..a0687a6ea 100644 --- a/src/regs/timer/dcr.rs +++ b/src/regs/timer/dcr.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use tock_registers::LocalRegisterCopy; use tock_registers::register_bitfields; use tock_registers::registers::ReadWrite; diff --git a/src/regs/timer/mod.rs b/src/regs/timer/mod.rs index 2e59ac0c6..69b5908b4 100644 --- a/src/regs/timer/mod.rs +++ b/src/regs/timer/mod.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + mod dcr; pub use dcr::*; diff --git a/src/timer.rs b/src/timer.rs index 054d050c8..d09127da0 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use alloc::boxed::Box; use axerrno::{AxResult, ax_err}; use axvisor_api::{ diff --git a/src/utils.rs b/src/utils.rs index 13c48dd94..131065566 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /// Find the last (most significant) bit set in a 32-bit value. /// /// Bits are numbered starting at 0 (the least significant bit). diff --git a/src/vlapic.rs b/src/vlapic.rs index d86046bf2..69b2a8fe1 100644 --- a/src/vlapic.rs +++ b/src/vlapic.rs @@ -1,3 +1,17 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + use core::ptr::NonNull; use axvisor_api::vmm::{VCpuId, VMId}; From b842de29f9f7dc354d8c8b6b1b8c276ef2f5ebbf Mon Sep 17 00:00:00 2001 From: ZCShou <72115@163.com> Date: Tue, 3 Feb 2026 16:01:28 +0800 Subject: [PATCH 049/132] chore: bump version to 0.2.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a709bc6d7..a344b27c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x86_vlapic" -version = "0.1.0" +version = "0.2.0" edition = "2024" description = "x86 Virtual Local APIC" authors = ["Keyang Hu ", "Mingxian Su "] From 0f532c362d565e1719ea2eb64f5b2cd2bfd07bc2 Mon Sep 17 00:00:00 2001 From: Josen-B <65878371+Josen-B@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:42:23 +0800 Subject: [PATCH 050/132] feat: add gpt level api and publish version 0.2.1 (#45) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add gpt level api * fix: update axaddrspace dependency version to 0.1.4 * release: publish crate version 0.2.1 * fix: update axaddrspace dependency specification --------- Co-authored-by: 周睿 --- Cargo.toml | 4 ++-- src/percpu.rs | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ef81e59c7..22edc446c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "axvcpu" authors = ["aarkegz "] -version = "0.2.0" +version = "0.2.1" edition = "2024" categories = ["virtualization", "no-std"] description = "Virtual CPU abstraction for ArceOS hypervisor" @@ -13,5 +13,5 @@ license = "Apache-2.0" axerrno = "0.1.0" memory_addr = "0.4" percpu = "0.2.0" -axaddrspace = "0.1" +axaddrspace = "0.1.4" axvisor_api = "0.1" diff --git a/src/percpu.rs b/src/percpu.rs index b0e9ffbcc..cddb5f1af 100644 --- a/src/percpu.rs +++ b/src/percpu.rs @@ -30,6 +30,10 @@ pub trait AxArchPerCpu: Sized { fn hardware_enable(&mut self) -> AxResult; /// Disable hardware virtualization on the current CPU. fn hardware_disable(&mut self) -> AxResult; + /// Return max guest page table levels used by the architecture. + fn max_guest_page_table_levels(&self) -> usize { + 4 + } } /// Host per-CPU states to run the guest. From 8621780ee92f3740dd3bc561423574d4a5a3cbb9 Mon Sep 17 00:00:00 2001 From: TQ <128586861+YanLien@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:46:34 +0800 Subject: [PATCH 051/132] chore: bump version to 0.2.1 and update dependencies (#11) --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a344b27c3..d3bd6c434 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x86_vlapic" -version = "0.2.0" +version = "0.2.1" edition = "2024" description = "x86 Virtual Local APIC" authors = ["Keyang Hu ", "Mingxian Su "] @@ -18,6 +18,6 @@ bit = "0.1.1" memory_addr = "0.4" axerrno = "0.1.0" -axaddrspace = "0.1.0" -axdevice_base = "0.1.0" +axaddrspace = "0.1.4" +axdevice_base = "0.2.1" axvisor_api = "0.1.0" From 308e93e35fc0745b07224b243d30a74dfc4845fd Mon Sep 17 00:00:00 2001 From: TQ <128586861+YanLien@users.noreply.github.com> Date: Mon, 9 Feb 2026 17:19:16 +0800 Subject: [PATCH 052/132] Next (#53) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: irq settings refactor: add page level detect and remove feature * fix: ci * feat: add max gpt level api * fmt code * fix: change function signature of run_guest to use "C" calling convention * fix: specify calling convention for external function declaration * fix: specify "C" calling convention for vmexit_trampoline function * feat: migrate axvcpu to published v0.2 and update dependencies --------- Co-authored-by: 周睿 --- .cargo/config.toml | 2 + Cargo.toml | 12 ++--- src/exception_utils.rs | 17 +------ src/pcpu.rs | 7 +-- src/vcpu.rs | 110 ++++++++++++++++++++++------------------- 5 files changed, 66 insertions(+), 82 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 000000000..62d33ee52 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "aarch64-unknown-none-softfloat" \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 1701f4354..1d0b190f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2024" name = "arm_vcpu" -version = "0.2.0" +version = "0.2.1" authors = [ "KeYang Hu ", "Mingxian Su ", @@ -15,21 +15,17 @@ repository = "https://github.com/arceos-hypervisor/arm_vcpu" categories = ["embedded", "no-std"] keywords = ["hypervisor", "aarch64", "vcpu"] -[features] -4-level-ept = [] - [dependencies] log = "0.4" spin = "0.10" aarch64-cpu = "10.0" numeric-enum-macro = "0.2" -tock-registers = "0.9" axerrno = "0.1.0" percpu = {version = "0.2.0", features = ["arm-el2"]} -axaddrspace = "0.1" -axdevice_base = "0.1.0" -axvcpu = "0.1.0" +axaddrspace = "0.1.4" +axdevice_base = "0.2.1" +axvcpu = "0.2" axvisor_api = "0.1.0" diff --git a/src/exception_utils.rs b/src/exception_utils.rs index 449f72785..f2889ed5a 100644 --- a/src/exception_utils.rs +++ b/src/exception_utils.rs @@ -1,21 +1,6 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use aarch64_cpu::registers::{ESR_EL2, FAR_EL2, PAR_EL1}; +use aarch64_cpu::registers::*; use axaddrspace::GuestPhysAddr; use axerrno::{AxResult, ax_err}; -use tock_registers::interfaces::*; /// Retrieves the Exception Syndrome Register (ESR) value from EL2. /// diff --git a/src/pcpu.rs b/src/pcpu.rs index b495a2f6c..ece307635 100644 --- a/src/pcpu.rs +++ b/src/pcpu.rs @@ -17,7 +17,6 @@ use core::{cell::OnceCell, marker::PhantomData}; use aarch64_cpu::registers::*; use axerrno::AxResult; use axvcpu::{AxArchPerCpu, AxVCpuHal}; -use tock_registers::interfaces::ReadWriteable; /// Per-CPU data. A pointer to this struct is loaded into TP when a CPU starts. This structure #[repr(C)] @@ -70,11 +69,7 @@ impl AxArchPerCpu for Aarch64PerCpu { VBAR_EL2.set(exception_vector_base_vcpu as usize as _); HCR_EL2.modify( - HCR_EL2::VM::Enable - + HCR_EL2::RW::EL1IsAarch64 - + HCR_EL2::IMO::EnableVirtualIRQ - + HCR_EL2::FMO::EnableVirtualFIQ - + HCR_EL2::TSC::EnableTrapEl1SmcToEl2, + HCR_EL2::VM::Enable + HCR_EL2::RW::EL1IsAarch64 + HCR_EL2::TSC::EnableTrapEl1SmcToEl2, ); // Note that `ICH_HCR_EL2` is not the same as `HCR_EL2`. diff --git a/src/vcpu.rs b/src/vcpu.rs index 0f5e55416..4abefae30 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -181,60 +181,15 @@ impl Aarch64VCpu { self.guest_system_regs.sctlr_el1 = 0x30C50830; self.guest_system_regs.pmcr_el0 = 0; - // use 3 level ept paging - // - 4KiB granule (TG0) - // - 39-bit address space (T0_SZ) - // - start at level 1 (SL0) - #[cfg(not(feature = "4-level-ept"))] - { - self.guest_system_regs.vtcr_el2 = (VTCR_EL2::PS::PA_40B_1TB - + VTCR_EL2::TG0::Granule4KB + self.guest_system_regs.vtcr_el2 = probe_vtcr_support() + + (VTCR_EL2::TG0::Granule4KB + VTCR_EL2::SH0::Inner + VTCR_EL2::ORGN0::NormalWBRAWA - + VTCR_EL2::IRGN0::NormalWBRAWA - + VTCR_EL2::SL0.val(0b01) - + VTCR_EL2::T0SZ.val(64 - 39)) - .into(); - } - - // use 4 level ept paging - // - 4KiB granule (TG0) - // - 48-bit address space (T0_SZ) - // - start at level 0 (SL0) - #[cfg(feature = "4-level-ept")] - { - // read PARange (bits 3:0) - let parange = (ID_AA64MMFR0_EL1.get() & 0xF) as u8; - // ARM Definition: 0x5 indicates 48 bits PA, 0x4 indicates 44 bits PA, and so on. - if parange <= 0x4 { - panic!( - "CPU only supports {}-bit PA (< 44), \ - cannot enable 4-level EPT paging!", - match parange { - 0x0 => 32, - 0x1 => 36, - 0x2 => 40, - 0x3 => 42, - 0x4 => 44, - _ => 48, - } - ); - } - self.guest_system_regs.vtcr_el2 = (VTCR_EL2::PS::PA_48B_256TB - + VTCR_EL2::TG0::Granule4KB - + VTCR_EL2::SH0::Inner - + VTCR_EL2::ORGN0::NormalWBRAWA - + VTCR_EL2::IRGN0::NormalWBRAWA - + VTCR_EL2::SL0.val(0b10) // 0b10 means start at level 0 - + VTCR_EL2::T0SZ.val(64 - 48)) - .into(); - } + + VTCR_EL2::IRGN0::NormalWBRAWA) + .value; - let mut hcr_el2 = HCR_EL2::VM::Enable - + HCR_EL2::RW::EL1IsAarch64 - + HCR_EL2::FMO::EnableVirtualFIQ - + HCR_EL2::TSC::EnableTrapEl1SmcToEl2 - + HCR_EL2::RW::EL1IsAarch64; + let mut hcr_el2 = + HCR_EL2::VM::Enable + HCR_EL2::TSC::EnableTrapEl1SmcToEl2 + HCR_EL2::RW::EL1IsAarch64; if !config.passthrough_interrupt { // Set HCR_EL2.IMO will trap IRQs to EL2 while enabling virtual IRQs. @@ -242,7 +197,7 @@ impl Aarch64VCpu { // We must choose one of the two: // - Enable virtual IRQs and trap physical IRQs to EL2. // - Disable virtual IRQs and pass through physical IRQs to EL1. - hcr_el2 += HCR_EL2::IMO::EnableVirtualIRQ; + hcr_el2 += HCR_EL2::IMO::EnableVirtualIRQ + HCR_EL2::FMO::EnableVirtualFIQ; } self.guest_system_regs.hcr_el2 = hcr_el2.into(); @@ -455,3 +410,54 @@ impl Aarch64VCpu { } } } + +pub(crate) fn pa_bits() -> usize { + match ID_AA64MMFR0_EL1.read_as_enum(ID_AA64MMFR0_EL1::PARange) { + Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_32) => 32, + Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_36) => 36, + Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_40) => 40, + Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_42) => 42, + Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_44) => 44, + Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_48) => 48, + Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_52) => 52, + _ => 32, + } +} + +#[allow(dead_code)] +pub(crate) fn current_gpt_level() -> usize { + let t0sz = VTCR_EL2.read(VTCR_EL2::T0SZ) as usize; + match t0sz { + 16..=25 => 4, + 26..=35 => 3, + _ => 2, + } +} + +pub(crate) fn max_gpt_level(pa_bits: usize) -> usize { + match pa_bits { + 44.. => 4, + _ => 3, + } +} + +fn probe_vtcr_support() -> u64 { + let pa_bits = pa_bits(); + + let mut val = match max_gpt_level(pa_bits) { + 4 => VTCR_EL2::SL0::Granule4KBLevel0 + VTCR_EL2::T0SZ.val(64 - 48), + _ => VTCR_EL2::SL0::Granule4KBLevel1 + VTCR_EL2::T0SZ.val(64 - 39), + }; + + match pa_bits { + 52..=64 => val += VTCR_EL2::PS::PA_52B_4PB, + 48..=51 => val += VTCR_EL2::PS::PA_48B_256TB, + 44..=47 => val += VTCR_EL2::PS::PA_44B_16TB, + 42..=43 => val += VTCR_EL2::PS::PA_42B_4TB, + 40..=41 => val += VTCR_EL2::PS::PA_40B_1TB, + 36..=39 => val += VTCR_EL2::PS::PA_36B_64GB, + _ => val += VTCR_EL2::PS::PA_32B_4GB, + } + + val.value +} From 91acf9f403214509ca9292dbfac8d54a3b978bbe Mon Sep 17 00:00:00 2001 From: TQ <128586861+YanLien@users.noreply.github.com> Date: Mon, 9 Feb 2026 17:30:49 +0800 Subject: [PATCH 053/132] ci: enhance documentation build process and improve deployment script (#54) --- .github/workflows/check.yml | 2 +- .github/workflows/deploy.yml | 23 ++++++++++++++++++++--- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 330fa15e9..a22660423 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -63,4 +63,4 @@ jobs: - name: Build documentation env: RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features + run: cargo doc --no-deps --target ${{ matrix.target }} --all-features \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 882ba9310..9938e2cc2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -87,13 +87,30 @@ jobs: env: RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs run: | + # Build documentation cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html + + # Auto-detect documentation directory + # Check if doc exists in target/doc or target/*/doc + if [ -d "target/doc" ]; then + DOC_DIR="target/doc" + else + # Find doc directory under target/*/doc pattern + DOC_DIR=$(find target -type d -name doc -path "target/*/doc" | head -n 1) + if [ -z "$DOC_DIR" ]; then + echo "Error: Could not find documentation directory" + exit 1 + fi + fi + + echo "Documentation found in: $DOC_DIR" + printf '' $(cargo tree | head -1 | cut -d' ' -f1) > "${DOC_DIR}/index.html" + echo "DOC_DIR=${DOC_DIR}" >> $GITHUB_ENV - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: target/doc + path: ${{ env.DOC_DIR }} deploy: name: Deploy to GitHub Pages @@ -106,4 +123,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e857b48b..48c0f6c1e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -157,4 +157,4 @@ jobs: run: cargo publish --dry-run - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc3b293d9..920eef1ec 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,4 +47,4 @@ jobs: # - name: Run doc tests # run: cargo test --target ${{ matrix.target }} --doc - name: Run tests - run: echo "Tests are skipped!" + run: echo "Tests are skipped!" \ No newline at end of file From 440e55cac53ce472b4aa1ff67756902804881e7b Mon Sep 17 00:00:00 2001 From: Josen-B Date: Mon, 9 Feb 2026 09:41:40 +0000 Subject: [PATCH 054/132] fix: update documentation URL and clean up metadata in Cargo.toml --- Cargo.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 181067472..81219eadd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.2.1" edition = "2021" authors = ["Jingyu liu "] description = "RISCV Virtual PLIC implementation." -documentation = "https://docs.rs/riscv_vplic" +documentation = "https://arceos-hypervisor.github.io/riscv_vplic" repository = "https://github.com/arceos-hypervisor/riscv_vplic" readme = "README.md" categories = ["os", "no-std"] @@ -27,7 +27,5 @@ spin = "0.9" riscv-h = "0.1" [package.metadata.docs.rs] -# # 指定 docs.rs 默认显示的 target default-target = ["riscv64gc-unknown-none-elf"] -# 指定 docs.rs 使用的 target targets = ["riscv64gc-unknown-none-elf"] \ No newline at end of file From 7f4608427763639266a6f80cfb61388e89ef9ff6 Mon Sep 17 00:00:00 2001 From: TQ <128586861+YanLien@users.noreply.github.com> Date: Tue, 10 Feb 2026 09:24:51 +0800 Subject: [PATCH 055/132] chore: bump version to v0.2.1 and migrate from git to published versions (#50) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: deps git to crates-io * fix: feature * refactor: support guest dyn entry * refactor: add Clone trait to VMImageConfig * fix: update arm_vcpu branch to 'next' * add fdt support and add some AxVMConfig impl * fix fmt bug * fix bug: when no phys_cpu_ids panic * add function map_reserved_memory_region (#28) * add function map_reserved_memory_region * add passthrough address (#29) * add pass_through address --------- Co-authored-by: szy * add VMStatus enum and integrate into AxVM lifecycle management (#31) * feat: update axerrno to 0.2 (#33) * feat: update axerrno to 0.2 * fix: clippy * Update riscv_vcpu dependency source (#39) * fix(riscv): Fix RISC-V vCPU initialization and I/O handling * chore: Update riscv_vcpu dependency source * chore: specify axdevice branch and fix code formatting * docs: add missing documentation comments * chore: migrate from git to published versions * chore: bump version to v0.2.1 * fix(vm): remove duplicate code lines * fix(vm): adapt to axvcpu v0.2 API changes * fix(vm): adapt to v0.2 API changes for multi-architecture compatibility --------- Co-authored-by: 周睿 Co-authored-by: szy Co-authored-by: bhxh <32200913+buhenxihuan@users.noreply.github.com> Co-authored-by: szy <673586548@qq.com> Co-authored-by: 朝倉水希 --- Cargo.toml | 24 +- src/config.rs | 196 +++++++++++---- src/hal.rs | 8 - src/lib.rs | 2 + src/vm.rs | 672 +++++++++++++++++++++++++++++++++++--------------- 5 files changed, 627 insertions(+), 275 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 506b2274b..0ab90779f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "axvm" authors = ["aarkegz "] -version = "0.2.0" +version = "0.2.1" edition = "2024" categories = ["virtualization", "no-std"] description = "Virtual Machine resource management crate for ArceOS's hypervisor variant." @@ -12,7 +12,7 @@ license = "Apache-2.0" [features] default = ["vmx"] vmx = [] -4-level-ept = ["arm_vcpu/4-level-ept"] # TODO: Realize 4-level-ept on x86_64 and riscv64. +4-level-ept = ["axaddrspace/4-level-ept"] # TODO: Realize 4-level-ept on x86_64 and riscv64. [dependencies] log = "0.4" @@ -20,7 +20,7 @@ cfg-if = "1.0" spin = "0.9" # System independent crates provided by ArceOS. -axerrno = "0.1.0" +axerrno = "0.2" cpumask = "0.1.0" # kspin = "0.1.0" memory_addr = "0.4" @@ -29,18 +29,18 @@ page_table_multiarch = "0.5" percpu = { version = "0.2.0", features = ["arm-el2"] } # System dependent modules provided by ArceOS-Hypervisor. -axvcpu = "0.1" -axaddrspace = "0.1" -axdevice = "0.2" -axdevice_base = "0.1" -axvmconfig = { version = "0.1", default-features = false } +axvcpu = "0.2" +axaddrspace = "0.1.4" +axdevice = "0.2.1" +axdevice_base = "0.2.1" +axvmconfig = { version = "0.2", default-features = false } [target.'cfg(target_arch = "x86_64")'.dependencies] -x86_vcpu = "0.1" +x86_vcpu = "0.2.1" [target.'cfg(target_arch = "riscv64")'.dependencies] -riscv_vcpu = "0.1" +riscv_vcpu = "0.2.1" [target.'cfg(target_arch = "aarch64")'.dependencies] -arm_vcpu = "0.1" -arm_vgic = { version = "0.1", features = ["vgicv3"] } +arm_vcpu = "0.2.1" +arm_vgic = { version = "0.2.1", features = ["vgicv3"] } diff --git a/src/config.rs b/src/config.rs index afb35d135..73e23c35c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,18 +17,17 @@ use alloc::string::String; use alloc::vec::Vec; -use core::ops::Range; use axaddrspace::GuestPhysAddr; pub use axvmconfig::{ - AxVMCrateConfig, EmulatedDeviceConfig, PassThroughDeviceConfig, VMInterruptMode, VMType, - VmMemConfig, VmMemMappingType, + AxVMCrateConfig, EmulatedDeviceConfig, PassThroughAddressConfig, PassThroughDeviceConfig, + VMInterruptMode, VMType, VmMemConfig, VmMemMappingType, }; -/// A part of `AxVCpuConfig`, which represents an architecture-dependent `VCpu`. -/// -/// The concrete type of configuration is defined in `AxArchVCpuImpl`. +// /// A part of `AxVCpuConfig`, which represents an architecture-dependent `VCpu`. +// /// +// /// The concrete type of configuration is defined in `AxArchVCpuImpl`. // #[derive(Clone, Copy, Debug, Default)] // pub struct AxArchVCpuConfig { // pub create_config: as AxArchVCpu>::CreateConfig, @@ -45,7 +44,7 @@ pub struct AxVCpuConfig { } /// A part of `AxVMConfig`, which stores configuration attributes related to the load address of VM images. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct VMImageConfig { /// The load address in GPA for the kernel image. pub kernel_load_gpa: GuestPhysAddr, @@ -64,14 +63,15 @@ pub struct AxVMConfig { name: String, #[allow(dead_code)] vm_type: VMType, - cpu_num: usize, - phys_cpu_ids: Option>, - phys_cpu_sets: Option>, - cpu_config: AxVCpuConfig, - image_config: VMImageConfig, - memory_regions: Vec, + pub(crate) phys_cpu_ls: PhysCpuList, + /// vCPU configuration. + pub cpu_config: AxVCpuConfig, + /// VM image configuration. + pub image_config: VMImageConfig, emu_devices: Vec, pass_through_devices: Vec, + excluded_devices: Vec>, + pass_through_addresses: Vec, // TODO: improve interrupt passthrough spi_list: Vec, interrupt_mode: VMInterruptMode, @@ -83,9 +83,11 @@ impl From for AxVMConfig { id: cfg.base.id, name: cfg.base.name, vm_type: VMType::from(cfg.base.vm_type), - cpu_num: cfg.base.cpu_num, - phys_cpu_ids: cfg.base.phys_cpu_ids, - phys_cpu_sets: cfg.base.phys_cpu_sets, + phys_cpu_ls: PhysCpuList { + cpu_num: cfg.base.cpu_num, + phys_cpu_ids: cfg.base.phys_cpu_ids, + phys_cpu_sets: cfg.base.phys_cpu_sets, + }, cpu_config: AxVCpuConfig { bsp_entry: GuestPhysAddr::from(cfg.kernel.entry_point), ap_entry: GuestPhysAddr::from(cfg.kernel.entry_point), @@ -96,9 +98,11 @@ impl From for AxVMConfig { dtb_load_gpa: cfg.kernel.dtb_load_addr.map(GuestPhysAddr::from), ramdisk_load_gpa: cfg.kernel.ramdisk_load_addr.map(GuestPhysAddr::from), }, - memory_regions: cfg.kernel.memory_regions, + // memory_regions: cfg.kernel.memory_regions, emu_devices: cfg.devices.emu_devices, pass_through_devices: cfg.devices.passthrough_devices, + excluded_devices: cfg.devices.excluded_devices, + pass_through_addresses: cfg.devices.passthrough_addresses, spi_list: Vec::new(), interrupt_mode: cfg.devices.interrupt_mode, } @@ -116,32 +120,6 @@ impl AxVMConfig { self.name.clone() } - /// Returns vCpu id list and its corresponding pCpu affinity list, as well as its physical id. - /// If the pCpu affinity is None, it means the vCpu will be allocated to any available pCpu randomly. - /// if the pCPU id is not provided, the vCpu's physical id will be set as vCpu id. - /// - /// Returns a vector of tuples, each tuple contains: - /// - The vCpu id. - /// - The pCpu affinity mask, `None` if not set. - /// - The physical id of the vCpu, equal to vCpu id if not provided. - pub fn get_vcpu_affinities_pcpu_ids(&self) -> Vec<(usize, Option, usize)> { - let mut vcpu_pcpu_tuples = Vec::new(); - for vcpu_id in 0..self.cpu_num { - vcpu_pcpu_tuples.push((vcpu_id, None, vcpu_id)); - } - if let Some(phys_cpu_sets) = &self.phys_cpu_sets { - for (vcpu_id, pcpu_mask_bitmap) in phys_cpu_sets.iter().enumerate() { - vcpu_pcpu_tuples[vcpu_id].1 = Some(*pcpu_mask_bitmap); - } - } - if let Some(phys_cpu_ids) = &self.phys_cpu_ids { - for (vcpu_id, phys_id) in phys_cpu_ids.iter().enumerate() { - vcpu_pcpu_tuples[vcpu_id].2 = *phys_id; - } - } - vcpu_pcpu_tuples - } - /// Returns configurations related to VM image load addresses. pub fn image_config(&self) -> &VMImageConfig { &self.image_config @@ -159,22 +137,36 @@ impl AxVMConfig { self.cpu_config.ap_entry } - /// Returns configurations related to VM memory regions. - pub fn memory_regions(&self) -> &Vec { - &self.memory_regions + /// Returns a mutable reference to the physical CPU list. + pub fn phys_cpu_ls_mut(&mut self) -> &mut PhysCpuList { + &mut self.phys_cpu_ls } - /// Adds a new memory region to the VM configuration. - pub fn add_memory_region(&mut self, region: VmMemConfig) { - self.memory_regions.push(region); + /// Returns the list of excluded devices. + pub fn excluded_devices(&self) -> &Vec> { + &self.excluded_devices } - /// Checks if the VM memory regions contain a specific range. - pub fn contains_memory_range(&self, range: &Range) -> bool { - self.memory_regions - .iter() - .any(|region| region.gpa <= range.start && region.gpa + region.size >= range.end) + /// Returns the list of passthrough address configurations. + pub fn pass_through_addresses(&self) -> &Vec { + &self.pass_through_addresses } + // /// Returns configurations related to VM memory regions. + // pub fn memory_regions(&self) -> Vec { + // &self.memory_regions + // } + + // /// Adds a new memory region to the VM configuration. + // pub fn add_memory_region(&mut self, region: VmMemConfig) { + // self.memory_regions.push(region); + // } + + // /// Checks if the VM memory regions contain a specific range. + // pub fn contains_memory_range(&self, range: &Range) -> bool { + // self.memory_regions + // .iter() + // .any(|region| region.gpa <= range.start && region.gpa + region.size >= range.end) + // } /// Returns configurations related to VM emulated devices. pub fn emu_devices(&self) -> &Vec { @@ -191,6 +183,16 @@ impl AxVMConfig { self.pass_through_devices.push(device); } + /// Removes passthrough device from the VM configuration. + pub fn remove_pass_through_device(&mut self, device: PassThroughDeviceConfig) { + self.pass_through_devices.retain(|d| d == &device); + } + + /// Clears all passthrough devices from the VM configuration. + pub fn clear_pass_through_devices(&mut self) { + self.pass_through_devices.clear(); + } + /// Adds a passthrough SPI to the VM configuration. pub fn add_pass_through_spi(&mut self, spi: u32) { self.spi_list.push(spi); @@ -206,3 +208,89 @@ impl AxVMConfig { self.interrupt_mode } } + +/// Represents the list of physical CPUs available for the VM. +#[derive(Debug, Default, Clone)] +pub struct PhysCpuList { + cpu_num: usize, + phys_cpu_ids: Option>, + phys_cpu_sets: Option>, +} + +impl PhysCpuList { + /// Returns vCpu id list and its corresponding pCpu affinity list, as well as its physical id. + /// If the pCpu affinity is None, it means the vCpu will be allocated to any available pCpu randomly. + /// if the pCPU id is not provided, the vCpu's physical id will be set as vCpu id. + /// + /// Returns a vector of tuples, each tuple contains: + /// - The vCpu id. + /// - The pCpu affinity mask, `None` if not set. + /// - The physical id of the vCpu, equal to vCpu id if not provided. + pub fn get_vcpu_affinities_pcpu_ids(&self) -> Vec<(usize, Option, usize)> { + let mut vcpu_pcpu_tuples = Vec::new(); + #[cfg(target_arch = "riscv64")] + let mut pcpu_mask_flag = false; + + if let Some(phys_cpu_ids) = &self.phys_cpu_ids + && self.cpu_num != phys_cpu_ids.len() + { + error!( + "ERROR!!!: cpu_num: {}, phys_cpu_ids: {:?}", + self.cpu_num, self.phys_cpu_ids + ); + } + + for vcpu_id in 0..self.cpu_num { + vcpu_pcpu_tuples.push((vcpu_id, None, vcpu_id)); + } + + #[cfg(target_arch = "riscv64")] + if let Some(phys_cpu_sets) = &self.phys_cpu_sets { + pcpu_mask_flag = true; + for (vcpu_id, pcpu_mask_bitmap) in phys_cpu_sets.iter().enumerate() { + vcpu_pcpu_tuples[vcpu_id].1 = Some(*pcpu_mask_bitmap); + } + } + + #[cfg(not(target_arch = "riscv64"))] + if let Some(phys_cpu_sets) = &self.phys_cpu_sets { + for (vcpu_id, pcpu_mask_bitmap) in phys_cpu_sets.iter().enumerate() { + vcpu_pcpu_tuples[vcpu_id].1 = Some(*pcpu_mask_bitmap); + } + } + + if let Some(phys_cpu_ids) = &self.phys_cpu_ids { + for (vcpu_id, phys_id) in phys_cpu_ids.iter().enumerate() { + vcpu_pcpu_tuples[vcpu_id].2 = *phys_id; + #[cfg(target_arch = "riscv64")] + { + if !pcpu_mask_flag { + // if don't assign pcpu mask yet, assign it manually + vcpu_pcpu_tuples[vcpu_id].1 = Some(1 << (*phys_id)); + } + } + } + } + vcpu_pcpu_tuples + } + + /// Returns the number of CPUs. + pub fn cpu_num(&self) -> usize { + self.cpu_num + } + + /// Returns the physical CPU IDs. + pub fn phys_cpu_ids(&self) -> &Option> { + &self.phys_cpu_ids + } + + /// Returns the physical CPU sets. + pub fn phys_cpu_sets(&self) -> &Option> { + &self.phys_cpu_sets + } + + /// Sets the guest CPU sets. + pub fn set_guest_cpu_sets(&mut self, phys_cpu_sets: Vec) { + self.phys_cpu_sets = Some(phys_cpu_sets); + } +} diff --git a/src/hal.rs b/src/hal.rs index 22e2496ac..787c074f4 100644 --- a/src/hal.rs +++ b/src/hal.rs @@ -20,14 +20,6 @@ pub trait AxVMHal: Sized { /// The low-level **OS-dependent** helpers that must be provided for physical address management. type PagingHandler: page_table_multiarch::PagingHandler; - /// Allocates a memory region at the specified physical address. - /// - /// Returns `true` if the memory region is successfully allocated. - fn alloc_memory_region_at(base: HostPhysAddr, size: usize) -> bool; - - /// Deallocates a memory region at the specified physical address. - fn dealloc_memory_region_at(base: HostPhysAddr, size: usize); - /// Converts a virtual address to the corresponding physical address. fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr; diff --git a/src/lib.rs b/src/lib.rs index 4ff0b6045..30c8c0cf9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,8 @@ pub use hal::AxVMHal; pub use vm::AxVCpuRef; pub use vm::AxVM; pub use vm::AxVMRef; +pub use vm::VMMemoryRegion; +pub use vm::VMStatus; /// The architecture-independent per-CPU type. pub type AxVMPerCpu = axvcpu::AxPerCpu>; diff --git a/src/vm.rs b/src/vm.rs index 264b1a26d..12f62d9b6 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -16,30 +16,26 @@ use alloc::boxed::Box; use alloc::format; use alloc::sync::Arc; use alloc::vec::Vec; -#[cfg(target_arch = "aarch64")] -use axvmconfig::VMInterruptMode; -use core::sync::atomic::{AtomicBool, Ordering}; +use axaddrspace::HostVirtAddr; +use axerrno::{AxError, AxResult, ax_err, ax_err_type}; +use core::alloc::Layout; +use core::fmt; use memory_addr::{align_down_4k, align_up_4k}; - -use axerrno::{AxResult, ax_err, ax_err_type}; -use spin::Mutex; +use spin::{Mutex, Once}; use axaddrspace::{AddrSpace, GuestPhysAddr, HostPhysAddr, MappingFlags, device::AccessWidth}; use axdevice::{AxVmDeviceConfig, AxVmDevices}; use axvcpu::{AxVCpu, AxVCpuExitReason, AxVCpuHal}; - use cpumask::CpuMask; -use crate::config::{AxVMConfig, VmMemMappingType}; +use crate::config::{AxVMConfig, PhysCpuList}; use crate::vcpu::AxArchVCpuImpl; - -#[cfg(not(target_arch = "x86_64"))] -use crate::vcpu::AxVCpuCreateConfig; - use crate::{AxVMHal, has_hardware_support}; +#[cfg(target_arch = "riscv64")] +use crate::vcpu::AxVCpuCreateConfig; #[cfg(target_arch = "aarch64")] -use crate::vcpu::get_sysreg_device; +use crate::vcpu::{AxVCpuCreateConfig, get_sysreg_device}; const VM_ASPACE_BASE: usize = 0x0; const VM_ASPACE_SIZE: usize = 0x7fff_ffff_f000; @@ -55,8 +51,7 @@ pub type AxVCpuRef = Arc>; pub type AxVMRef = Arc>; // we know the bound is not enforced here, we keep it for clarity struct AxVMInnerConst { - id: usize, - config: AxVMConfig, + phys_cpu_ls: PhysCpuList, vcpu_list: Box<[AxVCpuRef]>, devices: AxVmDevices, } @@ -64,20 +59,96 @@ struct AxVMInnerConst { unsafe impl Send for AxVMInnerConst {} unsafe impl Sync for AxVMInnerConst {} +/// Represents a memory region in a virtual machine. +#[derive(Debug, Clone)] +pub struct VMMemoryRegion { + /// Guest physical address. + pub gpa: GuestPhysAddr, + /// Host virtual address. + pub hva: HostVirtAddr, + /// Memory layout of the region. + pub layout: Layout, + /// Whether this region was allocated by the allocator and needs to be deallocated + pub needs_dealloc: bool, +} + +impl VMMemoryRegion { + /// Returns the size of the memory region. + pub fn size(&self) -> usize { + self.layout.size() + } + + /// Returns `true` if the guest physical address is identical to the host virtual address. + pub fn is_identical(&self) -> bool { + self.gpa.as_usize() == self.hva.as_usize() + } +} + struct AxVMInnerMut { // Todo: use more efficient lock. - address_space: Mutex>, + address_space: AddrSpace, + memory_regions: Vec, + config: AxVMConfig, + vm_status: VMStatus, _marker: core::marker::PhantomData, } +/// VM status enumeration representing the lifecycle states of a virtual machine +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum VMStatus { + /// VM is being created/loaded + Loading, + /// VM is loaded but not yet started + Loaded, + /// VM is currently running + Running, + /// VM is suspended (paused but can be resumed) + Suspended, + /// VM is in the process of shutting down + Stopping, + /// VM is stopped + Stopped, +} + +impl VMStatus { + /// Get status as a string (lowercase) + pub fn as_str(&self) -> &'static str { + match self { + VMStatus::Loading => "loading", + VMStatus::Loaded => "loaded", + VMStatus::Running => "running", + VMStatus::Suspended => "suspended", + VMStatus::Stopping => "stopping", + VMStatus::Stopped => "stopped", + } + } + + /// Get status with emoji icon + pub fn as_str_with_icon(&self) -> &'static str { + match self { + VMStatus::Loading => "🔄 loading", + VMStatus::Loaded => "📦 loaded", + VMStatus::Running => "🚀 running", + VMStatus::Suspended => "🛑 suspended", + VMStatus::Stopping => "⏹️ stopping", + VMStatus::Stopped => "💤 stopped", + } + } +} + +impl fmt::Display for VMStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + const TEMP_MAX_VCPU_NUM: usize = 64; /// A Virtual Machine. pub struct AxVM { - running: AtomicBool, - shutting_down: AtomicBool, - inner_const: AxVMInnerConst, - inner_mut: AxVMInnerMut, + id: usize, + inner_const: Once>, + inner_mut: Mutex>, } impl AxVM { @@ -85,122 +156,71 @@ impl AxVM { /// Returns an error if the configuration is invalid. /// The VM is not started until `boot` is called. pub fn new(config: AxVMConfig) -> AxResult> { - let vcpu_id_pcpu_sets = config.get_vcpu_affinities_pcpu_ids(); + let address_space = + AddrSpace::new_empty(GuestPhysAddr::from(VM_ASPACE_BASE), VM_ASPACE_SIZE)?; - debug!( - "id: {}, VCpuIdPCpuSets: {:#x?}", - config.id(), - vcpu_id_pcpu_sets - ); + let result = Arc::new(Self { + id: config.id(), + inner_const: Once::new(), + inner_mut: Mutex::new(AxVMInnerMut { + address_space, + config, + memory_regions: Vec::new(), + vm_status: VMStatus::Loading, + _marker: core::marker::PhantomData, + }), + }); + + info!("VM created: id={}", result.id()); + + Ok(result) + } + + /// Returns the VM id. + #[inline] + pub fn id(&self) -> usize { + self.id + } + + /// Sets up the VM before booting. + pub fn init(&self) -> AxResult { + let mut inner_mut = self.inner_mut.lock(); + + let dtb_addr = inner_mut.config.image_config().dtb_load_gpa; + let vcpu_id_pcpu_sets = inner_mut.config.phys_cpu_ls.get_vcpu_affinities_pcpu_ids(); + + info!("dtb_load_gpa: {:?}", dtb_addr); + debug!("id: {}, VCpuIdPCpuSets: {vcpu_id_pcpu_sets:#x?}", self.id()); let mut vcpu_list = Vec::with_capacity(vcpu_id_pcpu_sets.len()); for (vcpu_id, phys_cpu_set, _pcpu_id) in vcpu_id_pcpu_sets { #[cfg(target_arch = "aarch64")] let arch_config = AxVCpuCreateConfig { mpidr_el1: _pcpu_id as _, - dtb_addr: config - .image_config() - .dtb_load_gpa - .unwrap_or_default() - .as_usize(), + dtb_addr: dtb_addr.unwrap_or_default().as_usize(), }; #[cfg(target_arch = "riscv64")] let arch_config = AxVCpuCreateConfig { hart_id: vcpu_id as _, - dtb_addr: config - .image_config() - .dtb_load_gpa - .unwrap_or(GuestPhysAddr::from_usize(0x9000_0000)), + dtb_addr: dtb_addr.unwrap_or_default().as_usize(), }; - #[cfg(not(target_arch = "x86_64"))] vcpu_list.push(Arc::new(VCpu::new( - config.id(), + self.id(), vcpu_id, 0, // Currently not used. phys_cpu_set, + #[cfg(target_arch = "aarch64")] arch_config, - )?)); - - #[cfg(target_arch = "x86_64")] - vcpu_list.push(Arc::new(VCpu::new( - config.id(), - vcpu_id, - 0, // Currently not used. - phys_cpu_set, + #[cfg(target_arch = "riscv64")] + arch_config, + #[cfg(target_arch = "x86_64")] (), )?)); } - let mut address_space = - AddrSpace::new_empty(GuestPhysAddr::from(VM_ASPACE_BASE), VM_ASPACE_SIZE)?; - - for mem_region in config.memory_regions() { - let mapping_flags = MappingFlags::from_bits(mem_region.flags).ok_or_else(|| { - ax_err_type!( - InvalidInput, - format!("Illegal flags {:?}", mem_region.flags) - ) - })?; - - // Check mapping flags. - if mapping_flags.contains(MappingFlags::DEVICE) { - warn!( - "Do not include DEVICE flag in memory region flags, it should be configured in pass_through_devices" - ); - continue; - } - - info!( - "Setting up memory region: [{:#x}~{:#x}] {:?}", - mem_region.gpa, - mem_region.gpa + mem_region.size, - mapping_flags - ); - - // Handle ram region. - match mem_region.map_type { - VmMemMappingType::MapIdentical => { - if H::alloc_memory_region_at( - HostPhysAddr::from(mem_region.gpa), - mem_region.size, - ) { - } else { - address_space.map_linear( - GuestPhysAddr::from(mem_region.gpa), - HostPhysAddr::from(mem_region.gpa), - mem_region.size, - mapping_flags, - )?; - warn!( - "Failed to allocate memory region at {:#x} for VM [{}]", - mem_region.gpa, - config.id() - ); - } - - address_space.map_linear( - GuestPhysAddr::from(mem_region.gpa), - HostPhysAddr::from(mem_region.gpa), - mem_region.size, - mapping_flags, - )?; - } - VmMemMappingType::MapAlloc => { - // Note: currently we use `map_alloc`, - // which allocates real physical memory in units of physical page frames, - // which may not be contiguous!!! - address_space.map_alloc( - GuestPhysAddr::from(mem_region.gpa), - mem_region.size, - mapping_flags, - true, - )?; - } - } - } let mut pt_dev_region = Vec::new(); - for pt_device in config.pass_through_devices() { + for pt_device in inner_mut.config.pass_through_devices() { trace!( "PT dev {:?} region: [{:#x}~{:#x}] -> [{:#x}~{:#x}]", pt_device.name, @@ -216,6 +236,16 @@ impl AxVM { )); } + for pt_addr in inner_mut.config.pass_through_addresses() { + debug!( + "PT addr region: [{:#x}~{:#x}]", + pt_addr.base_gpa, + pt_addr.base_gpa + pt_addr.length, + ); + // Align the base address and length to 4K boundaries. + pt_dev_region.push((align_down_4k(pt_addr.base_gpa), align_up_4k(pt_addr.length))); + } + pt_dev_region.sort_by_key(|(gpa, _)| *gpa); // Merge overlapping regions. @@ -237,7 +267,7 @@ impl AxVM { }); for (gpa, len) in &pt_dev_region { - address_space.map_linear( + inner_mut.address_space.map_linear( GuestPhysAddr::from(*gpa), HostPhysAddr::from(*gpa), *len, @@ -250,21 +280,20 @@ impl AxVM { #[cfg(target_arch = "aarch64")] let mut devices = axdevice::AxVmDevices::new(AxVmDeviceConfig { - emu_configs: config.emu_devices().to_vec(), + emu_configs: inner_mut.config.emu_devices().to_vec(), }); - #[cfg(target_arch = "aarch64")] - let passthrough = config.interrupt_mode() == VMInterruptMode::Passthrough; - #[cfg(not(target_arch = "aarch64"))] let devices = axdevice::AxVmDevices::new(AxVmDeviceConfig { - emu_configs: config.emu_devices().to_vec(), + emu_configs: inner_mut.config.emu_devices().to_vec(), }); #[cfg(target_arch = "aarch64")] { + let passthrough = + inner_mut.config.interrupt_mode() == axvmconfig::VMInterruptMode::Passthrough; if passthrough { - let spis = config.pass_through_spis(); - let cpu_id = config.id() - 1; // FIXME: get the real CPU id. + let spis = inner_mut.config.pass_through_spis(); + let cpu_id = self.id() - 1; // FIXME: get the real CPU id. let mut gicd_found = false; for device in devices.iter_mmio_dev() { @@ -277,7 +306,7 @@ impl AxVM { gicd.assign_irq(*spi + 32, cpu_id, (0, 0, 0, cpu_id as _)) } - Ok(()) + AxResult::Ok(()) }, ) { result?; @@ -300,58 +329,55 @@ impl AxVM { } } - let result = Arc::new(Self { - running: AtomicBool::new(false), - shutting_down: AtomicBool::new(false), - inner_const: AxVMInnerConst { - id: config.id(), - config, - vcpu_list: vcpu_list.into_boxed_slice(), - devices, - }, - inner_mut: AxVMInnerMut { - address_space: Mutex::new(address_space), - _marker: core::marker::PhantomData, - }, + self.inner_const.call_once(|| AxVMInnerConst { + phys_cpu_ls: inner_mut.config.phys_cpu_ls.clone(), + vcpu_list: vcpu_list.into_boxed_slice(), + devices, }); - info!("VM created: id={}", result.id()); - // Setup VCpus. - #[cfg(target_arch = "aarch64")] - for vcpu in result.vcpu_list() { - let setup_config = crate::vcpu::AxVCpuSetupConfig { - passthrough_interrupt: passthrough, - passthrough_timer: passthrough, + for vcpu in self.vcpu_list() { + #[cfg(target_arch = "aarch64")] + let setup_config = { + let passthrough = + inner_mut.config.interrupt_mode() == axvmconfig::VMInterruptMode::Passthrough; + crate::vcpu::AxVCpuSetupConfig { + passthrough_interrupt: passthrough, + passthrough_timer: passthrough, + } }; let entry = if vcpu.id() == 0 { - result.inner_const.config.bsp_entry() + inner_mut.config.bsp_entry() } else { - result.inner_const.config.ap_entry() + inner_mut.config.ap_entry() }; - vcpu.setup(entry, result.ept_root(), setup_config)?; - } - #[cfg(not(target_arch = "aarch64"))] - for vcpu in result.vcpu_list() { - let entry = if vcpu.id() == 0 { - result.inner_const.config.bsp_entry() - } else { - result.inner_const.config.ap_entry() - }; - vcpu.setup(entry, result.ept_root(), ())?; - } + debug!("Setting up vCPU[{}] entry at {:#x}", vcpu.id(), entry); - info!("VM setup: id={}", result.id()); + vcpu.setup( + entry, + inner_mut.address_space.page_table_root(), + #[cfg(target_arch = "aarch64")] + setup_config, + #[cfg(not(target_arch = "aarch64"))] + (), + )?; + } + info!("VM setup: id={}", self.id()); + Ok(()) + } - Ok(result) + /// Sets the VM status. + pub fn set_vm_status(&self, status: VMStatus) { + let mut inner_mut = self.inner_mut.lock(); + inner_mut.vm_status = status; } - /// Returns the VM id. - #[inline] - pub const fn id(&self) -> usize { - self.inner_const.id + /// Returns the current VM status. + pub fn vm_status(&self) -> VMStatus { + let inner_mut = self.inner_mut.lock(); + inner_mut.vm_status } /// Retrieves the vCPU corresponding to the given vcpu_id for the VM. @@ -363,19 +389,34 @@ impl AxVM { /// Returns the number of vCPUs corresponding to the VM. #[inline] - pub const fn vcpu_num(&self) -> usize { - self.inner_const.vcpu_list.len() + pub fn vcpu_num(&self) -> usize { + self.inner_const().vcpu_list.len() + } + + fn inner_const(&self) -> &AxVMInnerConst { + self.inner_const + .get() + .expect("VM inner_const not initialized") } /// Returns a reference to the list of vCPUs corresponding to the VM. #[inline] pub fn vcpu_list(&self) -> &[AxVCpuRef] { - &self.inner_const.vcpu_list + &self.inner_const().vcpu_list } /// Returns the base address of the two-stage address translation page table for the VM. pub fn ept_root(&self) -> HostPhysAddr { - self.inner_mut.address_space.lock().page_table_root() + self.inner_mut.lock().address_space.page_table_root() + } + + /// Returns to the VM's configuration. + pub fn with_config(&self, f: F) -> R + where + F: FnOnce(&mut AxVMConfig) -> R, + { + let mut g = self.inner_mut.lock(); + f(&mut g.config) } /// Returns guest VM image load region in `Vec<&'static mut [u8]>`, @@ -391,19 +432,15 @@ impl AxVM { image_load_gpa: GuestPhysAddr, image_size: usize, ) -> AxResult> { - let addr_space = self.inner_mut.address_space.lock(); - let image_load_hva = addr_space + let g = self.inner_mut.lock(); + let image_load_hva = g + .address_space .translated_byte_buffer(image_load_gpa, image_size) .expect("Failed to translate kernel image load address"); Ok(image_load_hva) } - /// Returns if the VM is running. - pub fn running(&self) -> bool { - self.running.load(Ordering::Relaxed) - } - - /// Boots the VM by setting the running flag as true. + /// Boots the VM by transitioning to Running state. pub fn boot(&self) -> AxResult { if !has_hardware_support() { ax_err!(Unsupported, "Hardware does not support virtualization") @@ -411,29 +448,44 @@ impl AxVM { ax_err!(BadState, format!("VM[{}] is already running", self.id())) } else { info!("Booting VM[{}]", self.id()); - self.running.store(true, Ordering::Relaxed); + self.set_vm_status(VMStatus::Running); Ok(()) } } - /// Returns if the VM is shutting down. - pub fn shutting_down(&self) -> bool { - self.shutting_down.load(Ordering::Relaxed) + /// Returns if the VM is running. + pub fn running(&self) -> bool { + self.vm_status() == VMStatus::Running } - /// Shuts down the VM by setting the shutting_down flag as true. + /// Returns if the VM is shutting down (in Stopping state). + pub fn stopping(&self) -> bool { + self.vm_status() == VMStatus::Stopping + } + + /// Returns if the VM is suspended. + pub fn suspending(&self) -> bool { + self.vm_status() == VMStatus::Suspended + } + + /// Returns if the VM is stopped. + pub fn stopped(&self) -> bool { + self.vm_status() == VMStatus::Stopped + } + + /// Shuts down the VM by transitioning to Stopping state. /// + /// This method sets the VM status to Stopping, which signals all vCPUs to exit. /// Currently, the "re-init" process of the VM is not implemented. Therefore, a VM can only be /// booted once. And after the VM is shut down, it cannot be booted again. pub fn shutdown(&self) -> AxResult { - if self.shutting_down() { - ax_err!( - BadState, - format!("VM[{}] is already shutting down", self.id()) - ) + if self.stopping() { + ax_err!(BadState, format!("VM[{}] is already stopping", self.id())) + } else if self.stopped() { + ax_err!(BadState, format!("VM[{}] is already stopped", self.id())) } else { info!("Shutting down VM[{}]", self.id()); - self.shutting_down.store(true, Ordering::Relaxed); + self.set_vm_status(VMStatus::Stopping); Ok(()) } } @@ -443,7 +495,7 @@ impl AxVM { /// Returns this VM's emulated devices. pub fn get_devices(&self) -> &AxVmDevices { - &self.inner_const.devices + &self.inner_const().devices } /// Run a vCPU according to the given vcpu_id. @@ -483,8 +535,12 @@ impl AxVM { } AxVCpuExitReason::IoRead { port, width } => { let val = self.get_devices().handle_port_read(*port, *width)?; + #[cfg(not(target_arch = "riscv64"))] vcpu.set_gpr(0, val); // The target is always eax/ax/al, todo: handle access_width correctly + #[cfg(target_arch = "riscv64")] + vcpu.set_gpr(riscv_vcpu::GprIndex::A0 as usize, val); + true } AxVCpuExitReason::IoWrite { port, width, data } => { @@ -512,8 +568,8 @@ impl AxVM { } AxVCpuExitReason::NestedPageFault { addr, access_flags } => self .inner_mut - .address_space .lock() + .address_space .handle_page_fault(*addr, *access_flags), _ => false, }; @@ -549,11 +605,25 @@ impl AxVM { Ok(()) } - /// Returns a reference to the VM's configuration. - pub fn config(&self) -> &AxVMConfig { - &self.inner_const.config + /// Returns vCpu id list and its corresponding pCpu affinity list, as well as its physical id. + /// If the pCpu affinity is None, it means the vCpu will be allocated to any available pCpu randomly. + /// if the pCPU id is not provided, the vCpu's physical id will be set as vCpu id. + /// + /// Returns a vector of tuples, each tuple contains: + /// - The vCpu id. + /// - The pCpu affinity mask, `None` if not set. + /// - The physical id of the vCpu, equal to vCpu id if not provided. + pub fn get_vcpu_affinities_pcpu_ids(&self) -> Vec<(usize, Option, usize)> { + self.inner_const() + .phys_cpu_ls + .get_vcpu_affinities_pcpu_ids() } + // /// Returns a reference to the VM's configuration. + // pub fn config(&self) -> &AxVMConfig { + // &self.inner_const.config + // } + /// Maps a region of host physical memory to guest physical memory. pub fn map_region( &self, @@ -561,16 +631,18 @@ impl AxVM { hpa: HostPhysAddr, size: usize, flags: MappingFlags, - ) -> AxResult<()> { + ) -> AxResult { self.inner_mut - .address_space .lock() - .map_linear(gpa, hpa, size, flags) + .address_space + .map_linear(gpa, hpa, size, flags)?; + Ok(()) } /// Unmaps a region of guest physical memory. - pub fn unmap_region(&self, gpa: GuestPhysAddr, size: usize) -> AxResult<()> { - self.inner_mut.address_space.lock().unmap(gpa, size) + pub fn unmap_region(&self, gpa: GuestPhysAddr, size: usize) -> AxResult { + self.inner_mut.lock().address_space.unmap(gpa, size)?; + Ok(()) } /// Reads an object of type `T` from the guest physical address. @@ -585,8 +657,8 @@ impl AxVM { return ax_err!(InvalidInput, "Unaligned guest physical address"); } - let addr_space = self.inner_mut.address_space.lock(); - match addr_space.translated_byte_buffer(gpa_ptr, size) { + let g = self.inner_mut.lock(); + match g.address_space.translated_byte_buffer(gpa_ptr, size) { Some(buffers) => { let mut data_bytes = Vec::with_capacity(size); for chunk in buffers { @@ -618,9 +690,12 @@ impl AxVM { /// Writes an object of type `T` to the guest physical address. pub fn write_to_guest_of(&self, gpa_ptr: GuestPhysAddr, data: &T) -> AxResult { - let addr_space = self.inner_mut.address_space.lock(); - - match addr_space.translated_byte_buffer(gpa_ptr, core::mem::size_of::()) { + match self + .inner_mut + .lock() + .address_space + .translated_byte_buffer(gpa_ptr, core::mem::size_of::()) + { Some(mut buffer) => { let bytes = unsafe { core::slice::from_raw_parts( @@ -649,7 +724,7 @@ impl AxVM { pub fn alloc_ivc_channel(&self, expected_size: usize) -> AxResult<(GuestPhysAddr, usize)> { // Ensure the expected size is aligned to 4K. let size = align_up_4k(expected_size); - let gpa = self.inner_const.devices.alloc_ivc_channel(size)?; + let gpa = self.inner_const().devices.alloc_ivc_channel(size)?; Ok((gpa, size)) } @@ -660,6 +735,201 @@ impl AxVM { /// ## Returns /// * `AxResult<()>` - An empty result indicating success or failure. pub fn release_ivc_channel(&self, gpa: GuestPhysAddr, size: usize) -> AxResult { - self.inner_const.devices.release_ivc_channel(gpa, size) + self.inner_const().devices.release_ivc_channel(gpa, size)?; + Ok(()) + } + + /// Allocates a new memory region for the VM. + pub fn alloc_memory_region( + &self, + layout: Layout, + gpa: Option, + ) -> AxResult<&[u8]> { + assert!( + layout.size() > 0, + "Cannot allocate zero-sized memory region" + ); + + let hva = unsafe { alloc::alloc::alloc_zeroed(layout) }; + if hva.is_null() { + return Err(AxError::NoMemory); + } + let s = unsafe { core::slice::from_raw_parts_mut(hva, layout.size()) }; + let hva = HostVirtAddr::from_mut_ptr_of(hva); + + let hpa = H::virt_to_phys(hva); + + let gpa = gpa.unwrap_or_else(|| hpa.as_usize().into()); + + let mut g = self.inner_mut.lock(); + g.address_space.map_linear( + gpa, + hpa, + layout.size(), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE | MappingFlags::USER, + )?; + g.memory_regions.push(VMMemoryRegion { + gpa, + hva, + layout, + needs_dealloc: true, // This region was allocated and needs to be freed + }); + + Ok(s) + } + + /// Returns a list of all memory regions in the VM. + pub fn memory_regions(&self) -> Vec { + self.inner_mut.lock().memory_regions.clone() + } + + /// Maps a reserved memory region for the VM. + pub fn map_reserved_memory_region( + &self, + layout: Layout, + gpa: Option, + ) -> AxResult<&[u8]> { + assert!( + layout.size() > 0, + "Cannot allocate zero-sized memory region" + ); + let mut g = self.inner_mut.lock(); + g.address_space.map_linear( + gpa.unwrap(), + gpa.unwrap().as_usize().into(), + layout.size(), + MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE | MappingFlags::USER, + )?; + let hva = gpa.unwrap().as_usize().into(); + let tem_hva = gpa.unwrap().as_usize() as *mut u8; + let s = unsafe { core::slice::from_raw_parts_mut(tem_hva, layout.size()) }; + let gpa = gpa.unwrap(); + g.memory_regions.push(VMMemoryRegion { + gpa, + hva, + layout, + needs_dealloc: false, // This is a reserved region, not allocated + }); + Ok(s) + } + + /// Cleanup resources for the VM before drop. + /// This is called internally by the Drop implementation. + fn cleanup_resources(&self) { + info!("Cleaning up VM[{}] resources...", self.id()); + + // 1. Ensure the VM is in Stopping or Stopped state + let current_status = self.vm_status(); + if !matches!(current_status, VMStatus::Stopping | VMStatus::Stopped) { + warn!( + "VM[{}] is being dropped without explicit shutdown (status: {:?}), marking as stopping", + self.id(), + current_status + ); + self.set_vm_status(VMStatus::Stopping); + } + + let mut inner_mut = self.inner_mut.lock(); + + // First, collect all memory regions to clean up + // We need to clone the regions to avoid borrowing issues + let regions_to_cleanup: Vec = inner_mut.memory_regions.clone(); + + // Unmap all memory regions from the address space + // This must be done BEFORE deallocating memory to avoid use-after-free + for region in ®ions_to_cleanup { + debug!( + "VM[{}] unmapping memory region: GPA={:#x}, size={:#x}", + self.id(), + region.gpa.as_usize(), + region.size() + ); + // Unmap the region from guest physical address space + if let Err(e) = inner_mut.address_space.unmap(region.gpa, region.size()) { + warn!( + "VM[{}] failed to unmap region at GPA={:#x}: {:?}", + self.id(), + region.gpa.as_usize(), + e + ); + } + } + + // Now it's safe to deallocate the memory + for region in ®ions_to_cleanup { + // Only deallocate memory regions that were allocated by the allocator + if region.needs_dealloc { + debug!( + "VM[{}] deallocating memory region: HVA={:#x}, size={:#x}", + self.id(), + region.hva.as_usize(), + region.size() + ); + unsafe { + alloc::alloc::dealloc(region.hva.as_mut_ptr(), region.layout); + } + } else { + debug!( + "VM[{}] skipping dealloc for reserved memory region: GPA={:#x}, HVA={:#x}, size={:#x}", + self.id(), + region.gpa.as_usize(), + region.hva.as_usize(), + region.size() + ); + } + } + inner_mut.memory_regions.clear(); + + // Clear remaining address space mappings + // This includes: + // - Passthrough device MMIO mappings + // - Emulated device MMIO mappings + // - Reserved memory mappings + // - All other page table entries + debug!( + "VM[{}] clearing remaining address space mappings", + self.id() + ); + inner_mut.address_space.clear(); + + // Release the lock before accessing inner_const + drop(inner_mut); + + // Device cleanup + // Although devices will be automatically dropped when inner_const is dropped, + // we should perform explicit cleanup if devices hold resources like: + // - Hardware interrupt registrations + // - DMA mappings + // - Background threads or timers + if let Some(inner_const) = self.inner_const.get() { + debug!( + "VM[{}] devices cleanup: {} MMIO devices, {} SysReg devices", + self.id(), + inner_const.devices.iter_mmio_dev().count(), + inner_const.devices.iter_sys_reg_dev().count() + ); + + // TODO: Add device-specific cleanup if needed + // For example: + // - Stop device background tasks + // - Unregister interrupts + // - Release device-specific resources + + // Note: Device Arc references will be dropped automatically when + // inner_const is dropped at the end of AxVM's drop + } + + info!("VM[{}] resources cleanup completed", self.id()); + } +} + +impl Drop for AxVM { + fn drop(&mut self) { + info!("Dropping VM[{}]", self.id()); + + // Clean up all allocated resources + self.cleanup_resources(); + + info!("VM[{}] dropped", self.id()); } } From 86d8d6ceadbb2997ca864bed9b1e6db9da4a3425 Mon Sep 17 00:00:00 2001 From: TQ <128586861+YanLien@users.noreply.github.com> Date: Tue, 10 Feb 2026 09:37:03 +0800 Subject: [PATCH 056/132] ci: fix doc deployment for cross-target builds (#51) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: deps git to crates-io * fix: feature * refactor: support guest dyn entry * refactor: add Clone trait to VMImageConfig * fix: update arm_vcpu branch to 'next' * add fdt support and add some AxVMConfig impl * fix fmt bug * fix bug: when no phys_cpu_ids panic * add function map_reserved_memory_region (#28) * add function map_reserved_memory_region * add passthrough address (#29) * add pass_through address --------- Co-authored-by: szy * add VMStatus enum and integrate into AxVM lifecycle management (#31) * feat: update axerrno to 0.2 (#33) * feat: update axerrno to 0.2 * fix: clippy * Update riscv_vcpu dependency source (#39) * fix(riscv): Fix RISC-V vCPU initialization and I/O handling * chore: Update riscv_vcpu dependency source * chore: specify axdevice branch and fix code formatting * docs: add missing documentation comments * chore: migrate from git to published versions * chore: bump version to v0.2.1 * fix(vm): remove duplicate code lines * fix(vm): adapt to axvcpu v0.2 API changes * fix(vm): adapt to v0.2 API changes for multi-architecture compatibility * ci: fix doc deployment for cross-target builds --------- Co-authored-by: 周睿 Co-authored-by: szy Co-authored-by: bhxh <32200913+buhenxihuan@users.noreply.github.com> Co-authored-by: szy <673586548@qq.com> Co-authored-by: 朝倉水希 --- .github/workflows/deploy.yml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 882ba9310..fda9a323d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -87,13 +87,30 @@ jobs: env: RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs run: | + # Build documentation cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html + + # Auto-detect documentation directory + # Check if doc exists in target/doc or target/*/doc + if [ -d "target/doc" ]; then + DOC_DIR="target/doc" + else + # Find doc directory under target/*/doc pattern + DOC_DIR=$(find target -type d -name doc -path "target/*/doc" | head -n 1) + if [ -z "$DOC_DIR" ]; then + echo "Error: Could not find documentation directory" + exit 1 + fi + fi + + echo "Documentation found in: $DOC_DIR" + printf '' $(cargo tree | head -1 | cut -d' ' -f1) > "${DOC_DIR}/index.html" + echo "DOC_DIR=${DOC_DIR}" >> $GITHUB_ENV - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: target/doc + path: ${{ env.DOC_DIR }} deploy: name: Deploy to GitHub Pages From c5e7af4cd7ab303c085937f81490e524abdb9f86 Mon Sep 17 00:00:00 2001 From: TQ <128586861+YanLien@users.noreply.github.com> Date: Tue, 24 Feb 2026 13:11:21 +0800 Subject: [PATCH 057/132] chore: bump version to 0.2.2 (#53) * chore: update page_table_multiarch to 0.6 * chore: bump version to 0.2.2 --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0ab90779f..1eeb7e158 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "axvm" authors = ["aarkegz "] -version = "0.2.1" +version = "0.2.2" edition = "2024" categories = ["virtualization", "no-std"] description = "Virtual Machine resource management crate for ArceOS's hypervisor variant." @@ -24,8 +24,8 @@ axerrno = "0.2" cpumask = "0.1.0" # kspin = "0.1.0" memory_addr = "0.4" -page_table_entry = { version = "0.5", features = ["arm-el2"] } -page_table_multiarch = "0.5" +page_table_entry = { version = "0.6", features = ["arm-el2"] } +page_table_multiarch = "0.6" percpu = { version = "0.2.0", features = ["arm-el2"] } # System dependent modules provided by ArceOS-Hypervisor. From 3ef464a7f497ab95a161f6b02f18fc4850f6c325 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Thu, 5 Mar 2026 02:22:05 +0800 Subject: [PATCH 058/132] Use local path for merged lower-level components (axvm, axdevice, axvmconfig) --- Cargo.toml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 55d15dab4..b493c720b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,8 @@ axklib = {git = "https://github.com/arceos-hypervisor/axklib.git"} axruntime = {path = "modules/axruntime"} axfs = {path = "modules/axfs"} axvcpu = "0.1" -axvm = {git = "https://github.com/arceos-hypervisor/axvm.git", branch = "aar-master"} +# axvm = {git = "https://github.com/arceos-hypervisor/axvm.git", branch = "aar-next"} +axvm = { path = "../axvm" } # System independent crates provided by ArceOS, these crates could be imported by remote url. axerrno = "0.2" @@ -79,7 +80,8 @@ rdrive = "0.18" vm-fdt = {git = "https://github.com/bullhh/vm-fdt.git", default-features = false, features = ["alloc"]} -axdevice = {git = "https://github.com/arceos-hypervisor/axdevice.git", branch = "aar-master"} +# axdevice = {git = "https://github.com/arceos-hypervisor/axdevice.git", branch = "aar-next"} +axdevice = { path = "../axdevice" } axdevice_base = "0.1" axvisor_api = "0.2" driver = {path = "modules/driver"} @@ -87,7 +89,8 @@ driver = {path = "modules/driver"} # platform axplat-x86-qemu-q35 = {path = "platform/x86-qemu-q35"} axplat-riscv64-qemu-virt = {path = "platform/riscv64-qemu-virt"} -axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "aar-master"} +# axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "aar-next"} +axvmconfig = { path = "../axvmconfig" } [patch."https://github.com/arceos-org/arceos.git"] axconfig = { path = "modules/axconfig" } @@ -103,7 +106,8 @@ arm_vgic = { git = "https://github.com/arceos-hypervisor/arm_vgic", branch = "aa x86_vcpu = { git = "https://github.com/arceos-hypervisor/x86_vcpu", branch = "aar-master"} arm_vcpu = { git = "https://github.com/arceos-hypervisor/arm_vcpu", branch = "aar-master"} riscv_vcpu = { git = "https://github.com/arceos-hypervisor/riscv_vcpu", branch = "aar-master"} -axvmconfig = { git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "aar-master" } +# axvmconfig = { git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "aar-next" } +axvmconfig = { path = "../axvmconfig" } [patch."https://github.com/arceos-org/arceos"] axconfig = {path = "modules/axconfig"} From 305eb6345dd0830612c28f2253fcb7e2e03fd359 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Thu, 5 Mar 2026 20:17:52 +0800 Subject: [PATCH 059/132] Update axaddrspace to 0.2.0 and enhance IRQ handling, bump version to 0.3.0 (#10) * update axaddrspace to 0.2.0 * make `memory::PhysFrame` `Debug` * add `arch::fetch_irq` for aarch64 * add `arch::handle_irq` * formatted * update comment in the `vmm` mod * add `vmm::inject_interrupt_to_cpus` * bump versions for `axvisor_api`, `axvisor_api_proc`, `crate_interface`, and `axaddrspace` to 0.3.0 in Cargo.toml * docs: add comment for VCpuSet type in vmm.rs --- Cargo.toml | 11 +++++++---- axvisor_api_proc/Cargo.toml | 2 +- src/arch.rs | 12 ++++++++++++ src/memory.rs | 1 + src/vmm.rs | 16 ++++++++++++++-- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a7ae5fb81..6d09f46de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/arceos-hypervisor/axvisor_api" [package] name = "axvisor_api" -version = "0.2.0" +version = "0.3.0" description = "Basic API for components of the Hypervisor on ArceOS" documentation = "https://docs.rs/axvisor_api" readme = "README.md" @@ -24,15 +24,18 @@ repository.workspace = true [dependencies] # === Core Components === # Procedural macro definitions -axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0" } +axvisor_api_proc = { path = "axvisor_api_proc", version = "0.3.0"} # === Third-party Libraries === # Address space abstraction -axaddrspace = "0.1.0" +axaddrspace = "0.3" # Interface definition tools -crate_interface = "0.2" +crate_interface = "0.3" # Physical/virtual address types memory_addr = "0.4" +# CPU mask +cpumask = "0.1.0" + [package.metadata.docs.rs] all-features = true diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml index 2d05c2b6d..f6e233ff7 100644 --- a/axvisor_api_proc/Cargo.toml +++ b/axvisor_api_proc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "axvisor_api_proc" -version = "0.2.0" +version = "0.3.0" description = "Procedural macros for the `axvisor_api` crate" documentation = "https://docs.rs/axvisor_api_proc" readme = "README.md" diff --git a/src/arch.rs b/src/arch.rs index 4671b4eeb..977f63952 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -133,4 +133,16 @@ pub trait ArchIf { /// later. #[cfg(target_arch = "aarch64")] fn get_host_gicr_base() -> PhysAddr; + + /// Retrieve the current pending interrupt for the CPU from the physical hardware. + #[cfg(target_arch = "aarch64")] + fn fetch_irq() -> u64; + /// Calls the IRQ handler of the underlying OS to handle a pending interrupt. + /// + /// TODO: Determine if this function should be exposed in other architectures (and moved to + /// `host` module) or remain architecture-specific. + /// + /// TODO: Consider whether this function should replace `AxVCpuExitReason::ExternalInterrupt`. + #[cfg(target_arch = "aarch64")] + fn handle_irq(); } diff --git a/src/memory.rs b/src/memory.rs index 49f603311..941807c07 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -170,6 +170,7 @@ pub trait MemoryIf { /// This struct provides an implementation of the `AxMmHal` trait from the /// `axaddrspace` crate, delegating to the axvisor_api memory functions. #[doc(hidden)] +#[derive(Debug)] pub struct AxMmHalApiImpl; impl axaddrspace::AxMmHal for AxMmHalApiImpl { diff --git a/src/vmm.rs b/src/vmm.rs index 722f1d53b..8de149c15 100644 --- a/src/vmm.rs +++ b/src/vmm.rs @@ -71,6 +71,12 @@ pub type VCpuId = usize; /// Represents the interrupt vector number to be injected into a guest. pub type InterruptVector = u8; +/// The maximum number of virtual CPUs supported in a virtual machine. +pub const MAX_VCPU_NUM: usize = 64; + +/// A set of virtual CPUs. +pub type VCpuSet = cpumask::CpuMask; + /// The API trait for virtual machine management functionalities. /// /// This trait defines the core VM management interface required by the @@ -78,6 +84,7 @@ pub type InterruptVector = u8; /// layer. #[crate::api_def] pub trait VmmIf { + /// Notify that a virtual CPU timer has expired. /// Get the identifier of the current virtual machine. /// /// This function returns the VM ID of the VM that the calling context @@ -145,6 +152,9 @@ pub trait VmmIf { /// ``` fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector); + /// Inject an interrupt to a set of virtual CPUs. + fn inject_interrupt_to_cpus(vm_id: VMId, vcpu_set: VCpuSet, vector: InterruptVector); + /// Notify that a virtual CPU's timer has expired. /// /// This function is called when a vCPU's virtual timer expires and needs @@ -162,7 +172,8 @@ pub trait VmmIf { fn notify_vcpu_timer_expired(vm_id: VMId, vcpu_id: VCpuId); } -/// Get the number of virtual CPUs in the current virtual machine. +/// Get the number of virtual CPUs in the current virtual machine executing on +/// the current physical CPU. /// /// This is a convenience function that combines [`current_vm_id`] and /// [`vcpu_num`]. @@ -179,7 +190,8 @@ pub fn current_vm_vcpu_num() -> usize { vcpu_num(current_vm_id()).unwrap() } -/// Get the bitmask of active virtual CPUs in the current virtual machine. +/// Get the bitmask of active virtual CPUs in the current virtual machine +/// executing on the current physical CPU. /// /// This is a convenience function that combines [`current_vm_id`] and /// [`active_vcpus`]. From 2338c6f2817d9ab2e14aa999569601f7ab2a990a Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Thu, 5 Mar 2026 20:28:10 +0800 Subject: [PATCH 060/132] Update dependencies, remove hal, add `max_guest_page_table_levels` api, bump to v0.3.0 (#46) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add gpt level api * update dependencies * remove hal * update axaddrspace and axvisor_api dependencies to version 0.3 --------- Co-authored-by: 周睿 --- Cargo.toml | 8 ++++---- src/hal.rs | 46 ---------------------------------------------- src/lib.rs | 2 -- 3 files changed, 4 insertions(+), 52 deletions(-) delete mode 100644 src/hal.rs diff --git a/Cargo.toml b/Cargo.toml index 22edc446c..bf5ca3677 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "axvcpu" authors = ["aarkegz "] -version = "0.2.1" +version = "0.3.0" edition = "2024" categories = ["virtualization", "no-std"] description = "Virtual CPU abstraction for ArceOS hypervisor" @@ -10,8 +10,8 @@ keywords = ["vcpu", "hypervisor", "arceos"] license = "Apache-2.0" [dependencies] -axerrno = "0.1.0" +axerrno = "0.2" memory_addr = "0.4" percpu = "0.2.0" -axaddrspace = "0.1.4" -axvisor_api = "0.1" +axaddrspace = "0.3" +axvisor_api = "0.3" diff --git a/src/hal.rs b/src/hal.rs deleted file mode 100644 index 822614997..000000000 --- a/src/hal.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// Hardware abstraction layer interfaces for VCpu operations. -/// -/// This trait defines the interfaces that the underlying software (kernel or hypervisor) -/// must implement to support VCpu operations such as interrupt handling and memory management. -pub trait AxVCpuHal { - /// Memory management interfaces required by the VCpu subsystem. - /// Must implement the AxMmHal trait from the axaddrspace crate. - type MmHal: axaddrspace::AxMmHal; - - /// Fetches the current interrupt (IRQ) number from the hardware. - fn irq_fetch() -> usize { - 0 - } - - /// Dispatches an interrupt request (IRQ) to the underlying host OS. - /// - /// This function should handle the actual interrupt processing and delegation - /// to the appropriate interrupt handler in the host system. - /// - /// # Implementation Required - /// - /// The default implementation panics as this function **must** be implemented - /// by the underlying hypervisor or kernel to provide proper interrupt handling. - /// - /// # Safety - /// - /// Implementations should ensure proper interrupt handling and avoid - /// reentrancy issues during interrupt processing. - fn irq_hanlder() { - unimplemented!("irq_handler is not implemented"); - } -} diff --git a/src/lib.rs b/src/lib.rs index 38282ac27..cddca862f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,6 @@ extern crate alloc; // Core modules mod arch_vcpu; // Architecture-specific VCpu trait definition mod exit; // VM exit reason enumeration and handling -mod hal; // Hardware abstraction layer interfaces mod percpu; // Per-CPU virtualization state management mod test; // Unit tests for VCpu functionality mod vcpu; // Main VCpu implementation and state management @@ -43,6 +42,5 @@ mod vcpu; // Main VCpu implementation and state management // Public API exports pub use arch_vcpu::AxArchVCpu; // Architecture-specific VCpu trait pub use exit::AxVCpuExitReason; -pub use hal::AxVCpuHal; // Hardware abstraction layer trait pub use percpu::*; // Per-CPU state management types pub use vcpu::*; // Main VCpu types and functions // VM exit reasons From 49da0a22d96172469efc54d635d4a624c521fb3e Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Fri, 6 Mar 2026 00:32:43 +0800 Subject: [PATCH 061/132] Update dependencies, bump to v0.2.2 (#50) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 格式化 Cargo.toml 文件,调整字段顺序以提高可读性 * update dependencies * update axaddrspace to 0.3 and axvmconfig to 0.2, remove obsolete patch section. * clean unused `cfg-if` * bump to 0.2.2 * reset serde dependency version to 1.0 --------- Co-authored-by: 周睿 --- Cargo.toml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ddbb66642..39b5f8575 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "axdevice_base" -version = "0.2.1" +version = "0.2.2" edition = "2024" authors = ["Hui Xiao <1127751018@qq.com>"] description = "Basic traits and structures for emulated devices in ArceOS hypervisor." @@ -12,14 +12,12 @@ keywords = ["arceos", "hypervisor", "virtualization", "device", "no-std"] categories = ["no-std", "virtualization"] [dependencies] -# Serialization support serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } -# ArceOS core crates -axaddrspace = "0.1.4" -axerrno = "0.1" -axvmconfig = { version = "0.2", default-features = false } -memory_addr = "0.4" +# ArceOS and AxVisor crates. +axaddrspace = "0.3" +axerrno = "0.2" +axvmconfig = {version = "0.2", default-features = false} [dev-dependencies] From 536f610047353b01dae73e7309a67e2e51dfeb74 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Fri, 6 Mar 2026 10:52:16 +0800 Subject: [PATCH 062/132] Update dependencies and bump version to v0.2.2 (#12) * update dependencies * chore: update dependency versions in Cargo.toml * bump to v0.2.2 --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d3bd6c434..4fb7597c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x86_vlapic" -version = "0.2.1" +version = "0.2.2" edition = "2024" description = "x86 Virtual Local APIC" authors = ["Keyang Hu ", "Mingxian Su "] @@ -16,8 +16,8 @@ tock-registers = "0.10.0" bit = "0.1.1" memory_addr = "0.4" -axerrno = "0.1.0" +axerrno = "0.2" -axaddrspace = "0.1.4" -axdevice_base = "0.2.1" -axvisor_api = "0.1.0" +axaddrspace = "0.3" +axdevice_base = "0.2.2" +axvisor_api = "0.3" From 42c1fd4fd805e997ef34c857b3e3a2ac0d7e62f3 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Fri, 6 Mar 2026 10:52:24 +0800 Subject: [PATCH 063/132] Update dependencies and bump version to v0.2.2 (#2) * update dependencies * bump to v0.2.2 * update `riscv-h` --- Cargo.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 81219eadd..b7ed2ffb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "riscv_vplic" -version = "0.2.1" +version = "0.2.2" edition = "2021" authors = ["Jingyu liu "] description = "RISCV Virtual PLIC implementation." @@ -15,16 +15,16 @@ license = "Apache-2.0" default = [] [dependencies] -axaddrspace = "0.1.4" -axdevice_base = "0.2.1" -axvisor_api = "0.1" +axaddrspace = "0.3" +axdevice_base = "0.2.2" +axvisor_api = "0.3" -axerrno = "0.1.0" +axerrno = "0.2" bitmaps = { version = "3.2", default-features = false } log = "0.4" -spin = "0.9" +spin = "0.10" -riscv-h = "0.1" +riscv-h = "0.2" [package.metadata.docs.rs] default-target = ["riscv64gc-unknown-none-elf"] From 181b25b57197fb802317618edec484f56e99ffdf Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Fri, 6 Mar 2026 10:52:51 +0800 Subject: [PATCH 064/132] Update dependencies and bump version to v0.2.2 (#27) * update dependencies * bump to v0.2.2 --- Cargo.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 34430b4d0..5710e7955 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -version = "0.2.1" +version = "0.2.2" authors = [ "Mingxian Su ", "DeBin Luo ", @@ -19,15 +19,15 @@ default = [] vgicv3 = [] [dependencies] -axaddrspace = "0.1.4" -axdevice_base = "0.2.1" -axvisor_api = "0.1" +axaddrspace = "0.3" +axdevice_base = "0.2.2" +axvisor_api = "0.3" -aarch64-cpu = "10.0" +aarch64-cpu = "11.0" aarch64_sysreg = "0.1.1" -axerrno = "0.1.0" +axerrno = "0.2" bitmaps = {version = "3.2", default-features = false} log = "0.4" memory_addr = "0.4" -spin = "0.9" +spin = "0.10" tock-registers = "0.10" From 6a5724a4f9a8cd4a863ac3362f706fc7df84da64 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Fri, 6 Mar 2026 11:27:05 +0800 Subject: [PATCH 065/132] Adapt to new dependencies, remove hal, bump version to v0.3.0 (#37) * remove all type parameters * update dependencies and bump to v0.3.0 --- Cargo.toml | 18 ++++++++-------- src/test_utils.rs | 51 +++++++++++++++++++++++++++------------------- src/vmx/percpu.rs | 20 +++++++++--------- src/vmx/structs.rs | 45 ++++++++++++++++++++-------------------- src/vmx/vcpu.rs | 24 +++++++++++----------- 5 files changed, 84 insertions(+), 74 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 93cd1ede5..07a59a54c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x86_vcpu" -version = "0.2.2" +version = "0.3.0" edition = "2024" authors = [ "Mingxian Su ", @@ -23,18 +23,18 @@ x86_64 = "0.15" raw-cpuid = "11.0" numeric-enum-macro = "0.2" -axerrno = "0.1.0" +axerrno = "0.2.0" page_table_entry = "0.6" memory_addr = "0.4" -crate_interface = "0.1" +crate_interface = "0.3" -axaddrspace = "0.1.4" -axvcpu = "0.2" -x86_vlapic = "0.2.1" -axdevice_base = "0.2.1" -axvisor_api = "0.1.0" +axaddrspace = "0.3" +axvcpu = "0.3" +x86_vlapic = "0.2.2" +axdevice_base = "0.2.2" +axvisor_api = "0.3" -spin = { version = "0.9", default-features = false } +spin = { version = "0.10", default-features = false } [features] default = ["vmx"] diff --git a/src/test_utils.rs b/src/test_utils.rs index 237b469a3..2ca3fa5cb 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -14,12 +14,10 @@ #[cfg(test)] pub mod mock { - use axaddrspace::AxMmHal; + use axvisor_api::{api_impl, memory::MemoryIf}; + use memory_addr::{PhysAddr, VirtAddr}; use spin::Mutex; - #[derive(Debug)] - pub struct MockMmHal; - static GLOBAL_LOCK: Mutex = Mutex::new(MockMmHalState::new()); // State for the mock memory allocator @@ -40,9 +38,13 @@ pub mod mock { } } - impl AxMmHal for MockMmHal { - // Allocate a frame of memory - fn alloc_frame() -> Option { + #[derive(Debug)] + pub struct MockMmHal; + + #[api_impl] + impl MemoryIf for MockMmHal { + /// Allocate a frame. + fn alloc_frame() -> Option { let mut state = GLOBAL_LOCK.lock(); for i in 0..16 { @@ -56,8 +58,16 @@ pub mod mock { None } - // Deallocate a frame - fn dealloc_frame(paddr: memory_addr::PhysAddr) { + /// Allocate a number of contiguous frames, with a specified alignment. + fn alloc_contiguous_frames( + _num_frames: usize, + _frame_align_pow2: usize, + ) -> Option { + unimplemented!() + } + + /// Deallocate a frame allocated previously by [`alloc_frame`]. + fn dealloc_frame(paddr: PhysAddr) { let mut state = GLOBAL_LOCK.lock(); let addr = paddr.as_usize(); @@ -68,8 +78,14 @@ pub mod mock { } } - // Convert physical address to virtual address - fn phys_to_virt(paddr: memory_addr::PhysAddr) -> memory_addr::VirtAddr { + /// Deallocate a number of contiguous frames allocated previously by + /// [`alloc_contiguous_frames`]. + fn dealloc_contiguous_frames(_first_addr: PhysAddr, _num_frames: usize) { + unimplemented!() + } + + /// Convert a physical address to a virtual address. + fn phys_to_virt(paddr: PhysAddr) -> VirtAddr { let state = GLOBAL_LOCK.lock(); let addr = paddr.as_usize(); @@ -84,8 +100,8 @@ pub mod mock { } } - // Convert virtual address to physical address - fn virt_to_phys(vaddr: memory_addr::VirtAddr) -> memory_addr::PhysAddr { + /// Convert a virtual address to a physical address. + fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr { let state = GLOBAL_LOCK.lock(); let pool_start = state.memory_pool.as_ptr() as usize; @@ -139,19 +155,12 @@ pub mod mock { state.reset_counter } } - - #[derive(Debug)] - pub struct MockVCpuHal; - - impl axvcpu::AxVCpuHal for MockVCpuHal { - type MmHal = MockMmHal; - } } #[cfg(test)] mod tests { use crate::test_utils::mock::MockMmHal; - use axaddrspace::AxMmHal; + use axvisor_api::memory::MemoryIf; #[test] fn test_mock_allocator() { diff --git a/src/vmx/percpu.rs b/src/vmx/percpu.rs index f382fe358..0c290d4b7 100644 --- a/src/vmx/percpu.rs +++ b/src/vmx/percpu.rs @@ -16,7 +16,7 @@ use x86::bits64::vmx; use x86_64::registers::control::{Cr0, Cr4, Cr4Flags}; use axerrno::{AxResult, ax_err, ax_err_type}; -use axvcpu::{AxArchPerCpu, AxVCpuHal}; +use axvcpu::AxArchPerCpu; use memory_addr::PAGE_SIZE_4K as PAGE_SIZE; use crate::msr::Msr; @@ -29,7 +29,7 @@ use crate::vmx::structs::{FeatureControl, FeatureControlFlags, VmxBasic, VmxRegi /// when operating in VMX mode, including the VMCS revision identifier and /// the VMX region. #[derive(Debug)] -pub struct VmxPerCpuState { +pub struct VmxPerCpuState { /// The VMCS (Virtual Machine Control Structure) revision identifier. /// /// This identifier is used to ensure compatibility between the software @@ -40,10 +40,10 @@ pub struct VmxPerCpuState { /// /// This region typically contains the VMCS and other state information /// required for managing virtual machines on this particular CPU. - vmx_region: VmxRegion, + vmx_region: VmxRegion, } -impl AxArchPerCpu for VmxPerCpuState { +impl AxArchPerCpu for VmxPerCpuState { fn new(_cpu_id: usize) -> AxResult { Ok(Self { vmcs_revision_id: 0, @@ -159,14 +159,14 @@ impl AxArchPerCpu for VmxPerCpuState { #[cfg(test)] mod tests { use super::*; - use crate::test_utils::mock::{MockMmHal, MockVCpuHal}; + use crate::test_utils::mock::MockMmHal; use alloc::format; use alloc::vec::Vec; #[test] fn test_vmx_per_cpu_state_new() { MockMmHal::reset(); // Reset before test - let result = VmxPerCpuState::::new(0); + let result = VmxPerCpuState::new(0); assert!(result.is_ok()); let state = result.unwrap(); @@ -176,7 +176,7 @@ mod tests { #[test] fn test_vmx_per_cpu_state_default_values() { MockMmHal::reset(); // Reset before test - let state = VmxPerCpuState::::new(0).unwrap(); + let state = VmxPerCpuState::new(0).unwrap(); // Test that vmcs_revision_id is initialized to 0 assert_eq!(state.vmcs_revision_id, 0); @@ -193,7 +193,7 @@ mod tests { // Create states for multiple CPUs for cpu_id in 0..4 { - let state = VmxPerCpuState::::new(cpu_id).unwrap(); + let state = VmxPerCpuState::new(cpu_id).unwrap(); states.push(state); } @@ -211,7 +211,7 @@ mod tests { #[test] fn test_vmx_per_cpu_state_debug() { MockMmHal::reset(); // Reset before test - let state = VmxPerCpuState::::new(0).unwrap(); + let state = VmxPerCpuState::new(0).unwrap(); // Test that Debug trait is implemented and doesn't panic let debug_str = format!("{:?}", state); @@ -223,7 +223,7 @@ mod tests { use core::mem; // Test that the struct has a reasonable size - let size = mem::size_of::>(); + let size = mem::size_of::(); // Should be larger than just the u32 field due to the VmxRegion assert!(size > 4); diff --git a/src/vmx/structs.rs b/src/vmx/structs.rs index fef13f968..d949d2429 100644 --- a/src/vmx/structs.rs +++ b/src/vmx/structs.rs @@ -17,18 +17,19 @@ use bitflags::bitflags; use memory_addr::PAGE_SIZE_4K as PAGE_SIZE; -use axaddrspace::{AxMmHal, HostPhysAddr, PhysFrame}; +use axaddrspace::HostPhysAddr; use axerrno::AxResult; +use axvisor_api::memory::PhysFrame; use crate::msr::{Msr, MsrReadWrite}; /// VMCS/VMXON region in 4K size. (SDM Vol. 3C, Section 24.2) #[derive(Debug)] -pub struct VmxRegion { - frame: PhysFrame, +pub struct VmxRegion { + frame: PhysFrame, } -impl VmxRegion { +impl VmxRegion { pub const unsafe fn uninit() -> Self { Self { frame: unsafe { PhysFrame::uninit() }, @@ -55,12 +56,12 @@ impl VmxRegion { // I/O bitmap A contains one bit for each I/O port in the range 0000H through 7FFFH; // I/O bitmap B contains bits for ports in the range 8000H through FFFFH. #[derive(Debug)] -pub struct IOBitmap { - io_bitmap_a_frame: PhysFrame, - io_bitmap_b_frame: PhysFrame, +pub struct IOBitmap { + io_bitmap_a_frame: PhysFrame, + io_bitmap_b_frame: PhysFrame, } -impl IOBitmap { +impl IOBitmap { pub fn passthrough_all() -> AxResult { Ok(Self { io_bitmap_a_frame: PhysFrame::alloc_zero()?, @@ -115,11 +116,11 @@ impl IOBitmap { } #[derive(Debug)] -pub struct MsrBitmap { - frame: PhysFrame, +pub struct MsrBitmap { + frame: PhysFrame, } -impl MsrBitmap { +impl MsrBitmap { pub fn passthrough_all() -> AxResult { Ok(Self { frame: PhysFrame::alloc_zero()?, @@ -291,7 +292,7 @@ mod tests { #[test] fn test_vmx_region_uninit() { - let region = unsafe { VmxRegion::::uninit() }; + let region = unsafe { VmxRegion::uninit() }; // Test that we can create an uninitialized region // Can't test much more without allocating memory @@ -305,7 +306,7 @@ mod tests { MockMmHal::reset(); // Test VmxRegion::new with valid parameters - let region = VmxRegion::::new(0x12345, false); + let region = VmxRegion::new(0x12345, false); assert!(region.is_ok()); let region = region.unwrap(); @@ -321,10 +322,10 @@ mod tests { MockMmHal::reset(); // Test VmxRegion::new with different shadow indicator values - let region_no_shadow = VmxRegion::::new(0x12345, false); + let region_no_shadow = VmxRegion::new(0x12345, false); assert!(region_no_shadow.is_ok()); - let region_with_shadow = VmxRegion::::new(0x12345, true); + let region_with_shadow = VmxRegion::new(0x12345, true); assert!(region_with_shadow.is_ok()); // Test that both regions have valid physical addresses @@ -347,11 +348,11 @@ mod tests { MockMmHal::reset(); // Test passthrough_all creation - let passthrough_bitmap = IOBitmap::::passthrough_all(); + let passthrough_bitmap = IOBitmap::passthrough_all(); assert!(passthrough_bitmap.is_ok()); // Test intercept_all creation - let intercept_bitmap = IOBitmap::::intercept_all(); + let intercept_bitmap = IOBitmap::intercept_all(); assert!(intercept_bitmap.is_ok()); // Test that phys_addr returns valid addresses @@ -368,11 +369,11 @@ mod tests { MockMmHal::reset(); // Test passthrough_all creation - let passthrough_bitmap = MsrBitmap::::passthrough_all(); + let passthrough_bitmap = MsrBitmap::passthrough_all(); assert!(passthrough_bitmap.is_ok()); // Test intercept_all creation - let intercept_bitmap = MsrBitmap::::intercept_all(); + let intercept_bitmap = MsrBitmap::intercept_all(); assert!(intercept_bitmap.is_ok()); // Test that phys_addr returns valid addresses @@ -466,13 +467,13 @@ mod tests { #[test] fn test_debug_implementations() { // Test that all our structs implement Debug properly - let vmx_region = unsafe { VmxRegion::::uninit() }; + let vmx_region = unsafe { VmxRegion::uninit() }; let _debug_str = format!("{:?}", vmx_region); - let io_bitmap = IOBitmap::::passthrough_all().unwrap(); + let io_bitmap = IOBitmap::passthrough_all().unwrap(); let _debug_str = format!("{:?}", io_bitmap); - let msr_bitmap = MsrBitmap::::passthrough_all().unwrap(); + let msr_bitmap = MsrBitmap::passthrough_all().unwrap(); let _debug_str = format!("{:?}", msr_bitmap); let flags = FeatureControlFlags::LOCKED; diff --git a/src/vmx/vcpu.rs b/src/vmx/vcpu.rs index bff610096..531600c03 100644 --- a/src/vmx/vcpu.rs +++ b/src/vmx/vcpu.rs @@ -35,7 +35,7 @@ use axaddrspace::{ }; use axdevice_base::BaseDeviceOps; use axerrno::{AxResult, ax_err, ax_err_type}; -use axvcpu::{AxArchVCpu, AxVCpuExitReason, AxVCpuHal}; +use axvcpu::{AxArchVCpu, AxVCpuExitReason}; use axvisor_api::vmm::{VCpuId, VMId}; use super::VmxExitInfo; @@ -167,7 +167,7 @@ const CR0_PE: usize = 1 << 0; /// A virtual CPU within a guest. #[repr(C)] -pub struct VmxVcpu { +pub struct VmxVcpu { // The order of `guest_regs` and `host_stack_top` is mandatory. They must be the first two fields. If you want to // change the order or the type of these fields, you must also change the assembly in this file. /// Guest general-purpose registers. @@ -189,11 +189,11 @@ pub struct VmxVcpu { // VMCS-related fields /// The VMCS region. - vmcs: VmxRegion, + vmcs: VmxRegion, /// The I/O bitmap for the VMCS. - io_bitmap: IOBitmap, + io_bitmap: IOBitmap, /// The MSR bitmap for the VMCS. - msr_bitmap: MsrBitmap, + msr_bitmap: MsrBitmap, // Interrupt-related fields /// Pending events to be injected to the guest. @@ -211,7 +211,7 @@ pub struct VmxVcpu { guest_regs_exiting: GeneralRegisters, } -impl VmxVcpu { +impl VmxVcpu { /// Create a new [`VmxVcpu`]. pub fn new(vm_id: VMId, vcpu_id: VCpuId) -> AxResult { let vmcs_revision_id = super::read_vmcs_revision_id(); @@ -509,7 +509,7 @@ impl VmxVcpu { } // Implementation of private methods -impl VmxVcpu { +impl VmxVcpu { fn setup_io_bitmap(&mut self) -> AxResult { // By default, I/O bitmap is set as `intercept_all`. // Todo: these should be combined with emulated pio device management, @@ -797,7 +797,7 @@ impl VmxVcpu { // Implementaton for type1.5 hypervisor // #[cfg(feature = "type1_5")] -impl VmxVcpu { +impl VmxVcpu { fn set_cr(&mut self, cr_idx: usize, val: u64) { (|| -> AxResult { // debug!("set guest CR{} to val {:#x}", cr_idx, val); @@ -871,7 +871,7 @@ macro_rules! vmx_entry_with { } } -impl VmxVcpu { +impl VmxVcpu { #[unsafe(naked)] /// Enter guest with vmlaunch. /// @@ -1230,7 +1230,7 @@ impl VmxVcpu { } } -impl Drop for VmxVcpu { +impl Drop for VmxVcpu { fn drop(&mut self) { unsafe { vmx::vmclear(self.vmcs.phys_addr().as_usize() as u64).unwrap() }; info!("[HV] dropped VmxVcpu(vmcs: {:#x})", self.vmcs.phys_addr()); @@ -1253,7 +1253,7 @@ fn get_tr_base(tr: SegmentSelector, gdt: &DescriptorTablePointer) -> u64 { } } -impl Debug for VmxVcpu { +impl Debug for VmxVcpu { fn fmt(&self, f: &mut Formatter) -> Result { (|| -> AxResult { Ok(f.debug_struct("VmxVcpu") @@ -1274,7 +1274,7 @@ impl Debug for VmxVcpu { } } -impl AxArchVCpu for VmxVcpu { +impl AxArchVCpu for VmxVcpu { type CreateConfig = (); type SetupConfig = (); From 1de4e603a1a9d3fb3fbf7e191d95c4ad84d0ce39 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Fri, 6 Mar 2026 11:30:49 +0800 Subject: [PATCH 066/132] Adapt to new dependencies, remove hal, bump version to v0.3.0 (#55) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: irq settings refactor: add page level detect and remove feature * fix: ci * feat: add max gpt level api * fmt code * fix: change function signature of run_guest to use "C" calling convention * fix: specify calling convention for external function declaration * fix: specify "C" calling convention for vmexit_trampoline function * fix: add target architecture configuration for aarch64 * fix: remove "C" calling convention specification for vmexit_trampoline and run_guest functions * fix: update axaddrspace dependency version to 0.2 and format axvcpu declaration * fix: revert axvcpu dependency to version 0.1.0 * update dependencies * remove `ORI_EXCEPTION_VECTOR_BASE` * remove type parameters * formatted * fix clippy by removing unused imports * bump version * fix errors and warnings caused by merging --------- Co-authored-by: 周睿 --- Cargo.toml | 24 ++++++++++++------------ src/exception.rs | 8 ++++---- src/lib.rs | 1 + src/pcpu.rs | 39 +++++++++++++++------------------------ src/vcpu.rs | 16 ++++++---------- 5 files changed, 38 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1d0b190f1..1dc74b4f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [package] edition = "2024" name = "arm_vcpu" -version = "0.2.1" +version = "0.3.0" authors = [ - "KeYang Hu ", - "Mingxian Su ", - "ShiMei Tang ", - "DeBin Luo ", - "周睿 " + "KeYang Hu ", + "Mingxian Su ", + "ShiMei Tang ", + "DeBin Luo ", + "周睿 ", ] description = "Aarch64 VCPU implementation for Arceos Hypervisor" license = "Apache-2.0" @@ -19,13 +19,13 @@ keywords = ["hypervisor", "aarch64", "vcpu"] log = "0.4" spin = "0.10" -aarch64-cpu = "10.0" +aarch64-cpu = "11.0" numeric-enum-macro = "0.2" -axerrno = "0.1.0" +axerrno = "0.2.0" percpu = {version = "0.2.0", features = ["arm-el2"]} -axaddrspace = "0.1.4" -axdevice_base = "0.2.1" -axvcpu = "0.2" -axvisor_api = "0.1.0" +axaddrspace = "0.3" +axdevice_base = "0.2.2" +axvcpu = "0.3" +axvisor_api = "0.3" diff --git a/src/exception.rs b/src/exception.rs index 41c127ca9..de8d280ef 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -292,12 +292,12 @@ fn handle_smc64_exception(ctx: &mut TrapFrame) -> AxResult { /// Handles IRQ exceptions that occur from the current exception level. /// Dispatches IRQs to the appropriate handler provided by the underlying host OS, -/// which is registered at [`crate::pcpu::IRQ_HANDLER`] during `Aarch64PerCpu::new()`. +/// which is provided by `axvisor_api::arch::handle_irq()`. #[unsafe(no_mangle)] fn current_el_irq_handler(_tf: &mut TrapFrame) { - unsafe { crate::pcpu::IRQ_HANDLER.current_ref_raw() } - .get() - .unwrap()() + // TODO: consider if returning AxVCpuExitReason::ExternalInterrupt (or another enum variant) is + // better than directly calling the handler here. + axvisor_api::arch::handle_irq() } /// Handles synchronous exceptions that occur from the current exception level. diff --git a/src/lib.rs b/src/lib.rs index 2b63c1af4..0ed6bed82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ // limitations under the License. #![no_std] +#![cfg(target_arch = "aarch64")] #![feature(doc_cfg)] #![doc = include_str!("../README.md")] diff --git a/src/pcpu.rs b/src/pcpu.rs index ece307635..e1e2c3f26 100644 --- a/src/pcpu.rs +++ b/src/pcpu.rs @@ -12,45 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. -use core::{cell::OnceCell, marker::PhantomData}; +use core::mem; use aarch64_cpu::registers::*; use axerrno::AxResult; -use axvcpu::{AxArchPerCpu, AxVCpuHal}; +use axvcpu::AxArchPerCpu; /// Per-CPU data. A pointer to this struct is loaded into TP when a CPU starts. This structure #[repr(C)] #[repr(align(4096))] -pub struct Aarch64PerCpu { +pub struct Aarch64PerCpu { /// per cpu id pub cpu_id: usize, - _phantom: PhantomData, + /// The original value of `VBAR_EL2` (exception vector base) before enabling + /// the virtualization. + pub original_vbar_el2: u64, } -#[percpu::def_percpu] -static ORI_EXCEPTION_VECTOR_BASE: usize = 0; - -/// IRQ handler registered by underlying host OS during per-cpu initialization, -/// for dispatching IRQs to the host OS. -/// -/// Set `IRQ_HANDLER` as per-cpu variable to avoid the need of `OnceLock`. -#[percpu::def_percpu] -pub static IRQ_HANDLER: OnceCell<&(dyn Fn() + Send + Sync)> = OnceCell::new(); - unsafe extern "C" { fn exception_vector_base_vcpu(); } -impl AxArchPerCpu for Aarch64PerCpu { +impl AxArchPerCpu for Aarch64PerCpu { fn new(cpu_id: usize) -> AxResult { - // Register IRQ handler for this CPU. - let _ = unsafe { IRQ_HANDLER.current_ref_mut_raw() } - .set(&|| H::irq_hanlder()) - .map(|_| {}); - Ok(Self { cpu_id, - _phantom: PhantomData, + original_vbar_el2: 0, }) } @@ -62,11 +49,11 @@ impl AxArchPerCpu for Aarch64PerCpu { // First we save origin `exception_vector_base`. // Safety: // Todo: take care of `preemption` - unsafe { ORI_EXCEPTION_VECTOR_BASE.write_current_raw(VBAR_EL2.get() as usize) } + self.original_vbar_el2 = VBAR_EL2.get(); // Set current `VBAR_EL2` to `exception_vector_base_vcpu` // defined in this crate. - VBAR_EL2.set(exception_vector_base_vcpu as usize as _); + VBAR_EL2.set(exception_vector_base_vcpu as *const () as usize as _); HCR_EL2.modify( HCR_EL2::VM::Enable + HCR_EL2::RW::EL1IsAarch64 + HCR_EL2::TSC::EnableTrapEl1SmcToEl2, @@ -92,9 +79,13 @@ impl AxArchPerCpu for Aarch64PerCpu { // Reset `VBAR_EL2` into previous value. // Safety: // Todo: take care of `preemption` - VBAR_EL2.set(unsafe { ORI_EXCEPTION_VECTOR_BASE.read_current_raw() } as _); + VBAR_EL2.set(mem::take(&mut self.original_vbar_el2)); HCR_EL2.set(HCR_EL2::VM::Disable.into()); Ok(()) } + + fn max_guest_page_table_levels(&self) -> usize { + crate::vcpu::max_gpt_level(crate::vcpu::pa_bits()) + } } diff --git a/src/vcpu.rs b/src/vcpu.rs index 4abefae30..895b70216 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -12,12 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use core::marker::PhantomData; - use aarch64_cpu::registers::*; use axaddrspace::{GuestPhysAddr, HostPhysAddr, device::SysRegAddr}; use axerrno::AxResult; -use axvcpu::{AxArchVCpu, AxVCpuExitReason, AxVCpuHal}; +use axvcpu::{AxArchVCpu, AxVCpuExitReason}; use crate::TrapFrame; use crate::context_frame::GuestSystemRegisters; @@ -51,7 +49,7 @@ pub struct VmCpuRegisters { /// A virtual CPU within a guest #[repr(C)] #[derive(Debug)] -pub struct Aarch64VCpu { +pub struct Aarch64VCpu { // DO NOT modify `guest_regs` and `host_stack_top` and their order unless you do know what you are doing! // DO NOT add anything before or between them unless you do know what you are doing! ctx: TrapFrame, @@ -59,7 +57,6 @@ pub struct Aarch64VCpu { guest_system_regs: GuestSystemRegisters, /// The MPIDR_EL1 value for the vCPU. mpidr: u64, - _phantom: PhantomData, } /// Configuration for creating a new `Aarch64VCpu` @@ -83,7 +80,7 @@ pub struct Aarch64VCpuSetupConfig { pub passthrough_timer: bool, } -impl axvcpu::AxArchVCpu for Aarch64VCpu { +impl axvcpu::AxArchVCpu for Aarch64VCpu { type CreateConfig = Aarch64VCpuCreateConfig; type SetupConfig = Aarch64VCpuSetupConfig; @@ -97,7 +94,6 @@ impl axvcpu::AxArchVCpu for Aarch64VCpu { host_stack_top: 0, guest_system_regs: GuestSystemRegisters::default(), mpidr: config.mpidr_el1, - _phantom: PhantomData, }) } @@ -156,7 +152,7 @@ impl axvcpu::AxArchVCpu for Aarch64VCpu { } // Private function -impl Aarch64VCpu { +impl Aarch64VCpu { fn init_hv(&mut self, config: Aarch64VCpuSetupConfig) { self.ctx.spsr = (SPSR_EL1::M::EL1h + SPSR_EL1::I::Masked @@ -223,7 +219,7 @@ impl Aarch64VCpu { } /// Private functions related to vcpu runtime control flow. -impl Aarch64VCpu { +impl Aarch64VCpu { /// Save host context and run guest. /// /// When a VM-Exit happens when guest's vCpu is running, @@ -318,7 +314,7 @@ impl Aarch64VCpu { let result = match exit_reason { TrapKind::Synchronous => handle_exception_sync(&mut self.ctx), TrapKind::Irq => Ok(AxVCpuExitReason::ExternalInterrupt { - vector: H::irq_fetch() as _, + vector: axvisor_api::arch::fetch_irq(), }), _ => panic!("Unhandled exception {:?}", exit_reason), }; From 2a4afa7c4d67570e046a037229bae3a2fc1115cc Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Fri, 6 Mar 2026 11:30:54 +0800 Subject: [PATCH 067/132] Adapt to new dependencies, remove hal, bump version to v0.3.0 (#31) * update dependencies * remove type parameters * bump version to v0.3.0 * remove unused import --- Cargo.toml | 14 +++++++------- src/percpu.rs | 14 ++++---------- src/vcpu.rs | 14 ++++++-------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eb59152f5..6ffefc52a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "riscv_vcpu" -version = "0.2.2" +version = "0.3.0" edition = "2024" authors = [ "KeYang Hu ", @@ -23,7 +23,7 @@ log = "0.4" cfg-if = "1.0" bitflags = "2.2" bit_field = "0.10" -crate_interface = "0.1" +crate_interface = "0.3" riscv = { version = "0.14.0", features = ["s-mode"] } riscv-h = "0.2" @@ -32,13 +32,13 @@ riscv-decode = "0.2.3" rustsbi = { version = "0.4.0", features = ["forward"] } sbi-rt = { version = "0.0.3", features = ["integer-impls"] } sbi-spec = { version = "0.0.7", features = ["legacy"] } -tock-registers = "0.9" +tock-registers = "0.10" memoffset = { version = ">=0.6.5", features = ["unstable_const"] } -axerrno = "0.1.0" +axerrno = "0.2" page_table_entry = "0.6" memory_addr = "0.4" -axaddrspace = "0.1.4" -axvcpu = "0.2" -axvisor_api = "0.1" +axaddrspace = "0.3" +axvcpu = "0.3" +axvisor_api = "0.3" diff --git a/src/percpu.rs b/src/percpu.rs index 7fd829d83..00d94f81e 100644 --- a/src/percpu.rs +++ b/src/percpu.rs @@ -12,10 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use core::marker::PhantomData; - use axerrno::{AxError, AxResult}; -use axvcpu::{AxArchPerCpu, AxVCpuHal}; +use axvcpu::AxArchPerCpu; use riscv::register::sie; use riscv_h::register::{hedeleg, hideleg, hvip}; @@ -24,19 +22,15 @@ use crate::consts::traps; use crate::has_hardware_support; /// Risc-V per-CPU state. -pub struct RISCVPerCpu { - _marker: PhantomData, -} +pub struct RISCVPerCpu; -impl AxArchPerCpu for RISCVPerCpu { +impl AxArchPerCpu for RISCVPerCpu { fn new(_cpu_id: usize) -> AxResult { unsafe { setup_csrs(); } - Ok(Self { - _marker: PhantomData, - }) + Ok(Self) } fn is_enabled(&self) -> bool { diff --git a/src/vcpu.rs b/src/vcpu.rs index 57cad0395..83043a41a 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -36,8 +36,8 @@ use crate::{ }; use axaddrspace::{GuestPhysAddr, GuestVirtAddr, HostPhysAddr, MappingFlags, device::AccessWidth}; -use axerrno::{AxError::InvalidData, AxResult}; -use axvcpu::{AxVCpuExitReason, AxVCpuHal}; +use axerrno::{AxErrorKind::InvalidData, AxResult}; +use axvcpu::AxVCpuExitReason; unsafe extern "C" { fn _run_guest(state: *mut VmCpuRegisters); @@ -57,10 +57,9 @@ pub struct VCpuConfig {} #[derive(Default)] /// A virtual CPU within a guest -pub struct RISCVVCpu { +pub struct RISCVVCpu { regs: VmCpuRegisters, sbi: RISCVVCpuSbi, - _marker: core::marker::PhantomData, } #[derive(RustSBI)] @@ -76,7 +75,7 @@ impl Default for RISCVVCpuSbi { } } -impl axvcpu::AxArchVCpu for RISCVVCpu { +impl axvcpu::AxArchVCpu for RISCVVCpu { type CreateConfig = RISCVVCpuCreateConfig; type SetupConfig = (); @@ -92,7 +91,6 @@ impl axvcpu::AxArchVCpu for RISCVVCpu { Ok(Self { regs, sbi: RISCVVCpuSbi::default(), - _marker: core::marker::PhantomData, }) } @@ -228,7 +226,7 @@ impl axvcpu::AxArchVCpu for RISCVVCpu { } } -impl RISCVVCpu { +impl RISCVVCpu { /// Gets one of the vCPU's general purpose registers. pub fn get_gpr(&self, index: GprIndex) -> usize { self.regs.guest_regs.gprs.reg(index) @@ -250,7 +248,7 @@ impl RISCVVCpu { } } -impl RISCVVCpu { +impl RISCVVCpu { fn vmexit_handler(&mut self) -> AxResult { self.regs.trap_csrs.load_from_hw(); From 44c3bff53f9f83515a9eed5d8b6cb544c4853764 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Fri, 6 Mar 2026 14:00:06 +0800 Subject: [PATCH 068/132] Update dependencies, bump to v0.2.2 (#29) * update dependencies * update range-alloc dep * update dependencies, remove patches * bump to version v0.2.2 * update `range-alloc-arceos` to stable release --- Cargo.toml | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7d35cec59..998e85c93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "axdevice" -version = "0.2.1" +version = "0.2.2" description = "A reusable, OS-agnostic device abstraction layer designed for virtual machines." homepage = "https://github.com/arceos-hypervisor/axdevice" repository = "https://github.com/arceos-hypervisor/axdevice" keywords = ["hypervisor"] license = "Apache-2.0" -authors = ["aarkegz "] +authors = ["Su Mingxian "] documentation = "https://docs.rs/axdevice" categories = ["virtualization"] edition = "2024" @@ -17,26 +17,19 @@ readme = "README.md" [dependencies] log = "0.4" cfg-if = "1.0" -spin = "0.9" +spin = "0.10" # System independent crates provided by ArceOS. -axerrno = "0.1.0" +axerrno = "0.2" memory_addr = "0.4" -axvmconfig = { version = "0.2", default-features = false } -axaddrspace = "0.1.4" -axdevice_base = "0.2.1" -range-alloc-arceos = "0.1.4-pre.1" +axvmconfig = { version = "0.2.2", default-features = false } +axaddrspace = "0.3" +axdevice_base = "0.2.2" +range-alloc-arceos = "0.1.4" [target.'cfg(target_arch = "aarch64")'.dependencies] -arm_vgic = { version = "0.2.1", features = ["vgicv3"] } +arm_vgic = { version = "0.2.2", features = ["vgicv3"] } [target.'cfg(target_arch = "riscv64")'.dependencies] -riscv_vplic = "0.2.1" - -[dev-dependencies] -axdevice_base = "0.2.1" -axaddrspace = "0.1.4" -memory_addr = "0.4" -axerrno = "0.1" -axvmconfig = "0.2" \ No newline at end of file +riscv_vplic = "0.2.2" From e58d57c11dc475af914fc53c711ae4ba402bfc0b Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Fri, 6 Mar 2026 15:25:53 +0800 Subject: [PATCH 069/132] Refactor dependencies and enhance AxVM lifecycle management, remove hal, bump to v0.3.0 (#54) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: deps git to crates-io * fix: feature * refactor: support guest dyn entry * refactor: add Clone trait to VMImageConfig * fix: update arm_vcpu branch to 'next' * add fdt support and add some AxVMConfig impl * fix fmt bug * fix bug: when no phys_cpu_ids panic * add function map_reserved_memory_region (#28) * add function map_reserved_memory_region * add passthrough address (#29) * add pass_through address --------- Co-authored-by: szy * add VMStatus enum and integrate into AxVM lifecycle management (#31) * feat: update axerrno to 0.2 (#33) * feat: update axerrno to 0.2 * fix: clippy * update toolchain * update dependencies * remove `AxVCpuHal` * remove type parameter on `AxVMInnerMut` * remove all type parameters * formatted * Update riscv_vcpu dependency source (#39) * fix(riscv): Fix RISC-V vCPU initialization and I/O handling * chore: Update riscv_vcpu dependency source * chore: specify axdevice branch and fix code formatting * docs: add missing documentation comments * Use local path for merged components (axdevice, axvmconfig) * update dependencies * fixes after code merging * formatted * fix clippy * bump version to v0.3.0 * remove `AxVMHal` --------- Co-authored-by: 周睿 Co-authored-by: szy Co-authored-by: bhxh <32200913+buhenxihuan@users.noreply.github.com> Co-authored-by: szy <673586548@qq.com> Co-authored-by: TQ <128586861+YanLien@users.noreply.github.com> Co-authored-by: 朝倉水希 --- Cargo.toml | 25 +++++++------- rust-toolchain.toml | 10 ++++++ src/hal.rs | 72 ++++++++++++++++++++------------------- src/lib.rs | 9 ++--- src/vm.rs | 82 ++++++++++++++++++++++----------------------- 5 files changed, 102 insertions(+), 96 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/Cargo.toml b/Cargo.toml index 1eeb7e158..12cb1c411 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "axvm" authors = ["aarkegz "] -version = "0.2.2" +version = "0.3.0" edition = "2024" categories = ["virtualization", "no-std"] description = "Virtual Machine resource management crate for ArceOS's hypervisor variant." @@ -12,12 +12,12 @@ license = "Apache-2.0" [features] default = ["vmx"] vmx = [] -4-level-ept = ["axaddrspace/4-level-ept"] # TODO: Realize 4-level-ept on x86_64 and riscv64. +4-level-ept = [] # TODO: Realize 4-level-ept on x86_64 and riscv64. [dependencies] log = "0.4" cfg-if = "1.0" -spin = "0.9" +spin = "0.10" # System independent crates provided by ArceOS. axerrno = "0.2" @@ -29,18 +29,19 @@ page_table_multiarch = "0.6" percpu = { version = "0.2.0", features = ["arm-el2"] } # System dependent modules provided by ArceOS-Hypervisor. -axvcpu = "0.2" -axaddrspace = "0.1.4" -axdevice = "0.2.1" -axdevice_base = "0.2.1" -axvmconfig = { version = "0.2", default-features = false } +axvcpu = "0.3" +axaddrspace = "0.3" +axvisor_api = "0.3" +axdevice = "0.2.2" +axdevice_base = "0.2.2" +axvmconfig = { version = "0.2.2", default-features = false } [target.'cfg(target_arch = "x86_64")'.dependencies] -x86_vcpu = "0.2.1" +x86_vcpu = "0.3" [target.'cfg(target_arch = "riscv64")'.dependencies] -riscv_vcpu = "0.2.1" +riscv_vcpu = "0.3" [target.'cfg(target_arch = "aarch64")'.dependencies] -arm_vcpu = "0.2.1" -arm_vgic = { version = "0.2.1", features = ["vgicv3"] } +arm_vcpu = "0.3" +arm_vgic = { version = "0.2.2", features = ["vgicv3"] } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..d3d6b8265 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,10 @@ +[toolchain] +profile = "minimal" +channel = "nightly-2025-05-20" +components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] +targets = [ + "x86_64-unknown-none", + "riscv64gc-unknown-none-elf", + "aarch64-unknown-none-softfloat", + "loongarch64-unknown-none-softfloat", +] \ No newline at end of file diff --git a/src/hal.rs b/src/hal.rs index 787c074f4..5c727cdfe 100644 --- a/src/hal.rs +++ b/src/hal.rs @@ -12,39 +12,41 @@ // See the License for the specific language governing permissions and // limitations under the License. -use axaddrspace::{HostPhysAddr, HostVirtAddr}; -use axerrno::AxResult; - -/// The interfaces which the underlying software (kernel or hypervisor) must implement. -pub trait AxVMHal: Sized { - /// The low-level **OS-dependent** helpers that must be provided for physical address management. - type PagingHandler: page_table_multiarch::PagingHandler; - - /// Converts a virtual address to the corresponding physical address. - fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr; - - /// Current time in nanoseconds. - fn current_time_nanos() -> u64; - - /// Current VM ID. - fn current_vm_id() -> usize; - - /// Current Virtual CPU ID. - fn current_vcpu_id() -> usize; - - /// Current Physical CPU ID. - fn current_pcpu_id() -> usize; - - /// Get the Physical CPU ID where the specified VCPU of the current VM resides. - /// - /// Returns an error if the VCPU is not found. - fn vcpu_resides_on(vm_id: usize, vcpu_id: usize) -> AxResult; - - /// Inject an IRQ to the specified VCPU. - /// - /// This method should find the physical CPU where the specified VCPU resides and inject the IRQ - /// to it on that physical CPU with [`axvcpu::AxVCpu::inject_interrupt`]. - /// - /// Returns an error if the VCPU is not found. - fn inject_irq_to_vcpu(vm_id: usize, vcpu_id: usize, irq: usize) -> AxResult; +use memory_addr::{PAGE_SIZE_4K, PhysAddr, VirtAddr}; +use page_table_multiarch::PagingHandler; + +pub struct PagingHandlerImpl; + +impl PagingHandler for PagingHandlerImpl { + fn alloc_frames(num: usize, align: usize) -> Option { + let align_frames = if align.is_multiple_of(PAGE_SIZE_4K) { + align / PAGE_SIZE_4K + } else { + panic!("align must be multiple of PAGE_SIZE_4K") + }; + + let align_frames_pow2 = if align_frames.is_power_of_two() { + align_frames.trailing_zeros() + } else { + panic!("align must be a power of 2") + }; + + axvisor_api::memory::alloc_contiguous_frames(num, align_frames_pow2 as _) + } + + fn dealloc_frames(paddr: PhysAddr, num: usize) { + axvisor_api::memory::dealloc_contiguous_frames(paddr, num); + } + + fn alloc_frame() -> Option { + axvisor_api::memory::alloc_frame() + } + + fn dealloc_frame(paddr: PhysAddr) { + axvisor_api::memory::dealloc_frame(paddr) + } + + fn phys_to_virt(paddr: PhysAddr) -> VirtAddr { + axvisor_api::memory::phys_to_virt(paddr) + } } diff --git a/src/lib.rs b/src/lib.rs index 30c8c0cf9..520d070cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,15 +33,10 @@ mod vm; pub mod config; -pub use hal::AxVMHal; -pub use vm::AxVCpuRef; -pub use vm::AxVM; -pub use vm::AxVMRef; -pub use vm::VMMemoryRegion; -pub use vm::VMStatus; +pub use vm::{AxVCpuRef, AxVM, AxVMRef, VMMemoryRegion, VMStatus}; /// The architecture-independent per-CPU type. -pub type AxVMPerCpu = axvcpu::AxPerCpu>; +pub type AxVMPerCpu = axvcpu::AxPerCpu; /// Whether the hardware has virtualization support. pub fn has_hardware_support() -> bool { diff --git a/src/vm.rs b/src/vm.rs index 12f62d9b6..81f30d92b 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -18,6 +18,7 @@ use alloc::sync::Arc; use alloc::vec::Vec; use axaddrspace::HostVirtAddr; use axerrno::{AxError, AxResult, ax_err, ax_err_type}; +use axvisor_api::vmm::InterruptVector; use core::alloc::Layout; use core::fmt; use memory_addr::{align_down_4k, align_up_4k}; @@ -25,39 +26,38 @@ use spin::{Mutex, Once}; use axaddrspace::{AddrSpace, GuestPhysAddr, HostPhysAddr, MappingFlags, device::AccessWidth}; use axdevice::{AxVmDeviceConfig, AxVmDevices}; -use axvcpu::{AxVCpu, AxVCpuExitReason, AxVCpuHal}; +use axvcpu::{AxVCpu, AxVCpuExitReason}; use cpumask::CpuMask; use crate::config::{AxVMConfig, PhysCpuList}; +use crate::hal::PagingHandlerImpl; +use crate::has_hardware_support; use crate::vcpu::AxArchVCpuImpl; -use crate::{AxVMHal, has_hardware_support}; -#[cfg(target_arch = "riscv64")] +#[cfg(not(target_arch = "x86_64"))] use crate::vcpu::AxVCpuCreateConfig; + #[cfg(target_arch = "aarch64")] -use crate::vcpu::{AxVCpuCreateConfig, get_sysreg_device}; +use crate::vcpu::get_sysreg_device; const VM_ASPACE_BASE: usize = 0x0; const VM_ASPACE_SIZE: usize = 0x7fff_ffff_f000; /// A vCPU with architecture-independent interface. -#[allow(type_alias_bounds)] -type VCpu = AxVCpu>; +type VCpu = AxVCpu; /// A reference to a vCPU. -#[allow(type_alias_bounds)] -pub type AxVCpuRef = Arc>; +pub type AxVCpuRef = Arc; /// A reference to a VM. -#[allow(type_alias_bounds)] -pub type AxVMRef = Arc>; // we know the bound is not enforced here, we keep it for clarity +pub type AxVMRef = Arc; -struct AxVMInnerConst { +struct AxVMInnerConst { phys_cpu_ls: PhysCpuList, - vcpu_list: Box<[AxVCpuRef]>, + vcpu_list: Box<[AxVCpuRef]>, devices: AxVmDevices, } -unsafe impl Send for AxVMInnerConst {} -unsafe impl Sync for AxVMInnerConst {} +unsafe impl Send for AxVMInnerConst {} +unsafe impl Sync for AxVMInnerConst {} /// Represents a memory region in a virtual machine. #[derive(Debug, Clone)] @@ -84,13 +84,12 @@ impl VMMemoryRegion { } } -struct AxVMInnerMut { +struct AxVMInnerMut { // Todo: use more efficient lock. - address_space: AddrSpace, + address_space: AddrSpace, memory_regions: Vec, config: AxVMConfig, vm_status: VMStatus, - _marker: core::marker::PhantomData, } /// VM status enumeration representing the lifecycle states of a virtual machine @@ -145,19 +144,20 @@ impl fmt::Display for VMStatus { const TEMP_MAX_VCPU_NUM: usize = 64; /// A Virtual Machine. -pub struct AxVM { +pub struct AxVM { id: usize, - inner_const: Once>, - inner_mut: Mutex>, + inner_const: Once, + inner_mut: Mutex, } -impl AxVM { +impl AxVM { /// Creates a new VM with the given configuration. /// Returns an error if the configuration is invalid. /// The VM is not started until `boot` is called. - pub fn new(config: AxVMConfig) -> AxResult> { + pub fn new(config: AxVMConfig) -> AxResult { let address_space = - AddrSpace::new_empty(GuestPhysAddr::from(VM_ASPACE_BASE), VM_ASPACE_SIZE)?; + // TODO: read level from config + AddrSpace::new_empty(4, GuestPhysAddr::from(VM_ASPACE_BASE), VM_ASPACE_SIZE)?; let result = Arc::new(Self { id: config.id(), @@ -167,7 +167,6 @@ impl AxVM { config, memory_regions: Vec::new(), vm_status: VMStatus::Loading, - _marker: core::marker::PhantomData, }), }); @@ -189,7 +188,7 @@ impl AxVM { let dtb_addr = inner_mut.config.image_config().dtb_load_gpa; let vcpu_id_pcpu_sets = inner_mut.config.phys_cpu_ls.get_vcpu_affinities_pcpu_ids(); - info!("dtb_load_gpa: {:?}", dtb_addr); + info!("dtb_load_gpa: {dtb_addr:?}"); debug!("id: {}, VCpuIdPCpuSets: {vcpu_id_pcpu_sets:#x?}", self.id()); let mut vcpu_list = Vec::with_capacity(vcpu_id_pcpu_sets.len()); @@ -205,6 +204,10 @@ impl AxVM { dtb_addr: dtb_addr.unwrap_or_default().as_usize(), }; + // FIXME: VCpu is neither `Send` nor `Sync` by design, check whether + // 1. we should make it `Send` and `Sync`, or + // 2. we can guarantee that no cross-thread access is performed + #[allow(clippy::arc_with_non_send_sync)] vcpu_list.push(Arc::new(VCpu::new( self.id(), vcpu_id, @@ -278,14 +281,10 @@ impl AxVM { )?; } - #[cfg(target_arch = "aarch64")] + #[cfg_attr(not(target_arch = "aarch64"), expect(unused_mut))] let mut devices = axdevice::AxVmDevices::new(AxVmDeviceConfig { emu_configs: inner_mut.config.emu_devices().to_vec(), }); - #[cfg(not(target_arch = "aarch64"))] - let devices = axdevice::AxVmDevices::new(AxVmDeviceConfig { - emu_configs: inner_mut.config.emu_devices().to_vec(), - }); #[cfg(target_arch = "aarch64")] { @@ -346,6 +345,9 @@ impl AxVM { passthrough_timer: passthrough, } }; + #[cfg(not(target_arch = "aarch64"))] + #[allow(clippy::let_unit_value)] + let setup_config = ::SetupConfig::default(); let entry = if vcpu.id() == 0 { inner_mut.config.bsp_entry() @@ -358,10 +360,7 @@ impl AxVM { vcpu.setup( entry, inner_mut.address_space.page_table_root(), - #[cfg(target_arch = "aarch64")] setup_config, - #[cfg(not(target_arch = "aarch64"))] - (), )?; } info!("VM setup: id={}", self.id()); @@ -383,7 +382,7 @@ impl AxVM { /// Retrieves the vCPU corresponding to the given vcpu_id for the VM. /// Returns None if the vCPU does not exist. #[inline] - pub fn vcpu(&self, vcpu_id: usize) -> Option> { + pub fn vcpu(&self, vcpu_id: usize) -> Option { self.vcpu_list().get(vcpu_id).cloned() } @@ -393,7 +392,7 @@ impl AxVM { self.inner_const().vcpu_list.len() } - fn inner_const(&self) -> &AxVMInnerConst { + fn inner_const(&self) -> &AxVMInnerConst { self.inner_const .get() .expect("VM inner_const not initialized") @@ -401,7 +400,7 @@ impl AxVM { /// Returns a reference to the list of vCPUs corresponding to the VM. #[inline] - pub fn vcpu_list(&self) -> &[AxVCpuRef] { + pub fn vcpu_list(&self) -> &[AxVCpuRef] { &self.inner_const().vcpu_list } @@ -594,13 +593,12 @@ impl AxVM { // It is not supported to inject interrupt to a vcpu in another VM yet. // // It may be supported in the future, as a essential feature for cross-VM communication. - if H::current_vm_id() != self.id() { + let current_running_vm = axvisor_api::vmm::current_vm_id(); + if current_running_vm != vm_id { panic!("Injecting interrupt to a vcpu in another VM is not supported"); } - for target_vcpu in &targets { - H::inject_irq_to_vcpu(vm_id, target_vcpu, irq)?; - } + axvisor_api::vmm::inject_interrupt_to_cpus(vm_id, targets, irq as InterruptVector); Ok(()) } @@ -757,7 +755,7 @@ impl AxVM { let s = unsafe { core::slice::from_raw_parts_mut(hva, layout.size()) }; let hva = HostVirtAddr::from_mut_ptr_of(hva); - let hpa = H::virt_to_phys(hva); + let hpa = axvisor_api::memory::virt_to_phys(hva); let gpa = gpa.unwrap_or_else(|| hpa.as_usize().into()); @@ -923,7 +921,7 @@ impl AxVM { } } -impl Drop for AxVM { +impl Drop for AxVM { fn drop(&mut self) { info!("Dropping VM[{}]", self.id()); From c8ec53cac562a67c29bb32fb3f1f500fb6a947e3 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 6 Mar 2026 15:34:02 +0800 Subject: [PATCH 070/132] update dependencies --- Cargo.lock | 551 +++++++++++++++++++++++++++++++++++++++------- Cargo.toml | 40 +--- kernel/Cargo.toml | 12 +- 3 files changed, 482 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d197ed4e..77c961f4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,7 +192,7 @@ dependencies = [ "axdriver", "axerrno 0.1.2", "axfeat", - "axfs", + "axfs 0.2.0", "axhal", "axio", "axlog", @@ -236,15 +236,16 @@ dependencies = [ [[package]] name = "arm_vcpu" -version = "0.1.1" -source = "git+https://github.com/arceos-hypervisor/arm_vcpu?branch=aar-master#3170912a4ff784385691f1ca169e4f87d687e3bf" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "979230a9de461189f361c5a41a01006b4e943dcf9ebbac1c1444fb6c29fdbae2" dependencies = [ "aarch64-cpu 11.2.0", - "axaddrspace", - "axdevice_base", + "axaddrspace 0.3.0", + "axdevice_base 0.2.2", "axerrno 0.2.2", - "axvcpu", - "axvisor_api", + "axvcpu 0.3.0", + "axvisor_api 0.3.0", "log", "numeric-enum-macro", "percpu", @@ -253,15 +254,16 @@ dependencies = [ [[package]] name = "arm_vgic" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/arm_vgic?branch=aar-master#1b145dc767a8d3e0c6f677f26474959de10e6ccf" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49c287b9ceddf2a9f36662df520a85e2fca93a194d303d98d50727c4b19647fa" dependencies = [ "aarch64-cpu 11.2.0", "aarch64_sysreg", - "axaddrspace", - "axdevice_base", + "axaddrspace 0.3.0", + "axdevice_base 0.2.2", "axerrno 0.2.2", - "axvisor_api", + "axvisor_api 0.3.0", "bitmaps", "log", "memory_addr", @@ -304,6 +306,26 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "axaddrspace" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91962ea80ef137c2986b6802d664900e73aa7a014272c13dd9172cc948d5c94e" +dependencies = [ + "axerrno 0.1.2", + "bit_field", + "bitflags 2.10.0", + "cfg-if", + "lazyinit", + "log", + "memory_addr", + "memory_set", + "numeric-enum-macro", + "page_table_entry 0.6.1", + "page_table_multiarch 0.6.1", + "x86", +] + [[package]] name = "axaddrspace" version = "0.2.0" @@ -318,8 +340,28 @@ dependencies = [ "memory_addr", "memory_set", "numeric-enum-macro", - "page_table_entry", - "page_table_multiarch", + "page_table_entry 0.5.7", + "page_table_multiarch 0.5.7", + "x86", +] + +[[package]] +name = "axaddrspace" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2dffa605d03eb184604fb7fcc15f2e641cf21b7a17b8f71e23b22f5c04b7be" +dependencies = [ + "axerrno 0.2.2", + "bit_field", + "bitflags 2.10.0", + "cfg-if", + "lazyinit", + "log", + "memory_addr", + "memory_set", + "numeric-enum-macro", + "page_table_entry 0.6.1", + "page_table_multiarch 0.6.1", "x86", ] @@ -390,10 +432,10 @@ dependencies = [ "log", "loongArch64", "memory_addr", - "page_table_entry", - "page_table_multiarch", + "page_table_entry 0.5.7", + "page_table_multiarch 0.5.7", "percpu", - "riscv", + "riscv 0.14.0", "static_assertions", "tock-registers 0.9.0", "x86", @@ -402,18 +444,37 @@ dependencies = [ [[package]] name = "axdevice" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axdevice.git?branch=aar-master#9e35b2f6bb8d80eeff134dc0f9e80a8085e261d8" +version = "0.2.2" +dependencies = [ + "arm_vgic", + "axaddrspace 0.3.0", + "axdevice_base 0.2.2", + "axerrno 0.2.2", + "axvmconfig 0.2.2", + "cfg-if", + "log", + "memory_addr", + "range-alloc-arceos", + "riscv_vplic 0.2.2", + "spin 0.10.0", +] + +[[package]] +name = "axdevice" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55bcbe9825eecc55421eb205511a0d359f0c83141a4df53f9d3c2824b515fbba" dependencies = [ "arm_vgic", - "axaddrspace", - "axdevice_base", + "axaddrspace 0.3.0", + "axdevice_base 0.2.2", "axerrno 0.2.2", - "axvmconfig", + "axvmconfig 0.2.2", "cfg-if", "log", "memory_addr", - "range-alloc", + "range-alloc-arceos", + "riscv_vplic 0.2.2", "spin 0.10.0", ] @@ -422,14 +483,26 @@ name = "axdevice_base" version = "0.1.0" source = "git+https://github.com/arceos-hypervisor/axdevice_crates?branch=aar-master#f5d6ebe2a1a65f5872f75799cfbc2fd85fa13b85" dependencies = [ - "axaddrspace", + "axaddrspace 0.2.0", "axerrno 0.2.2", - "axvmconfig", + "axvmconfig 0.1.0", "cfg-if", "memory_addr", "serde", ] +[[package]] +name = "axdevice_base" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f466dbb1dcf24f0ad76f737b9b65425a1bdaef30daeff938c9d69d808eb1805" +dependencies = [ + "axaddrspace 0.3.0", + "axerrno 0.2.2", + "axvmconfig 0.2.2", + "serde", +] + [[package]] name = "axdisplay" version = "0.2.0" @@ -564,7 +637,7 @@ dependencies = [ "axalloc", "axbacktrace", "axdriver", - "axfs", + "axfs 0.2.0", "axhal", "axlog", "axruntime", @@ -584,13 +657,45 @@ dependencies = [ "axfs_vfs", "axio", "cap_access", - "fatfs", + "fatfs 0.4.0 (git+https://github.com/Josen-B/rust-fatfs.git?rev=41122ef)", "lazyinit", "log", "rsext4", "spin 0.9.8", ] +[[package]] +name = "axfs" +version = "0.2.0" +source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" +dependencies = [ + "axdriver", + "axdriver_block 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", + "axerrno 0.1.2", + "axfs_devfs", + "axfs_ramfs", + "axfs_vfs", + "axio", + "axsync", + "cap_access", + "cfg-if", + "fatfs 0.4.0 (git+https://github.com/rafalh/rust-fatfs?rev=4eccb50)", + "lazyinit", + "log", + "scope-local", +] + +[[package]] +name = "axfs_devfs" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b87ae981272ca8d5d8f106a4452c63f4b5ac36e17ee8f848ee1b250538b9f8" +dependencies = [ + "axfs_vfs", + "log", + "spin 0.9.8", +] + [[package]] name = "axfs_ramfs" version = "0.1.2" @@ -624,7 +729,7 @@ dependencies = [ "axplat", "axplat-aarch64-qemu-virt", "axplat-loongarch64-qemu-virt", - "axplat-riscv64-qemu-virt", + "axplat-riscv64-qemu-virt 0.3.0 (git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03)", "axplat-x86-pc", "cfg-if", "fdt-parser", @@ -634,7 +739,7 @@ dependencies = [ "linkme", "log", "memory_addr", - "page_table_multiarch", + "page_table_multiarch 0.5.7", "percpu", ] @@ -756,7 +861,7 @@ dependencies = [ "lazyinit", "log", "memory_addr", - "page_table_entry", + "page_table_entry 0.5.7", "paste", "percpu", "rdif-intc", @@ -782,7 +887,7 @@ dependencies = [ "kspin", "lazyinit", "log", - "page_table_entry", + "page_table_entry 0.5.7", "spin 0.10.0", ] @@ -796,7 +901,7 @@ dependencies = [ "axplat", "axplat-aarch64-peripherals", "log", - "page_table_entry", + "page_table_entry 0.5.7", ] [[package]] @@ -811,7 +916,7 @@ dependencies = [ "lazyinit", "log", "loongArch64", - "page_table_entry", + "page_table_entry 0.5.7", "uart_16550", ] @@ -825,6 +930,25 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "axplat-riscv64-qemu-virt" +version = "0.3.0" +dependencies = [ + "axconfig-macros", + "axcpu", + "axplat", + "axvisor_api 0.2.0", + "crate_interface 0.2.0", + "kspin", + "lazyinit", + "log", + "riscv 0.14.0", + "riscv_goldfish", + "riscv_plic", + "sbi-rt", + "uart_16550", +] + [[package]] name = "axplat-riscv64-qemu-virt" version = "0.3.0" @@ -836,7 +960,7 @@ dependencies = [ "kspin", "lazyinit", "log", - "riscv", + "riscv 0.14.0", "riscv_plic", "sbi-rt", "uart_16550", @@ -907,8 +1031,8 @@ dependencies = [ "axconfig", "axdisplay", "axdriver", - "axerrno 0.1.2", - "axfs", + "axerrno 0.2.2", + "axfs 0.1.0", "axhal", "axipi", "axlog", @@ -916,6 +1040,7 @@ dependencies = [ "axnet", "axplat", "axplat-aarch64-dyn", + "axplat-riscv64-qemu-virt 0.3.0", "axplat-x86-qemu-q35", "axtask", "cfg-if", @@ -1041,9 +1166,22 @@ name = "axvcpu" version = "0.1.2" source = "git+https://github.com/arceos-hypervisor/axvcpu?branch=aar-master#62425896e4fb112efd414c0a49290086bfec6b5c" dependencies = [ - "axaddrspace", + "axaddrspace 0.2.0", + "axerrno 0.2.2", + "axvisor_api 0.2.0", + "memory_addr", + "percpu", +] + +[[package]] +name = "axvcpu" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "506c3ab0ce353e76d3279927b88a43ee0effd4368ce86e8bc72a0ee9430e0709" +dependencies = [ + "axaddrspace 0.3.0", "axerrno 0.2.2", - "axvisor_api", + "axvisor_api 0.3.0", "memory_addr", "percpu", ] @@ -1055,16 +1193,18 @@ dependencies = [ "aarch64-cpu-ext", "anyhow", "arm-gic-driver", - "axaddrspace", + "axaddrspace 0.3.0", "axconfig", - "axdevice", - "axdevice_base", + "axdevice 0.2.2", + "axdevice_base 0.1.0", + "axdriver", "axerrno 0.2.2", "axhvc", + "axplat-riscv64-qemu-virt 0.3.0", "axruntime", "axstd", - "axvcpu", - "axvisor_api", + "axvcpu 0.3.0", + "axvisor_api 0.2.0", "axvm", "bitflags 2.10.0", "byte-unit", @@ -1080,13 +1220,15 @@ dependencies = [ "lazyinit", "log", "memory_addr", - "page_table_entry", - "page_table_multiarch", + "page_table_entry 0.5.7", + "page_table_multiarch 0.5.7", "percpu", "prettyplease", "quote", "rdif-intc", "rdrive", + "riscv_vcpu 0.1.2", + "riscv_vplic 0.1.0", "spin 0.10.0", "syn 2.0.111", "timer_list", @@ -1094,18 +1236,55 @@ dependencies = [ "vm-fdt 0.3.0 (git+https://github.com/bullhh/vm-fdt.git)", ] +[[package]] +name = "axvisor_api" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa7233b2a1338dc06a80e2779b572b4df02007ea128ef7b235b66fc3eeac0ca6" +dependencies = [ + "axaddrspace 0.1.5", + "axvisor_api_proc 0.1.0", + "crate_interface 0.1.4", + "memory_addr", +] + [[package]] name = "axvisor_api" version = "0.2.0" source = "git+https://github.com/arceos-hypervisor/axvisor_api?branch=aar-master#657ef629e46738f8b32987cf1de7a5950c1ba904" dependencies = [ - "axaddrspace", - "axvisor_api_proc", + "axaddrspace 0.2.0", + "axvisor_api_proc 0.2.0", "cpumask", "crate_interface 0.2.0", "memory_addr", ] +[[package]] +name = "axvisor_api" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7959b2dd8afbac2a0cda189986df6ceb753a92e985c28ce2c279cf432cbe2dfc" +dependencies = [ + "axaddrspace 0.3.0", + "axvisor_api_proc 0.3.0", + "cpumask", + "crate_interface 0.3.0", + "memory_addr", +] + +[[package]] +name = "axvisor_api_proc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a64eb4410ae8357ac8c01c2fb201e57d7aeeb5436ed4d0f5774bfa11cc5902" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "axvisor_api_proc" version = "0.2.0" @@ -1117,28 +1296,39 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "axvisor_api_proc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5351791fec23f5545518f88d4d8a134c564085a6f37ed6953bbd4f35d4f32c7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "axvm" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axvm.git?branch=aar-master#7185c2fdd3b372fab96c82f42f7d81b24e0c99f0" +version = "0.3.0" dependencies = [ "arm_vcpu", "arm_vgic", - "axaddrspace", - "axdevice", - "axdevice_base", + "axaddrspace 0.3.0", + "axdevice 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axdevice_base 0.2.2", "axerrno 0.2.2", - "axvcpu", - "axvisor_api", - "axvmconfig", + "axvcpu 0.3.0", + "axvisor_api 0.3.0", + "axvmconfig 0.2.2", "cfg-if", "cpumask", "log", "memory_addr", - "page_table_entry", - "page_table_multiarch", + "page_table_entry 0.6.1", + "page_table_multiarch 0.6.1", "percpu", - "riscv_vcpu", + "riscv_vcpu 0.3.0", "spin 0.10.0", "x86_vcpu", ] @@ -1146,7 +1336,20 @@ dependencies = [ [[package]] name = "axvmconfig" version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axvmconfig.git?branch=aar-master#494b8d6d56fe9b7e155837801fcc75a4990f06b8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3618f2ebd9b25027b70f423c48a8482dc2883ca17f76d2a8d1fdac377e762f02" +dependencies = [ + "axerrno 0.1.2", + "enumerable", + "log", + "serde", + "serde_repr", + "toml 0.9.10+spec-1.1.0", +] + +[[package]] +name = "axvmconfig" +version = "0.2.2" dependencies = [ "axerrno 0.2.2", "clap", @@ -1689,6 +1892,17 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "crate_interface" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e5c7109dea31fc91ab584e99752baa997f76d46e49bab0a17b5e9679248df7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "crc" version = "3.4.0" @@ -2293,6 +2507,15 @@ dependencies = [ "log", ] +[[package]] +name = "fatfs" +version = "0.4.0" +source = "git+https://github.com/rafalh/rust-fatfs?rev=4eccb50#4eccb50d011146fbed20e133d33b22f3c27292e7" +dependencies = [ + "bitflags 2.10.0", + "log", +] + [[package]] name = "fdt-parser" version = "0.4.18" @@ -3743,6 +3966,18 @@ dependencies = [ "x86_64", ] +[[package]] +name = "page_table_entry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9a63b9b86d32f64c3874a90936939281d045ef1751d0aca3d82d5e4e06b2ef" +dependencies = [ + "aarch64-cpu 11.2.0", + "bitflags 2.10.0", + "memory_addr", + "x86_64", +] + [[package]] name = "page_table_multiarch" version = "0.5.7" @@ -3751,8 +3986,22 @@ checksum = "9fa11a21844255e14aa6688ef0eafb058d7be19338633024fb59417f1bfb07f8" dependencies = [ "log", "memory_addr", - "page_table_entry", - "riscv", + "page_table_entry 0.5.7", + "riscv 0.14.0", + "x86", +] + +[[package]] +name = "page_table_multiarch" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42c5b75d5d9bdbee44c827b0dd2766fa3d478a76b9c6735419228089d1b24536" +dependencies = [ + "arrayvec", + "log", + "memory_addr", + "page_table_entry 0.6.1", + "riscv 0.16.0", "x86", ] @@ -4164,9 +4413,10 @@ dependencies = [ ] [[package]] -name = "range-alloc" +name = "range-alloc-arceos" version = "0.1.4" -source = "git+https://github.com/arceos-hypervisor/range-alloc.git#fc826e54dab9072be5358a1b0e7fc34503d6630d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187083342c080f6150a1e8e6e6a687b49239df195aaf866ff4ed535961ab860e" [[package]] name = "ratatui" @@ -4507,6 +4757,18 @@ dependencies = [ "riscv-pac", ] +[[package]] +name = "riscv" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9251433e48c39d2133cbaff3ae7809ce6a1ecbc8225ca7da33d96d10cf360582" +dependencies = [ + "critical-section", + "embedded-hal", + "paste", + "riscv-types", +] + [[package]] name = "riscv-decode" version = "0.2.3" @@ -4523,7 +4785,20 @@ dependencies = [ "bit_field", "bitflags 2.10.0", "log", - "riscv", + "riscv 0.14.0", +] + +[[package]] +name = "riscv-h" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bf93901d1af87f984c7be7a348cd27571e1f6f29293b8f3b3193f4c40d4d40" +dependencies = [ + "bare-metal", + "bit_field", + "bitflags 2.10.0", + "log", + "riscv 0.14.0", ] [[package]] @@ -4543,6 +4818,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" +[[package]] +name = "riscv-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3f2ad9f15a07f4a0e1677124f9120ce7e83ab7e1ca7186af0ca9da529b62e80" + +[[package]] +name = "riscv_goldfish" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07aac72f95e774476db82916d79f2d303191310393830573c1ab5c821b21660a" + [[package]] name = "riscv_plic" version = "0.2.0" @@ -4557,10 +4844,10 @@ name = "riscv_vcpu" version = "0.1.2" source = "git+https://github.com/arceos-hypervisor/riscv_vcpu?branch=aar-master#4cad20373c85d150e509821c3551fefb16084a7a" dependencies = [ - "axaddrspace", + "axaddrspace 0.2.0", "axerrno 0.2.2", - "axvcpu", - "axvisor_api", + "axvcpu 0.1.2", + "axvisor_api 0.2.0", "bit_field", "bitflags 2.10.0", "cfg-if", @@ -4568,16 +4855,74 @@ dependencies = [ "log", "memoffset", "memory_addr", - "page_table_entry", - "riscv", + "page_table_entry 0.5.7", + "riscv 0.14.0", + "riscv-decode", + "riscv-h 0.1.0", + "rustsbi", + "sbi-rt", + "sbi-spec", + "tock-registers 0.10.1", +] + +[[package]] +name = "riscv_vcpu" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f7d192465c7a3e2f674c56d3e49cfeb00f84d0616137cbecb8aacb250e40c53" +dependencies = [ + "axaddrspace 0.3.0", + "axerrno 0.2.2", + "axvcpu 0.3.0", + "axvisor_api 0.3.0", + "bit_field", + "bitflags 2.10.0", + "cfg-if", + "crate_interface 0.3.0", + "log", + "memoffset", + "memory_addr", + "page_table_entry 0.6.1", + "riscv 0.14.0", "riscv-decode", - "riscv-h", + "riscv-h 0.2.0", "rustsbi", "sbi-rt", "sbi-spec", "tock-registers 0.10.1", ] +[[package]] +name = "riscv_vplic" +version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/riscv_vplic?rev=8bc5213#8bc5213c77139755e1106b49133248392fcb4f58" +dependencies = [ + "axaddrspace 0.1.5", + "axdevice_base 0.1.0", + "axerrno 0.1.2", + "axvisor_api 0.1.0", + "bitmaps", + "log", + "riscv-h 0.1.0", + "spin 0.9.8", +] + +[[package]] +name = "riscv_vplic" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec84bba7d814f072afdd11a61c64ad55bedaeb17937a78bb6b9cec44b63a70fb" +dependencies = [ + "axaddrspace 0.3.0", + "axdevice_base 0.2.2", + "axerrno 0.2.2", + "axvisor_api 0.3.0", + "bitmaps", + "log", + "riscv-h 0.2.0", + "spin 0.10.0", +] + [[package]] name = "rk3568_clk" version = "0.1.0" @@ -4864,6 +5209,16 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "scope-local" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a7d5ed5013e6436fcd78f2bcd3892a6286ef9ce6c9b61504d4c4a08d6a40eab" +dependencies = [ + "percpu", + "spin 0.10.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -6582,25 +6937,26 @@ dependencies = [ [[package]] name = "x86_vcpu" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/x86_vcpu?branch=aar-master#24fce09b11af95077f7efc03ff6634bf78ffa187" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "906f6ead2156dde21b712f0eb654b620d1c2652e62b6b0731a7807a565454692" dependencies = [ - "axaddrspace", - "axdevice_base", + "axaddrspace 0.3.0", + "axdevice_base 0.2.2", "axerrno 0.2.2", - "axvcpu", - "axvisor_api", + "axvcpu 0.3.0", + "axvisor_api 0.3.0", "bit_field", "bitflags 2.10.0", "cfg-if", - "crate_interface 0.2.0", + "crate_interface 0.3.0", "log", "memory_addr", "numeric-enum-macro", - "page_table_entry", + "page_table_entry 0.6.1", "paste", "raw-cpuid 11.6.0", - "spin 0.9.8", + "spin 0.10.0", "x86", "x86_64", "x86_vlapic", @@ -6608,13 +6964,14 @@ dependencies = [ [[package]] name = "x86_vlapic" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/x86_vlapic?branch=aar-master#207fb0dfa10ff549b46a77f2d767483fad0c29f6" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73364dacf1435c64e3395567405311456c3087f7bb4852904332e0fa1379eb65" dependencies = [ - "axaddrspace", - "axdevice_base", + "axaddrspace 0.3.0", + "axdevice_base 0.2.2", "axerrno 0.2.2", - "axvisor_api", + "axvisor_api 0.3.0", "bit", "log", "memory_addr", @@ -6643,7 +7000,7 @@ name = "xtask" version = "0.1.0" dependencies = [ "anyhow", - "axvmconfig", + "axvmconfig 0.2.2", "cargo_metadata 0.23.1", "chrono", "clap", @@ -6784,3 +7141,27 @@ dependencies = [ "quote", "syn 2.0.111", ] + +[[patch.unused]] +name = "arm_vcpu" +version = "0.1.1" +source = "git+https://github.com/arceos-hypervisor/arm_vcpu?branch=aar-master#3170912a4ff784385691f1ca169e4f87d687e3bf" + +[[patch.unused]] +name = "arm_vgic" +version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/arm_vgic?branch=aar-master#1b145dc767a8d3e0c6f677f26474959de10e6ccf" + +[[patch.unused]] +name = "x86_vcpu" +version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/x86_vcpu?branch=aar-master#24fce09b11af95077f7efc03ff6634bf78ffa187" + +[[patch.unused]] +name = "x86_vlapic" +version = "0.1.0" +source = "git+https://github.com/arceos-hypervisor/x86_vlapic?branch=aar-master#207fb0dfa10ff549b46a77f2d767483fad0c29f6" + +[[patch.unused]] +name = "riscv_vplic" +version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index b493c720b..b9b2462c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,59 +57,39 @@ axcpu = {git = "https://github.com/arceos-org/axcpu.git", tag = "dev-v03"} axplat = {git = "https://github.com/arceos-org/axplat_crates.git", tag = "dev-v03"} # System dependent modules provided by ArceOS-Hypervisor. -axaddrspace = "0.2" +axaddrspace = "0.3" axhvc = {git = "https://github.com/arceos-hypervisor/axhvc.git"} axklib = {git = "https://github.com/arceos-hypervisor/axklib.git"} axruntime = {path = "modules/axruntime"} axfs = {path = "modules/axfs"} -axvcpu = "0.1" -# axvm = {git = "https://github.com/arceos-hypervisor/axvm.git", branch = "aar-next"} -axvm = { path = "../axvm" } +axvcpu = "0.3" +axvm = "0.3" # System independent crates provided by ArceOS, these crates could be imported by remote url. axerrno = "0.2" byte-unit = {version = "5", default-features = false, features = ["byte"]} -crate_interface = "0.2" +crate_interface = "0.3" fdt-parser = "0.4" memory_addr = "0.4" -page_table_entry = {version = "0.5", features = ["arm-el2"]} -page_table_multiarch = "0.5" +page_table_entry = {version = "0.6", features = ["arm-el2"]} +page_table_multiarch = "0.6" percpu = {version = "0.2", features = ["arm-el2"]} rdif-intc = "0.12" rdrive = "0.18" vm-fdt = {git = "https://github.com/bullhh/vm-fdt.git", default-features = false, features = ["alloc"]} -# axdevice = {git = "https://github.com/arceos-hypervisor/axdevice.git", branch = "aar-next"} -axdevice = { path = "../axdevice" } -axdevice_base = "0.1" -axvisor_api = "0.2" +axdevice = "0.2.2" +axdevice_base = "0.2.2" +axvisor_api = "0.3" +axvmconfig = { version = "0.2.2", default-features = false } driver = {path = "modules/driver"} # platform axplat-x86-qemu-q35 = {path = "platform/x86-qemu-q35"} axplat-riscv64-qemu-virt = {path = "platform/riscv64-qemu-virt"} -# axvmconfig = {git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "aar-next"} -axvmconfig = { path = "../axvmconfig" } [patch."https://github.com/arceos-org/arceos.git"] axconfig = { path = "modules/axconfig" } axruntime = { path = "modules/axruntime" } - -[patch.crates-io] -axvcpu = { git = "https://github.com/arceos-hypervisor/axvcpu", branch = "aar-master" } -axvisor_api = { git = "https://github.com/arceos-hypervisor/axvisor_api", branch = "aar-master" } -axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace", branch = "aar-master" } -axdevice_base = { git = "https://github.com/arceos-hypervisor/axdevice_crates", branch = "aar-master"} -x86_vlapic = { git = "https://github.com/arceos-hypervisor/x86_vlapic", branch = "aar-master"} -arm_vgic = { git = "https://github.com/arceos-hypervisor/arm_vgic", branch = "aar-master"} -x86_vcpu = { git = "https://github.com/arceos-hypervisor/x86_vcpu", branch = "aar-master"} -arm_vcpu = { git = "https://github.com/arceos-hypervisor/arm_vcpu", branch = "aar-master"} -riscv_vcpu = { git = "https://github.com/arceos-hypervisor/riscv_vcpu", branch = "aar-master"} -# axvmconfig = { git = "https://github.com/arceos-hypervisor/axvmconfig.git", branch = "aar-next" } -axvmconfig = { path = "../axvmconfig" } - -[patch."https://github.com/arceos-org/arceos"] -axconfig = {path = "modules/axconfig"} -axruntime = {path = "modules/axruntime"} axfs = {path = "modules/axfs"} diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index b70676dde..ce57a49e0 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -45,8 +45,8 @@ crate_interface.workspace = true extern-trait = "0.2" fdt-parser = "0.4" memory_addr.workspace = true -page_table_entry = {version = "0.5", features = ["arm-el2"]} -page_table_multiarch = "0.5" +page_table_entry.workspace = true +page_table_multiarch.workspace = true percpu = {version = "0.2", features = ["arm-el2"]} rdif-intc = "0.12" rdrive = "0.18" @@ -54,8 +54,8 @@ rdrive = "0.18" vm-fdt = {workspace = true, default-features = false, features = ["alloc"]} axdevice.workspace = true -axdevice_base = "0.1" -axvisor_api = "0.2" +axdevice_base.workspace = true +axvisor_api.workspace = true driver.workspace = true [target.'cfg(target_arch = "aarch64")'.dependencies] @@ -64,8 +64,8 @@ arm-gic-driver = {version = "0.15.5", features = ["rdif"]} [target.'cfg(target_arch = "riscv64")'.dependencies] axplat-riscv64-qemu-virt.workspace = true -riscv_vplic = { git = "https://github.com/arceos-hypervisor/riscv_vplic.git", rev = "8bc5213" } -riscv_vcpu = { git = "https://github.com/arceos-hypervisor/riscv_vcpu.git", branch = "master" } +riscv_vplic.workspace = true +riscv_vcpu.workspace = true [build-dependencies] axconfig.workspace = true From 06d18d7ed167b627dc1dea2367b7ae5c45a5fbea Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 6 Mar 2026 15:46:57 +0800 Subject: [PATCH 071/132] fixes after merge --- Cargo.lock | 369 ++++++++++-------------------------------- kernel/Cargo.toml | 4 +- kernel/src/hal/mod.rs | 6 +- 3 files changed, 87 insertions(+), 292 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56c0f8e32..13df05b3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,11 +241,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "979230a9de461189f361c5a41a01006b4e943dcf9ebbac1c1444fb6c29fdbae2" dependencies = [ "aarch64-cpu 11.2.0", - "axaddrspace 0.3.0", - "axdevice_base 0.2.2", + "axaddrspace", + "axdevice_base", "axerrno 0.2.2", - "axvcpu 0.3.0", - "axvisor_api 0.3.0", + "axvcpu", + "axvisor_api", "log", "numeric-enum-macro", "percpu", @@ -260,10 +260,10 @@ checksum = "49c287b9ceddf2a9f36662df520a85e2fca93a194d303d98d50727c4b19647fa" dependencies = [ "aarch64-cpu 11.2.0", "aarch64_sysreg", - "axaddrspace 0.3.0", - "axdevice_base 0.2.2", + "axaddrspace", + "axdevice_base", "axerrno 0.2.2", - "axvisor_api 0.3.0", + "axvisor_api", "bitmaps", "log", "memory_addr", @@ -328,45 +328,6 @@ dependencies = [ "fs_extra", ] -[[package]] -name = "axaddrspace" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91962ea80ef137c2986b6802d664900e73aa7a014272c13dd9172cc948d5c94e" -dependencies = [ - "axerrno 0.1.2", - "bit_field", - "bitflags 2.10.0", - "cfg-if", - "lazyinit", - "log", - "memory_addr", - "memory_set", - "numeric-enum-macro", - "page_table_entry 0.6.1", - "page_table_multiarch 0.6.1", - "x86", -] - -[[package]] -name = "axaddrspace" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axaddrspace?branch=aar-master#16163ef4a4516fa2d5b51c8b08f5940d1ff31f04" -dependencies = [ - "axerrno 0.2.2", - "bit_field", - "bitflags 2.10.0", - "cfg-if", - "lazyinit", - "log", - "memory_addr", - "memory_set", - "numeric-enum-macro", - "page_table_entry 0.5.7", - "page_table_multiarch 0.5.7", - "x86", -] - [[package]] name = "axaddrspace" version = "0.3.0" @@ -464,23 +425,6 @@ dependencies = [ "x86_64", ] -[[package]] -name = "axdevice" -version = "0.2.2" -dependencies = [ - "arm_vgic", - "axaddrspace 0.3.0", - "axdevice_base 0.2.2", - "axerrno 0.2.2", - "axvmconfig 0.2.2", - "cfg-if", - "log", - "memory_addr", - "range-alloc-arceos", - "riscv_vplic 0.2.2", - "spin 0.10.0", -] - [[package]] name = "axdevice" version = "0.2.2" @@ -488,40 +432,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55bcbe9825eecc55421eb205511a0d359f0c83141a4df53f9d3c2824b515fbba" dependencies = [ "arm_vgic", - "axaddrspace 0.3.0", - "axdevice_base 0.2.2", + "axaddrspace", + "axdevice_base", "axerrno 0.2.2", - "axvmconfig 0.2.2", + "axvmconfig", "cfg-if", "log", "memory_addr", "range-alloc-arceos", - "riscv_vplic 0.2.2", + "riscv_vplic", "spin 0.10.0", ] -[[package]] -name = "axdevice_base" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/axdevice_crates?branch=aar-master#f5d6ebe2a1a65f5872f75799cfbc2fd85fa13b85" -dependencies = [ - "axaddrspace 0.2.0", - "axerrno 0.2.2", - "axvmconfig 0.1.0", - "cfg-if", - "memory_addr", - "serde", -] - [[package]] name = "axdevice_base" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f466dbb1dcf24f0ad76f737b9b65425a1bdaef30daeff938c9d69d808eb1805" dependencies = [ - "axaddrspace 0.3.0", + "axaddrspace", "axerrno 0.2.2", - "axvmconfig 0.2.2", + "axvmconfig", "serde", ] @@ -959,8 +890,8 @@ dependencies = [ "axconfig-macros", "axcpu", "axplat", - "axvisor_api 0.2.0", - "crate_interface 0.2.0", + "axvisor_api", + "crate_interface 0.3.0", "kspin", "lazyinit", "log", @@ -1183,27 +1114,15 @@ dependencies = [ "tracing", ] -[[package]] -name = "axvcpu" -version = "0.1.2" -source = "git+https://github.com/arceos-hypervisor/axvcpu?branch=aar-master#62425896e4fb112efd414c0a49290086bfec6b5c" -dependencies = [ - "axaddrspace 0.2.0", - "axerrno 0.2.2", - "axvisor_api 0.2.0", - "memory_addr", - "percpu", -] - [[package]] name = "axvcpu" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "506c3ab0ce353e76d3279927b88a43ee0effd4368ce86e8bc72a0ee9430e0709" dependencies = [ - "axaddrspace 0.3.0", + "axaddrspace", "axerrno 0.2.2", - "axvisor_api 0.3.0", + "axvisor_api", "memory_addr", "percpu", ] @@ -1215,24 +1134,24 @@ dependencies = [ "aarch64-cpu-ext", "anyhow", "arm-gic-driver", - "axaddrspace 0.3.0", + "axaddrspace", "axconfig", - "axdevice 0.2.2", - "axdevice_base 0.1.0", + "axdevice", + "axdevice_base", "axdriver", "axerrno 0.2.2", "axhvc", "axplat-riscv64-qemu-virt 0.3.0", "axruntime", "axstd", - "axvcpu 0.3.0", - "axvisor_api 0.2.0", + "axvcpu", + "axvisor_api", "axvm", "bitflags 2.10.0", "byte-unit", "cfg-if", "cpumask", - "crate_interface 0.2.0", + "crate_interface 0.3.0", "driver", "extern-trait", "fdt-parser", @@ -1242,15 +1161,15 @@ dependencies = [ "lazyinit", "log", "memory_addr", - "page_table_entry 0.5.7", - "page_table_multiarch 0.5.7", + "page_table_entry 0.6.1", + "page_table_multiarch 0.6.1", "percpu", "prettyplease", "quote", "rdif-intc", "rdrive", - "riscv_vcpu 0.1.2", - "riscv_vplic 0.1.0", + "riscv_vcpu", + "riscv_vplic", "spin 0.10.0", "syn 2.0.111", "timer_list", @@ -1258,66 +1177,19 @@ dependencies = [ "vm-fdt 0.3.0 (git+https://github.com/bullhh/vm-fdt.git)", ] -[[package]] -name = "axvisor_api" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7233b2a1338dc06a80e2779b572b4df02007ea128ef7b235b66fc3eeac0ca6" -dependencies = [ - "axaddrspace 0.1.5", - "axvisor_api_proc 0.1.0", - "crate_interface 0.1.4", - "memory_addr", -] - -[[package]] -name = "axvisor_api" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axvisor_api?branch=aar-master#657ef629e46738f8b32987cf1de7a5950c1ba904" -dependencies = [ - "axaddrspace 0.2.0", - "axvisor_api_proc 0.2.0", - "cpumask", - "crate_interface 0.2.0", - "memory_addr", -] - [[package]] name = "axvisor_api" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7959b2dd8afbac2a0cda189986df6ceb753a92e985c28ce2c279cf432cbe2dfc" dependencies = [ - "axaddrspace 0.3.0", - "axvisor_api_proc 0.3.0", + "axaddrspace", + "axvisor_api_proc", "cpumask", "crate_interface 0.3.0", "memory_addr", ] -[[package]] -name = "axvisor_api_proc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a64eb4410ae8357ac8c01c2fb201e57d7aeeb5436ed4d0f5774bfa11cc5902" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "axvisor_api_proc" -version = "0.2.0" -source = "git+https://github.com/arceos-hypervisor/axvisor_api?branch=aar-master#657ef629e46738f8b32987cf1de7a5950c1ba904" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "axvisor_api_proc" version = "0.3.0" @@ -1333,16 +1205,18 @@ dependencies = [ [[package]] name = "axvm" version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6406a71e7af560b7d544fb697f3071e045d304a20d8525502b5a906dbbaab" dependencies = [ "arm_vcpu", "arm_vgic", - "axaddrspace 0.3.0", - "axdevice 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axdevice_base 0.2.2", + "axaddrspace", + "axdevice", + "axdevice_base", "axerrno 0.2.2", - "axvcpu 0.3.0", - "axvisor_api 0.3.0", - "axvmconfig 0.2.2", + "axvcpu", + "axvisor_api", + "axvmconfig", "cfg-if", "cpumask", "log", @@ -1350,28 +1224,16 @@ dependencies = [ "page_table_entry 0.6.1", "page_table_multiarch 0.6.1", "percpu", - "riscv_vcpu 0.3.0", + "riscv_vcpu", "spin 0.10.0", "x86_vcpu", ] -[[package]] -name = "axvmconfig" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3618f2ebd9b25027b70f423c48a8482dc2883ca17f76d2a8d1fdac377e762f02" -dependencies = [ - "axerrno 0.1.2", - "enumerable", - "log", - "serde", - "serde_repr", - "toml 0.9.10+spec-1.1.0", -] - [[package]] name = "axvmconfig" version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f2cf8bfcabc84e636f5cf3cd7b1c1ddcf1f584d6fa91791a572056204db35a" dependencies = [ "axerrno 0.2.2", "clap", @@ -1931,17 +1793,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "crate_interface" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15627e437dc14b22c0a7b8cfc7143fbab68aa6d740df2d695152b1dca58cdf17" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "crate_interface" version = "0.3.0" @@ -4066,6 +3917,18 @@ dependencies = [ "x86_64", ] +[[package]] +name = "page_table_entry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9a63b9b86d32f64c3874a90936939281d045ef1751d0aca3d82d5e4e06b2ef" +dependencies = [ + "aarch64-cpu 11.2.0", + "bitflags 2.10.0", + "memory_addr", + "x86_64", +] + [[package]] name = "page_table_multiarch" version = "0.5.7" @@ -4074,8 +3937,22 @@ checksum = "9fa11a21844255e14aa6688ef0eafb058d7be19338633024fb59417f1bfb07f8" dependencies = [ "log", "memory_addr", - "page_table_entry", - "riscv", + "page_table_entry 0.5.7", + "riscv 0.14.0", + "x86", +] + +[[package]] +name = "page_table_multiarch" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42c5b75d5d9bdbee44c827b0dd2766fa3d478a76b9c6735419228089d1b24536" +dependencies = [ + "arrayvec", + "log", + "memory_addr", + "page_table_entry 0.6.1", + "riscv 0.16.0", "x86", ] @@ -4889,19 +4766,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b59d645e392e041ad18f5e529ed13242d8405c66bb192f59703ea2137017d0" -[[package]] -name = "riscv-h" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cffa652689d01c5f7033abe105e69f4d57ac85bf7e17da688bab10e4b9d3a2d8" -dependencies = [ - "bare-metal", - "bit_field", - "bitflags 2.10.0", - "log", - "riscv 0.14.0", -] - [[package]] name = "riscv-h" version = "0.2.0" @@ -4953,42 +4817,16 @@ dependencies = [ "tock-registers 0.10.1", ] -[[package]] -name = "riscv_vcpu" -version = "0.1.2" -source = "git+https://github.com/arceos-hypervisor/riscv_vcpu?branch=aar-master#4cad20373c85d150e509821c3551fefb16084a7a" -dependencies = [ - "axaddrspace 0.2.0", - "axerrno 0.2.2", - "axvcpu 0.1.2", - "axvisor_api 0.2.0", - "bit_field", - "bitflags 2.10.0", - "cfg-if", - "crate_interface 0.2.0", - "log", - "memoffset", - "memory_addr", - "page_table_entry", - "riscv", - "riscv-decode", - "riscv-h 0.1.0", - "rustsbi", - "sbi-rt", - "sbi-spec", - "tock-registers 0.10.1", -] - [[package]] name = "riscv_vcpu" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f7d192465c7a3e2f674c56d3e49cfeb00f84d0616137cbecb8aacb250e40c53" dependencies = [ - "axaddrspace 0.3.0", + "axaddrspace", "axerrno 0.2.2", - "axvcpu 0.3.0", - "axvisor_api 0.3.0", + "axvcpu", + "axvisor_api", "bit_field", "bitflags 2.10.0", "cfg-if", @@ -4999,41 +4837,26 @@ dependencies = [ "page_table_entry 0.6.1", "riscv 0.14.0", "riscv-decode", - "riscv-h 0.2.0", + "riscv-h", "rustsbi", "sbi-rt", "sbi-spec", "tock-registers 0.10.1", ] -[[package]] -name = "riscv_vplic" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/riscv_vplic?rev=8bc5213#8bc5213c77139755e1106b49133248392fcb4f58" -dependencies = [ - "axaddrspace 0.1.5", - "axdevice_base 0.1.0", - "axerrno 0.1.2", - "axvisor_api 0.1.0", - "bitmaps", - "log", - "riscv-h 0.1.0", - "spin 0.9.8", -] - [[package]] name = "riscv_vplic" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec84bba7d814f072afdd11a61c64ad55bedaeb17937a78bb6b9cec44b63a70fb" dependencies = [ - "axaddrspace 0.3.0", - "axdevice_base 0.2.2", + "axaddrspace", + "axdevice_base", "axerrno 0.2.2", - "axvisor_api 0.3.0", + "axvisor_api", "bitmaps", "log", - "riscv-h 0.2.0", + "riscv-h", "spin 0.10.0", ] @@ -7216,11 +7039,11 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "906f6ead2156dde21b712f0eb654b620d1c2652e62b6b0731a7807a565454692" dependencies = [ - "axaddrspace 0.3.0", - "axdevice_base 0.2.2", + "axaddrspace", + "axdevice_base", "axerrno 0.2.2", - "axvcpu 0.3.0", - "axvisor_api 0.3.0", + "axvcpu", + "axvisor_api", "bit_field", "bitflags 2.10.0", "cfg-if", @@ -7243,10 +7066,10 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73364dacf1435c64e3395567405311456c3087f7bb4852904332e0fa1379eb65" dependencies = [ - "axaddrspace 0.3.0", - "axdevice_base 0.2.2", + "axaddrspace", + "axdevice_base", "axerrno 0.2.2", - "axvisor_api 0.3.0", + "axvisor_api", "bit", "log", "memory_addr", @@ -7275,7 +7098,7 @@ name = "xtask" version = "0.1.0" dependencies = [ "anyhow", - "axvmconfig 0.2.2", + "axvmconfig", "cargo_metadata 0.23.1", "chrono", "clap", @@ -7417,27 +7240,3 @@ dependencies = [ "quote", "syn 2.0.111", ] - -[[patch.unused]] -name = "arm_vcpu" -version = "0.1.1" -source = "git+https://github.com/arceos-hypervisor/arm_vcpu?branch=aar-master#3170912a4ff784385691f1ca169e4f87d687e3bf" - -[[patch.unused]] -name = "arm_vgic" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/arm_vgic?branch=aar-master#1b145dc767a8d3e0c6f677f26474959de10e6ccf" - -[[patch.unused]] -name = "x86_vcpu" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/x86_vcpu?branch=aar-master#24fce09b11af95077f7efc03ff6634bf78ffa187" - -[[patch.unused]] -name = "x86_vlapic" -version = "0.1.0" -source = "git+https://github.com/arceos-hypervisor/x86_vlapic?branch=aar-master#207fb0dfa10ff549b46a77f2d767483fad0c29f6" - -[[patch.unused]] -name = "riscv_vplic" -version = "0.2.2" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index ce57a49e0..d95fbd302 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -64,8 +64,8 @@ arm-gic-driver = {version = "0.15.5", features = ["rdif"]} [target.'cfg(target_arch = "riscv64")'.dependencies] axplat-riscv64-qemu-virt.workspace = true -riscv_vplic.workspace = true -riscv_vcpu.workspace = true +riscv_vplic = "0.2.2" +riscv_vcpu = "0.3" [build-dependencies] axconfig.workspace = true diff --git a/kernel/src/hal/mod.rs b/kernel/src/hal/mod.rs index 74e26d89f..96f08b927 100644 --- a/kernel/src/hal/mod.rs +++ b/kernel/src/hal/mod.rs @@ -14,17 +14,13 @@ use std::os::arceos::{ self, - modules::{axhal::percpu::this_cpu_id, axtask}, + modules::{axhal::percpu::this_cpu_id}, }; -use axerrno::{AxResult, ax_err_type}; -use memory_addr::PAGE_SIZE_4K; use page_table_multiarch::PagingHandler; - use arceos::modules::axhal; use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; use axvm::AxVMPerCpu; -use page_table_multiarch::PagingHandler; #[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/mod.rs")] #[cfg_attr(target_arch = "x86_64", path = "arch/x86_64/mod.rs")] From 912cd5632dd9f824f2861eefdc43c4382505a99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 13 Mar 2026 09:45:43 +0800 Subject: [PATCH 072/132] refactor: simplify xtask command and update dependencies feat: add target OS configuration for no_std environment fix: improve error handling for schema file writing --- Cargo.toml | 59 +++++++++++++++------------------------------- src/main.rs | 1 + xtask/src/tbuld.rs | 6 +++-- 3 files changed, 24 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index beb33782f..8274addf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ lto = true [[bin]] name = "xtask" path = "xtask/src/main.rs" -required-features = ["xtask"] [features] default = [] @@ -44,26 +43,6 @@ rk3588-clk = ["dep:rk3588-clk"] sdmmc = ["dep:sdmmc"] rockchip-pm = ["dep:rockchip-pm"] phytium-blk = ["dep:phytium-mci"] -xtask = [ - "dep:anyhow", - "dep:axvmconfig", - "dep:cargo_metadata", - "dep:chrono", - "dep:clap", - "dep:colored", - "dep:flate2", - "dep:jkconfig", - "dep:ostool", - "dep:regex", - "dep:reqwest", - "dep:schemars", - "dep:serde", - "dep:serde_json", - "dep:sha2", - "dep:tar", - "dep:tokio", - "dep:toml", -] [target.'cfg(not(any(windows, unix)))'.dependencies] bitflags = "2.2" @@ -138,27 +117,27 @@ axplat-x86-qemu-q35 = { version = "0.2.0", default-features = false, features = ] } axconfig = { version = "=0.3.0-preview.3", features = ["plat-dyn"] } -# xtask dependencies (only used when xtask feature is enabled) +# xtask dependencies (only used on host platforms) [target.'cfg(any(windows, unix))'.dependencies] -anyhow = { version = "1.0", optional = true } -cargo_metadata = { version = "0.23", optional = true } -chrono = { version = "0.4", features = ["serde"], optional = true } -clap = {version = "4.4", features = ["derive"], optional = true } -colored = { version = "3", optional = true } -flate2 = { version = "1.0", optional = true } -jkconfig = { version = "0.1", optional = true } -ostool = { version = "0.8.4", optional = true } -regex = { version = "1.12", optional = true } -reqwest = { version = "0.13", optional = true } -schemars = {version = "1", features = ["derive"], optional = true } -serde = {version = "1.0", features = ["derive"], optional = true } -serde_json = { version = "1", optional = true } -sha2 = { version = "0.10", optional = true } -tar = { version = "0.4", optional = true } -tokio = {version = "1", features = ["full"], optional = true } -toml = { version = "0.9", optional = true } +anyhow = { version = "1.0" } +cargo_metadata = { version = "0.23" } +chrono = { version = "0.4", features = ["serde"] } +clap = {version = "4.4", features = ["derive"] } +colored = { version = "3" } +flate2 = { version = "1.0" } +jkconfig = { version = "0.1" } +ostool = { version = "0.8.4" } +regex = { version = "1.12" } +reqwest = { version = "0.13" } +schemars = {version = "1", features = ["derive"] } +serde = {version = "1.0", features = ["derive"] } +serde_json = { version = "1" } +sha2 = { version = "0.10" } +tar = { version = "0.4" } +tokio = {version = "1", features = ["full"] } +toml = { version = "0.9" } -axvmconfig = { version = "0.2", features = ["std"], optional = true } +axvmconfig = { version = "0.2", features = ["std"] } [build-dependencies] axconfig = "=0.3.0-preview.3" diff --git a/src/main.rs b/src/main.rs index 99721034a..f9093226f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ #![no_std] #![no_main] #![feature(used_with_arg)] +#![cfg(target_os = "none")] #[macro_use] extern crate log; diff --git a/xtask/src/tbuld.rs b/xtask/src/tbuld.rs index 6b7f85b27..e2e5dc761 100644 --- a/xtask/src/tbuld.rs +++ b/xtask/src/tbuld.rs @@ -46,11 +46,13 @@ impl Context { config_path = c.clone(); } + let path = config_path.parent().unwrap().join(".build-schema.json"); + std::fs::write( - config_path.parent().unwrap().join(".build-schema.json"), + &path, serde_json::to_string_pretty(&json).unwrap(), ) - .with_context(|| "Failed to write schema file .build-schema.json")?; + .with_context(|| format!("Failed to write schema file: {}", path.display()))?; let config_str = std::fs::read_to_string(&config_path) .with_context(|| format!("Failed to read config file: {}", config_path.display()))?; From 3c347e8f014e3dc44558afece044d4025fd31208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 13 Mar 2026 10:01:40 +0800 Subject: [PATCH 073/132] refactor: simplify xtask command and improve config file handling --- .cargo/config.toml | 2 +- xtask/src/cargo.rs | 11 +++++------ xtask/src/tbuld.rs | 30 +++++++++++++++++++++++++----- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 867d1a6bc..d5bfb565f 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -21,4 +21,4 @@ rustflags = [ ] [alias] -xtask = "run --bin xtask --no-default-features --features xtask --" +xtask = "run --bin xtask --" diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index f5262e494..432f8837d 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -37,15 +37,14 @@ impl Context { } else { PathBuf::from(format!(".qemu-{arch:?}.toml").to_lowercase()) }; + let default_config_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("scripts") + .join("ostool") + .join(format!("qemu-{arch:?}.toml").to_lowercase()); // If the configuration file does not exist, copy from the default location if !config_path.exists() { - fs::copy( - PathBuf::from("scripts") - .join("ostool") - .join(format!("qemu-{arch:?}.toml").to_lowercase()), - &config_path, - )?; + fs::copy(&default_config_path, &config_path)?; } let kind = CargoRunnerKind::Qemu { diff --git a/xtask/src/tbuld.rs b/xtask/src/tbuld.rs index e2e5dc761..addc62e87 100644 --- a/xtask/src/tbuld.rs +++ b/xtask/src/tbuld.rs @@ -48,11 +48,8 @@ impl Context { let path = config_path.parent().unwrap().join(".build-schema.json"); - std::fs::write( - &path, - serde_json::to_string_pretty(&json).unwrap(), - ) - .with_context(|| format!("Failed to write schema file: {}", path.display()))?; + std::fs::write(&path, serde_json::to_string_pretty(&json).unwrap()) + .with_context(|| format!("Failed to write schema file: {}", path.display()))?; let config_str = std::fs::read_to_string(&config_path) .with_context(|| format!("Failed to read config file: {}", config_path.display()))?; @@ -93,6 +90,18 @@ impl Context { to_bin: config.to_bin, ..Default::default() }; + if cargo.target == "aarch64-unknown-none-softfloat" + && cargo.features.iter().any(|feature| feature == "dyn-plat") + { + // Dynamic-platform AArch64 builds link as PIE, so core/alloc must be + // rebuilt with the same PIC settings instead of using prebuilt std. + ensure_cargo_arg_pair(&mut cargo.args, "-Z", "build-std=core,alloc"); + ensure_cargo_arg_pair( + &mut cargo.args, + "-Z", + "build-std-features=compiler-builtins-mem", + ); + } cargo.args.extend(config.cargo_args); if let Some(smp) = config.smp { @@ -121,3 +130,14 @@ impl Context { Ok(()) } } + +fn ensure_cargo_arg_pair(args: &mut Vec, flag: &str, value: &str) { + if args + .windows(2) + .any(|window| window[0] == flag && window[1] == value) + { + return; + } + args.push(flag.to_string()); + args.push(value.to_string()); +} From 61ec188ba624c9e85558959bccb1338c3a5f8e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 13 Mar 2026 10:24:08 +0800 Subject: [PATCH 074/132] refactor: streamline configuration files and update cargo arguments for board targets --- .cargo/config.toml | 8 -------- configs/board/orangepi-5-plus.toml | 5 ++++- configs/board/phytiumpi.toml | 5 ++++- configs/board/qemu-aarch64.toml | 7 +++++-- configs/board/roc-rk3568-pc.toml | 5 ++++- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index d5bfb565f..0da14d5b2 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,14 +2,6 @@ # 使用系统 git 拉取依赖,可利用已配置的 git 凭证(如 gh auth、credential helper) git-fetch-with-cli = true -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-Crelocation-model=pic", - "-Clink-args=-pie", - "-Clink-args=-znostart-stop-gc", - "-Clink-args=-Taxplat.x", -] - [target.'cfg(target_os = "none")'] runner = "cargo osrun" diff --git a/configs/board/orangepi-5-plus.toml b/configs/board/orangepi-5-plus.toml index 21559ce51..465a3b29b 100644 --- a/configs/board/orangepi-5-plus.toml +++ b/configs/board/orangepi-5-plus.toml @@ -1,4 +1,7 @@ -cargo_args = [] +cargo_args = [ + "--config", + 'target.aarch64-unknown-none-softfloat.rustflags = ["-Crelocation-model=pic", "-Clink-args=-pie", "-Clink-args=-znostart-stop-gc", "-Clink-args=-Taxplat.x"]', +] features = [ # "ept-level-4", "dyn-plat", diff --git a/configs/board/phytiumpi.toml b/configs/board/phytiumpi.toml index 2fc970ca8..d55d74892 100644 --- a/configs/board/phytiumpi.toml +++ b/configs/board/phytiumpi.toml @@ -1,4 +1,7 @@ -cargo_args = [] +cargo_args = [ + "--config", + 'target.aarch64-unknown-none-softfloat.rustflags = ["-Crelocation-model=pic", "-Clink-args=-pie", "-Clink-args=-znostart-stop-gc", "-Clink-args=-Taxplat.x"]', +] features = [ # "ept-level-4", "dyn-plat", diff --git a/configs/board/qemu-aarch64.toml b/configs/board/qemu-aarch64.toml index c379cc79e..cfeacfd3d 100644 --- a/configs/board/qemu-aarch64.toml +++ b/configs/board/qemu-aarch64.toml @@ -1,4 +1,7 @@ -cargo_args = [] +cargo_args = [ + "--config", + 'target.aarch64-unknown-none-softfloat.rustflags = ["-Crelocation-model=pic", "-Clink-args=-pie", "-Clink-args=-znostart-stop-gc", "-Clink-args=-Taxplat.x"]', +] features = [ "ept-level-4", "axstd/bus-mmio", @@ -7,4 +10,4 @@ features = [ log = "Info" target = "aarch64-unknown-none-softfloat" to_bin = true -vm_configs = [] \ No newline at end of file +vm_configs = [] diff --git a/configs/board/roc-rk3568-pc.toml b/configs/board/roc-rk3568-pc.toml index 5ccf66336..9a40e5558 100644 --- a/configs/board/roc-rk3568-pc.toml +++ b/configs/board/roc-rk3568-pc.toml @@ -1,4 +1,7 @@ -cargo_args = [] +cargo_args = [ + "--config", + 'target.aarch64-unknown-none-softfloat.rustflags = ["-Crelocation-model=pic", "-Clink-args=-pie", "-Clink-args=-znostart-stop-gc", "-Clink-args=-Taxplat.x"]', +] features = [ # "ept-level-4", "dyn-plat", From 1aa5aab004ec2b2da088b8593531239a31786c19 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 15:13:26 +0800 Subject: [PATCH 075/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates (#384) --- .github/workflows/push.yml | 148 +++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..17cdc46a2 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,148 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - dev # 主仓库推送的目标分支 + - main # 兼容直接在子仓库开发的情况 + - ci + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From 23a922b449a5b802d2428af8bac525c0ed73d642 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 15:30:27 +0800 Subject: [PATCH 076/132] fix(ci): remove 'dev' and 'ci' branches from push trigger in workflow (#385) * feat(ci): add GitHub Actions workflow to notify parent repository on updates * fix(ci): remove 'dev' and 'ci' branches from push trigger in workflow --- .github/workflows/push.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 17cdc46a2..92bbcadea 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -47,9 +47,7 @@ name: Notify Parent Repository on: push: branches: - - dev # 主仓库推送的目标分支 - - main # 兼容直接在子仓库开发的情况 - - ci + - main workflow_dispatch: jobs: From 843bd78d7ce1494d760bf858e7d8e613e494b776 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 15:41:31 +0800 Subject: [PATCH 077/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 146 +++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..92bbcadea --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,146 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From 5984085f9347ca94d72e7aa0d1eb28ed5210c6ac Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 15:44:57 +0800 Subject: [PATCH 078/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 92bbcadea..a18de4a04 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -48,6 +48,7 @@ on: push: branches: - main + - master workflow_dispatch: jobs: From c3ee850d41399749579fa81d918dccf4025beeb8 Mon Sep 17 00:00:00 2001 From: ZCShou <72115@163.com> Date: Mon, 16 Mar 2026 15:46:36 +0800 Subject: [PATCH 079/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates (#35) --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From 396b8d45919c476acd624755aee29a386addfc5c Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 15:48:33 +0800 Subject: [PATCH 080/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From e9c2ea1a28f5e47484c947c4a77d3cf56618655d Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 15:52:56 +0800 Subject: [PATCH 081/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From dcc64c5e0f8e4a7c0d6896a8cf5395efa0e3632d Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 15:54:22 +0800 Subject: [PATCH 082/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From fa5eb0073c93b73175d82ea4e516740a1fd96332 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 15:55:45 +0800 Subject: [PATCH 083/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From c0af9a2c86ed4d1526e1c5555ec56418e504f351 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 15:57:23 +0800 Subject: [PATCH 084/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From ec02c6af9eb69809033c5408b47e66e5829e13d0 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 15:59:35 +0800 Subject: [PATCH 085/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From 6d3346edc10ce4d1d8fb6d39513a73be051bc42c Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 16:01:05 +0800 Subject: [PATCH 086/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From f5ca6574c497b6ea0c80cda95eff5f698cd5cc42 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 16:02:21 +0800 Subject: [PATCH 087/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From 75199e2a99f33ccc1d07134b4b2868da72fd0c99 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 16:08:59 +0800 Subject: [PATCH 088/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From d8b65a2a3ad23ed221e8e73e80bf25feed1f06ee Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 16:09:48 +0800 Subject: [PATCH 089/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From 56c09ebf3b598291740c35b3229a6c2f9c54a2e9 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 16:10:50 +0800 Subject: [PATCH 090/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From a48db63334f084838984d0512d677c5653c8edd9 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 16:12:05 +0800 Subject: [PATCH 091/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From bacf13fd552ec91ead1f652d3ea596fd87f2d4da Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 16:13:12 +0800 Subject: [PATCH 092/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From d5b0febb70446472e390997d6759345854ab912d Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 16:14:21 +0800 Subject: [PATCH 093/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From 48151a4847bf206c0fc9e2571dc8c215e369c112 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 16:15:19 +0800 Subject: [PATCH 094/132] feat(ci): add GitHub Actions workflow to notify parent repository on updates --- .github/workflows/push.yml | 147 +++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..a18de4a04 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,147 @@ +# ═══════════════════════════════════════════════════════════════════════════════ +# 组件仓库 GitHub Actions 配置模板 +# ═══════════════════════════════════════════════════════════════════════════════ +# +# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 +# +# 【使用步骤】 +# ───────────────────────────────────────────────────────────────────────────── +# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: +# cp scripts/push.yml <子仓库>/.github/workflows/push.yml +# +# 2. 在子仓库中配置 Secret: +# GitHub 仓库 → Settings → Secrets → Actions → New repository secret +# 名称: PARENT_REPO_TOKEN +# 值: 具有主仓库 repo 权限的 Personal Access Token +# +# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): +# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits +# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) +# +# 【Token 权限要求】 +# ───────────────────────────────────────────────────────────────────────────── +# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: +# - repo (Full control of private repositories) +# 或 +# - Fine-grained token: Contents (Read and Write) +# +# 【触发条件】 +# ───────────────────────────────────────────────────────────────────────────── +# - 自动触发:推送到 dev 或 main 分支时 +# - 手动触发:Actions → Notify Parent Repository → Run workflow +# +# 【工作流程】 +# ───────────────────────────────────────────────────────────────────────────── +# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull +# +# 【注意事项】 +# ───────────────────────────────────────────────────────────────────────────── +# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 +# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 +# +# ═══════════════════════════════════════════════════════════════════════════════ + +name: Notify Parent Repository + +# 当有新的推送时触发 +on: + push: + branches: + - main + - master + workflow_dispatch: + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Get repository info + id: repo + env: + GH_REPO_NAME: ${{ github.event.repository.name }} + GH_REF_NAME: ${{ github.ref_name }} + GH_SERVER_URL: ${{ github.server_url }} + GH_REPOSITORY: ${{ github.repository }} + run: | + # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 + COMPONENT="$GH_REPO_NAME" + BRANCH="$GH_REF_NAME" + # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list + REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" + + echo "component=${COMPONENT}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT + echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT + + echo "Component: ${COMPONENT}" + echo "Branch: ${BRANCH}" + echo "Repo URL: ${REPO_URL}" + + - name: Notify parent repository + env: + # ── 需要修改 ────────────────────────────────────────────────────────── + PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 + # ── 无需修改 ────────────────────────────────────────────────────────── + DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} + # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + GIT_ACTOR: ${{ github.actor }} + GIT_SHA: ${{ github.sha }} + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" + + # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 + PAYLOAD=$(jq -n \ + --arg component "$COMPONENT" \ + --arg branch "$BRANCH" \ + --arg repo_url "$REPO_URL" \ + --arg commit "$GIT_SHA" \ + --arg message "$COMMIT_MESSAGE" \ + --arg author "$GIT_ACTOR" \ + '{ + event_type: "subtree-update", + client_payload: { + component: $component, + branch: $branch, + repo_url: $repo_url, + commit: $commit, + message: $message, + author: $author + } + }') + + curl --fail --show-error -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Authorization: token ${DISPATCH_TOKEN}" \ + https://api.github.com/repos/${PARENT_REPO}/dispatches \ + -d "$PAYLOAD" + + echo "Notification sent successfully" + + - name: Create summary + env: + STEP_COMPONENT: ${{ steps.repo.outputs.component }} + STEP_BRANCH: ${{ steps.repo.outputs.branch }} + STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} + GIT_SHA: ${{ github.sha }} + GIT_ACTOR: ${{ github.actor }} + run: | + COMPONENT="$STEP_COMPONENT" + BRANCH="$STEP_BRANCH" + REPO_URL="$STEP_REPO_URL" + + echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY + echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY + echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY + echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY From c10f8b7b0d6625249915be29192a1f74022d52d7 Mon Sep 17 00:00:00 2001 From: ZCShou Date: Mon, 16 Mar 2026 16:19:57 +0800 Subject: [PATCH 095/132] fix(ci): Add master branch to push workflow --- .github/workflows/push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 92bbcadea..a18de4a04 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -48,6 +48,7 @@ on: push: branches: - main + - master workflow_dispatch: jobs: From 14d3cbcc4e1059d753ae99e18888d553804c9bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= <34859362+ZR233@users.noreply.github.com> Date: Tue, 17 Mar 2026 13:16:17 +0800 Subject: [PATCH 096/132] feat: add arceos tests (#6) * chore: update Cargo.lock and qemu-aarch64.toml for dependency adjustments * feat: Add QEMU runner and configuration for ArceOS - Implemented QEMU runner in `qemu.rs` to facilitate running ArceOS applications in QEMU. - Added board configuration files for AArch64, RISC-V, and x86_64 architectures. - Introduced `xtask` commands for building and running ArceOS applications. - Enhanced configuration loading to support command line arguments and board-specific settings. - Implemented default configuration setup for specified boards. - Updated dependencies in `Cargo.toml` for enhanced functionality. - Added tests for QEMU runner and configuration loading. * feat: add wait queue test suite for ArceOS * style: apply cargo fmt * refactor(axbuild): reuse ostool build and qemu runners * fix: simplify xtask alias in Cargo configuration * style: fix comment indentation in build.rs template * Refactor board configuration loading and QEMU runner integration - Updated `load_board_config` to use the new config module's board loader for backward compatibility. - Refactored `QemuRunner` to accept `manifest_dir` instead of `image_path` and `arceos_dir`, simplifying the constructor and related methods. - Removed redundant code and improved error handling in `run_defconfig` by leveraging `apply_defconfig`. - Enhanced configuration loading logic to prioritize command line arguments and board configurations. - Updated tests to reflect changes in the QEMU runner and configuration loading. - Cleaned up unused functions and imports in the configuration module. - Bumped `ostool` version in `Cargo.lock` to 0.8.13. * feat: add support for ArceOS targets and improve configuration management * feat: enhance ArceOS configuration with SMP support and QEMU integration * fix: update .gitignore to include axconfig wildcard and modify NUM_TASKS constant in main.rs * fix: prevent negative initialization value in TimeIf implementation * fix std test flakiness in scope-local and smoltcp doctest * feat: Introduce AxContext and AxBuild for improved configuration management - Added AxContext struct to encapsulate application context including configuration, manifest directory, and QEMU configuration path. - Implemented AxBuild struct to facilitate building and running applications with QEMU. - Refactored existing code to utilize AxContext and AxBuild, enhancing modularity and readability. - Removed deprecated board configuration files for QEMU (aarch64, riscv64, x86_64) as they are no longer needed. - Updated build and run commands to leverage new AxBuild structure, simplifying the command flow. - Adjusted tests and configuration overrides to align with the new context management approach. * fix: update Cargo.toml to include axerrno patch and clean up unused code in ostool * fix: update ostool to improve platform handling and cargo argument construction * refactor: improve test cases for config loading and cleanup functionality * fix: update axbuild dependency path and remove unnecessary features * refactor: rename log level conversion method and improve formatting in config handling --- configs/board/qemu-aarch64.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configs/board/qemu-aarch64.toml b/configs/board/qemu-aarch64.toml index cfeacfd3d..77eeb6781 100644 --- a/configs/board/qemu-aarch64.toml +++ b/configs/board/qemu-aarch64.toml @@ -2,6 +2,10 @@ cargo_args = [ "--config", 'target.aarch64-unknown-none-softfloat.rustflags = ["-Crelocation-model=pic", "-Clink-args=-pie", "-Clink-args=-znostart-stop-gc", "-Clink-args=-Taxplat.x"]', ] +# cargo_args = [ +# "--config", +# 'target.aarch64-unknown-none-softfloat.rustflags = ["-Clink-args=-Taxplat.x"]', +# ] features = [ "ept-level-4", "axstd/bus-mmio", From bae41c760826bb52bbecb5a1a0eee43aadd2e0ff Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 08:44:51 +0800 Subject: [PATCH 097/132] ci: migrate to shared workflow axci and add integration tests --- .github/config.json | 16 +++ .github/workflows/check.yml | 15 +++ .github/workflows/ci.yml | 55 --------- .github/workflows/deploy.yml | 16 +++ .github/workflows/release.yml | 19 ++++ .github/workflows/test.yml | 17 +++ .gitignore | 10 +- LICENSE | 201 +++++++++++++++++++++++++++++++++ README.md | 84 ++++++++++++++ scripts/check.sh | 35 ++++++ scripts/test.sh | 34 ++++++ tests/operation_type_tests.rs | 63 +++++++++++ tests/registers_type_tests.rs | 66 +++++++++++ tests/system_reg_type_tests.rs | 56 +++++++++ 14 files changed, 631 insertions(+), 56 deletions(-) create mode 100644 .github/config.json create mode 100644 .github/workflows/check.yml delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 LICENSE create mode 100755 scripts/check.sh create mode 100755 scripts/test.sh create mode 100644 tests/operation_type_tests.rs create mode 100644 tests/registers_type_tests.rs create mode 100644 tests/system_reg_type_tests.rs diff --git a/.github/config.json b/.github/config.json new file mode 100644 index 000000000..7a7079d23 --- /dev/null +++ b/.github/config.json @@ -0,0 +1,16 @@ +{ + "component": { + "name": "aarch64_sysreg", + "crate_name": "aarch64_sysreg" + }, + "targets": [ + "aarch64-unknown-none-softfloat" + ], + "unit_test_targets": [], + "test_targets": [], + "rust_components": [ + "rust-src", + "clippy", + "rustfmt" + ] +} \ No newline at end of file diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 000000000..0207ed160 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,15 @@ +# Quality Check Workflow +# References shared workflow from axci + +name: Check + +on: + push: + branches: ['**'] + tags-ignore: ['**'] + pull_request: + workflow_dispatch: + +jobs: + check: + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index b72f04c5c..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: CI - -on: [push, pull_request] - -jobs: - ci: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - rust-toolchain: [nightly] - targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat] - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: ${{ matrix.rust-toolchain }} - components: rust-src, clippy, rustfmt - targets: ${{ matrix.targets }} - - name: Check rust version - run: rustc --version --verbose - - name: Check code format - run: cargo fmt --all -- --check - - name: Clippy - run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default - - name: Build - run: cargo build --target ${{ matrix.targets }} --all-features - - name: Unit test - if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} - run: cargo test --target ${{ matrix.targets }} -- --nocapture - - doc: - runs-on: ubuntu-latest - strategy: - fail-fast: false - permissions: - contents: write - env: - default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - - name: Build docs - continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - name: Deploy to Github Pages - if: ${{ github.ref == env.default-branch }} - uses: JamesIves/github-pages-deploy-action@v4 - with: - single-commit: true - branch: gh-pages - folder: target/doc \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..c28115ab9 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,16 @@ +# Deploy Workflow +# References shared workflow from axci + +name: Deploy + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + deploy: + uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main + with: + verify_branch: true + verify_version: true \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..40986f163 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,19 @@ +# Release Workflow +# References shared workflow from axci + +name: Release + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' + +jobs: + release: + uses: arceos-hypervisor/axci/.github/workflows/release.yml@main + with: + verify_branch: true + verify_version: true + secrets: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..dc91940d4 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,17 @@ +# Integration Test Workflow +# References shared workflow from axci + +name: Test + +on: + push: + branches: + - '**' + tags-ignore: + - '**' + pull_request: + workflow_dispatch: + +jobs: + test: + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main \ No newline at end of file diff --git a/.gitignore b/.gitignore index d478c630d..f639050ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,12 @@ /target /.vscode .DS_Store -Cargo.lock \ No newline at end of file +Cargo.lock + +# Test results (generated by shared test framework) +/test-results/ +/test_repos/ +*.log + +# Downloaded test framework +/scripts/.axci/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..15e431c22 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index e69de29bb..2f839e8a5 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,84 @@ +# aarch64_sysreg + +[![Crates.io](https://img.shields.io/crates/v/aarch64_sysreg.svg)](https://crates.io/crates/aarch64_sysreg) +[![Docs.rs](https://docs.rs/aarch64_sysreg/badge.svg)](https://docs.rs/aarch64_sysreg) +[![License](https://img.shields.io/badge/license-GPL--3.0%2FApache--2.0%2FMulanPSL--2.0-blue.svg)](LICENSE) + +AArch64 系统寄存器类型定义库,提供 ARM64 架构中操作类型、寄存器类型和系统寄存器的枚举定义。 + +## 特性 + +- `#![no_std]` - 可在裸机环境使用 +- `OperationType` - AArch64 指令操作类型枚举 +- `RegistersType` - 通用寄存器类型枚举 (W/X/V/B/H/S/D/Q 寄存器等) +- `SystemRegType` - 系统寄存器类型枚举 + +## 安装 + +在 `Cargo.toml` 中添加: + +```toml +[dependencies] +aarch64_sysreg = "0.1" +``` + +## 使用示例 + +```rust +use aarch64_sysreg::{OperationType, RegistersType, SystemRegType}; + +fn main() { + // 操作类型 + let op = OperationType::ADD; + println!("Operation: {}", op); // ADD + println!("Value: 0x{:x}", op); // 0x6 + + // 从数值转换 + let op_from = OperationType::from(0x6); + assert_eq!(op_from, OperationType::ADD); + + // 寄存器类型 + let reg = RegistersType::X0; + println!("Register: {}", reg); // X0 + + // 系统寄存器 + let sys_reg = SystemRegType::MDSCR_EL1; + println!("System Register: {}", sys_reg); // MDSCR_EL1 +} +``` + +## 类型说明 + +### OperationType + +定义 AArch64 指令操作类型,包括: +- 算术运算: `ADD`, `SUB`, `MUL`, `DIV` 等 +- 逻辑运算: `AND`, `ORR`, `EOR`, `BIC` 等 +- 分支指令: `B`, `BL`, `BR`, `RET` 等 +- 加载存储: `LDR`, `STR`, `LDP`, `STP` 等 +- 系统指令: `MSR`, `MRS`, `SVC`, `HVC` 等 + +### RegistersType + +定义 AArch64 通用和向量寄存器: +- 32位通用寄存器: `W0` - `W30`, `WZR`, `WSP` +- 64位通用寄存器: `X0` - `X30`, `XZR`, `SP` +- 向量寄存器: `V0` - `V31`, `B0` - `B31`, `H0` - `H31`, `S0` - `S31`, `D0` - `D31`, `Q0` - `Q31` +- SVE 寄存器: `Z0` - `Z31`, `P0` - `P15` + +### SystemRegType + +定义 AArch64 系统寄存器,编号格式为 `000000`: +- 调试寄存器: `DBGBCR*_EL1`, `DBGBVR*_EL1` 等 +- 跟踪寄存器: `TRC*` 系列 +- 性能寄存器: `PMEVCNTR*_EL0`, `PMEVTYPER*_EL0` 等 +- 系统控制寄存器: `SCTLR_EL1`, `TTBR*_EL1` 等 + +## 许可证 + +本项目采用以下许可证之一: +- GPL-3.0-or-later +- Apache-2.0 +- MulanPSL-2.0 + +详见 [LICENSE](LICENSE) 文件。 diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 000000000..e1da652f2 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# aarch64_sysreg 代码检查脚本 +# 下载并调用 axci 仓库中的检查脚本 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +COMPONENT_NAME="$(basename "$COMPONENT_DIR")" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet -b ndev "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行检查 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/check.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" \ No newline at end of file diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 000000000..5de52ed07 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# +# aarch64_sysreg 测试脚本 +# 下载并调用 axci 仓库中的测试框架 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet -b ndev "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行测试,自动指定当前组件 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/tests.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" \ No newline at end of file diff --git a/tests/operation_type_tests.rs b/tests/operation_type_tests.rs new file mode 100644 index 000000000..041165266 --- /dev/null +++ b/tests/operation_type_tests.rs @@ -0,0 +1,63 @@ +use aarch64_sysreg::OperationType; + +#[test] +fn test_operation_type_values() { + assert_eq!(OperationType::ERROR as usize, 0x0); + assert_eq!(OperationType::ABS as usize, 0x1); + assert_eq!(OperationType::ADD as usize, 0x6); + assert_eq!(OperationType::SUB as usize, 0x3d8); + assert_eq!(OperationType::ZIP2 as usize, 0x49f); +} + +#[test] +fn test_operation_type_display() { + assert_eq!(format!("{}", OperationType::ERROR), "ERROR"); + assert_eq!(format!("{}", OperationType::ADD), "ADD"); + assert_eq!(format!("{}", OperationType::SUB), "SUB"); + assert_eq!(format!("{}", OperationType::MUL), "MUL"); + assert_eq!(format!("{}", OperationType::RET), "RET"); + assert_eq!(format!("{}", OperationType::BL), "BL"); +} + +#[test] +fn test_operation_type_lower_hex() { + assert_eq!(format!("{:x}", OperationType::ADD), "6"); + assert_eq!(format!("{:x}", OperationType::SUB), "3d8"); + assert_eq!(format!("{:x}", OperationType::ERROR), "0"); +} + +#[test] +fn test_operation_type_upper_hex() { + assert_eq!(format!("{:X}", OperationType::ADD), "6"); + assert_eq!(format!("{:X}", OperationType::SUB), "3D8"); + assert_eq!(format!("{:X}", OperationType::ERROR), "0"); +} + +#[test] +fn test_operation_type_from_usize() { + assert_eq!(OperationType::from(0x0), OperationType::ERROR); + assert_eq!(OperationType::from(0x1), OperationType::ABS); + assert_eq!(OperationType::from(0x6), OperationType::ADD); + assert_eq!(OperationType::from(0x3d8), OperationType::SUB); +} + +#[test] +#[should_panic(expected = "Invalid arm64 operation value")] +fn test_operation_type_from_invalid() { + let _ = OperationType::from(0xFFFF); +} + +#[test] +fn test_operation_type_clone_copy() { + let op = OperationType::ADD; + let op_clone = op.clone(); + let op_copy = op; + assert_eq!(op, op_clone); + assert_eq!(op, op_copy); +} + +#[test] +fn test_operation_type_partial_eq() { + assert_eq!(OperationType::ADD, OperationType::ADD); + assert_ne!(OperationType::ADD, OperationType::SUB); +} diff --git a/tests/registers_type_tests.rs b/tests/registers_type_tests.rs new file mode 100644 index 000000000..fff63b8a3 --- /dev/null +++ b/tests/registers_type_tests.rs @@ -0,0 +1,66 @@ +use aarch64_sysreg::RegistersType; + +#[test] +fn test_registers_type_values() { + assert_eq!(RegistersType::NONE as usize, 0x0); + assert_eq!(RegistersType::W0 as usize, 0x1); + assert_eq!(RegistersType::X0 as usize, 0x22); + assert_eq!(RegistersType::SP as usize, 0x42); + assert_eq!(RegistersType::V0 as usize, 0x43); + assert_eq!(RegistersType::Q0 as usize, 0xe3); +} + +#[test] +fn test_registers_type_display() { + assert_eq!(format!("{}", RegistersType::NONE), "NONE"); + assert_eq!(format!("{}", RegistersType::W0), "W0"); + assert_eq!(format!("{}", RegistersType::X0), "X0"); + assert_eq!(format!("{}", RegistersType::XZR), "XZR"); + assert_eq!(format!("{}", RegistersType::WZR), "WZR"); + assert_eq!(format!("{}", RegistersType::SP), "SP"); + assert_eq!(format!("{}", RegistersType::WSP), "WSP"); +} + +#[test] +fn test_registers_type_lower_hex() { + assert_eq!(format!("{:x}", RegistersType::W0), "1"); + assert_eq!(format!("{:x}", RegistersType::X0), "22"); + assert_eq!(format!("{:x}", RegistersType::SP), "42"); +} + +#[test] +fn test_registers_type_upper_hex() { + assert_eq!(format!("{:X}", RegistersType::W0), "1"); + assert_eq!(format!("{:X}", RegistersType::X0), "22"); + assert_eq!(format!("{:X}", RegistersType::Q0), "E3"); +} + +#[test] +fn test_registers_type_from_usize() { + assert_eq!(RegistersType::from(0x0), RegistersType::NONE); + assert_eq!(RegistersType::from(0x1), RegistersType::W0); + assert_eq!(RegistersType::from(0x22), RegistersType::X0); + assert_eq!(RegistersType::from(0x42), RegistersType::SP); +} + +#[test] +#[should_panic(expected = "Invalid register value")] +fn test_registers_type_from_invalid() { + let _ = RegistersType::from(0xFFFF); +} + +#[test] +fn test_registers_type_clone_copy() { + let reg = RegistersType::X0; + let reg_clone = reg.clone(); + let reg_copy = reg; + assert_eq!(reg, reg_clone); + assert_eq!(reg, reg_copy); +} + +#[test] +fn test_registers_type_partial_eq() { + assert_eq!(RegistersType::X0, RegistersType::X0); + assert_ne!(RegistersType::X0, RegistersType::X1); + assert_ne!(RegistersType::W0, RegistersType::X0); +} diff --git a/tests/system_reg_type_tests.rs b/tests/system_reg_type_tests.rs new file mode 100644 index 000000000..03b0389c5 --- /dev/null +++ b/tests/system_reg_type_tests.rs @@ -0,0 +1,56 @@ +use aarch64_sysreg::SystemRegType; + +#[test] +fn test_system_reg_type_values() { + assert_eq!(SystemRegType::OSDTRRX_EL1 as usize, 0x240000); + assert_eq!(SystemRegType::DBGBVR0_EL1 as usize, 0x280000); + assert_eq!(SystemRegType::MDSCR_EL1 as usize, 0x240004); +} + +#[test] +fn test_system_reg_type_display() { + assert_eq!(format!("{}", SystemRegType::OSDTRRX_EL1), "OSDTRRX_EL1"); + assert_eq!(format!("{}", SystemRegType::DBGBVR0_EL1), "DBGBVR0_EL1"); + assert_eq!(format!("{}", SystemRegType::MDSCR_EL1), "MDSCR_EL1"); + assert_eq!(format!("{}", SystemRegType::PSTATE_SPSEL), "PSTATE_SPSEL"); +} + +#[test] +fn test_system_reg_type_lower_hex() { + assert_eq!(format!("{:x}", SystemRegType::OSDTRRX_EL1), "240000"); + assert_eq!(format!("{:x}", SystemRegType::DBGBVR0_EL1), "280000"); +} + +#[test] +fn test_system_reg_type_upper_hex() { + assert_eq!(format!("{:X}", SystemRegType::OSDTRRX_EL1), "240000"); + assert_eq!(format!("{:X}", SystemRegType::DBGBVR0_EL1), "280000"); +} + +#[test] +fn test_system_reg_type_from_usize() { + assert_eq!(SystemRegType::from(0x240000), SystemRegType::OSDTRRX_EL1); + assert_eq!(SystemRegType::from(0x280000), SystemRegType::DBGBVR0_EL1); + assert_eq!(SystemRegType::from(0x240004), SystemRegType::MDSCR_EL1); +} + +#[test] +#[should_panic(expected = "Invalid system register value")] +fn test_system_reg_type_from_invalid() { + let _ = SystemRegType::from(0xFFFFFF); +} + +#[test] +fn test_system_reg_type_clone_copy() { + let reg = SystemRegType::MDSCR_EL1; + let reg_clone = reg.clone(); + let reg_copy = reg; + assert_eq!(reg, reg_clone); + assert_eq!(reg, reg_copy); +} + +#[test] +fn test_system_reg_type_partial_eq() { + assert_eq!(SystemRegType::MDSCR_EL1, SystemRegType::MDSCR_EL1); + assert_ne!(SystemRegType::MDSCR_EL1, SystemRegType::OSDTRRX_EL1); +} From a5d2215b30d762ffabff7a6c53e465c2a414770f Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 08:52:05 +0800 Subject: [PATCH 098/132] ci: remove empty test_targets config --- .github/config.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/config.json b/.github/config.json index 7a7079d23..f92ce4448 100644 --- a/.github/config.json +++ b/.github/config.json @@ -7,7 +7,6 @@ "aarch64-unknown-none-softfloat" ], "unit_test_targets": [], - "test_targets": [], "rust_components": [ "rust-src", "clippy", From 42a9b52745de204748b7bb836800c5d4fea6599c Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 09:28:37 +0800 Subject: [PATCH 099/132] ci: enable unit tests for aarch64 target --- .github/config.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/config.json b/.github/config.json index f92ce4448..f1cceabb3 100644 --- a/.github/config.json +++ b/.github/config.json @@ -6,7 +6,9 @@ "targets": [ "aarch64-unknown-none-softfloat" ], - "unit_test_targets": [], + "unit_test_targets": [ + "aarch64-unknown-none-softfloat" + ], "rust_components": [ "rust-src", "clippy", From 597cf7066a55997c4f30e5cc4d945ce8bb5bfa08 Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 09:45:27 +0800 Subject: [PATCH 100/132] ci: switch unit test target to linux-gnu and use ndev workflow --- .github/config.json | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/config.json b/.github/config.json index f1cceabb3..715af2bd3 100644 --- a/.github/config.json +++ b/.github/config.json @@ -7,7 +7,7 @@ "aarch64-unknown-none-softfloat" ], "unit_test_targets": [ - "aarch64-unknown-none-softfloat" + "aarch64-unknown-linux-gnu" ], "rust_components": [ "rust-src", diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc91940d4..67d307b16 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,4 +14,4 @@ on: jobs: test: - uses: arceos-hypervisor/axci/.github/workflows/test.yml@main \ No newline at end of file + uses: arceos-hypervisor/axci/.github/workflows/test.yml@ndev \ No newline at end of file From 2cd8dd26e4350621a544ca409ed0fdcd6d745fa6 Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 09:54:23 +0800 Subject: [PATCH 101/132] chore: add .claude to .gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f639050ff..d7c96c4a7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ Cargo.lock *.log # Downloaded test framework -/scripts/.axci/ \ No newline at end of file +/scripts/.axci/ + +.claude \ No newline at end of file From 7aa1788ef08c8945cdceef3b5fec43219d96eaa1 Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 10:55:31 +0800 Subject: [PATCH 102/132] ci: switch unit test target to x86_64-linux-gnu --- .github/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/config.json b/.github/config.json index 715af2bd3..b56dee791 100644 --- a/.github/config.json +++ b/.github/config.json @@ -7,7 +7,7 @@ "aarch64-unknown-none-softfloat" ], "unit_test_targets": [ - "aarch64-unknown-linux-gnu" + "x86_64-unknown-linux-gnu" ], "rust_components": [ "rust-src", From a6ddbcef64e49b5215a39d9d5e8766b5868a24d6 Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 12:24:21 +0800 Subject: [PATCH 103/132] chore: add newline at end of file --- scripts/check.sh | 2 +- scripts/test.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/check.sh b/scripts/check.sh index e1da652f2..e550a248e 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -32,4 +32,4 @@ main() { exec bash "$AXCI_DIR/check.sh" --component-dir "$COMPONENT_DIR" "$@" } -main "$@" \ No newline at end of file +main "$@" diff --git a/scripts/test.sh b/scripts/test.sh index 5de52ed07..9bbbf8950 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -31,4 +31,4 @@ main() { exec bash "$AXCI_DIR/tests.sh" --component-dir "$COMPONENT_DIR" "$@" } -main "$@" \ No newline at end of file +main "$@" From 43bfff8afe7bc2b72a7f87edf27d749d0517da60 Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 14:44:29 +0800 Subject: [PATCH 104/132] ci: switch shared workflows and branch to main --- .github/workflows/push.yml | 145 ++----------------------------------- .github/workflows/test.yml | 2 +- scripts/check.sh | 2 +- scripts/test.sh | 2 +- 4 files changed, 10 insertions(+), 141 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a18de4a04..3ff391a4a 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,147 +1,16 @@ -# ═══════════════════════════════════════════════════════════════════════════════ -# 组件仓库 GitHub Actions 配置模板 -# ═══════════════════════════════════════════════════════════════════════════════ -# -# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 -# -# 【使用步骤】 -# ───────────────────────────────────────────────────────────────────────────── -# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: -# cp scripts/push.yml <子仓库>/.github/workflows/push.yml -# -# 2. 在子仓库中配置 Secret: -# GitHub 仓库 → Settings → Secrets → Actions → New repository secret -# 名称: PARENT_REPO_TOKEN -# 值: 具有主仓库 repo 权限的 Personal Access Token -# -# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): -# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits -# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) -# -# 【Token 权限要求】 -# ───────────────────────────────────────────────────────────────────────────── -# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: -# - repo (Full control of private repositories) -# 或 -# - Fine-grained token: Contents (Read and Write) -# -# 【触发条件】 -# ───────────────────────────────────────────────────────────────────────────── -# - 自动触发:推送到 dev 或 main 分支时 -# - 手动触发:Actions → Notify Parent Repository → Run workflow -# -# 【工作流程】 -# ───────────────────────────────────────────────────────────────────────────── -# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull -# -# 【注意事项】 -# ───────────────────────────────────────────────────────────────────────────── -# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 -# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 -# -# ═══════════════════════════════════════════════════════════════════════════════ - name: Notify Parent Repository -# 当有新的推送时触发 on: push: branches: - main - - master + - zcs workflow_dispatch: jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Get repository info - id: repo - env: - GH_REPO_NAME: ${{ github.event.repository.name }} - GH_REF_NAME: ${{ github.ref_name }} - GH_SERVER_URL: ${{ github.server_url }} - GH_REPOSITORY: ${{ github.repository }} - run: | - # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 - COMPONENT="$GH_REPO_NAME" - BRANCH="$GH_REF_NAME" - # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list - REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" - - echo "component=${COMPONENT}" >> $GITHUB_OUTPUT - echo "branch=${BRANCH}" >> $GITHUB_OUTPUT - echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT - - echo "Component: ${COMPONENT}" - echo "Branch: ${BRANCH}" - echo "Repo URL: ${REPO_URL}" - - - name: Notify parent repository - env: - # ── 需要修改 ────────────────────────────────────────────────────────── - PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 - # ── 无需修改 ────────────────────────────────────────────────────────── - DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} - # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 - COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - GIT_ACTOR: ${{ github.actor }} - GIT_SHA: ${{ github.sha }} - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" - - # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 - PAYLOAD=$(jq -n \ - --arg component "$COMPONENT" \ - --arg branch "$BRANCH" \ - --arg repo_url "$REPO_URL" \ - --arg commit "$GIT_SHA" \ - --arg message "$COMMIT_MESSAGE" \ - --arg author "$GIT_ACTOR" \ - '{ - event_type: "subtree-update", - client_payload: { - component: $component, - branch: $branch, - repo_url: $repo_url, - commit: $commit, - message: $message, - author: $author - } - }') - - curl --fail --show-error -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${DISPATCH_TOKEN}" \ - https://api.github.com/repos/${PARENT_REPO}/dispatches \ - -d "$PAYLOAD" - - echo "Notification sent successfully" - - - name: Create summary - env: - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - GIT_SHA: ${{ github.sha }} - GIT_ACTOR: ${{ github.actor }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY - echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY + notify-parent: + name: Notify Parent Repository + # 调用 axci 仓库的可复用工作流 + uses: arceos-hypervisor/axci/.github/workflows/push.yml@main + secrets: + PARENT_REPO_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 67d307b16..dc91940d4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,4 +14,4 @@ on: jobs: test: - uses: arceos-hypervisor/axci/.github/workflows/test.yml@ndev \ No newline at end of file + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main \ No newline at end of file diff --git a/scripts/check.sh b/scripts/check.sh index e550a248e..8c95f0757 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -19,7 +19,7 @@ download_axci() { cd "$AXCI_DIR" && git pull --quiet else echo "Downloading axci repository..." - git clone --quiet -b ndev "$AXCI_REPO" "$AXCI_DIR" + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" fi } diff --git a/scripts/test.sh b/scripts/test.sh index 9bbbf8950..751cb0ef1 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -18,7 +18,7 @@ download_axci() { cd "$AXCI_DIR" && git pull --quiet else echo "Downloading axci repository..." - git clone --quiet -b ndev "$AXCI_REPO" "$AXCI_DIR" + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" fi } From 466662882861e2c495bfd1e9e4836ce18a70fb3a Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 15:13:27 +0800 Subject: [PATCH 105/132] chore: update license to Apache-2.0 only --- .github/workflows/release.yml | 10 +++++++++- Cargo.toml | 2 +- README.md | 9 ++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 40986f163..20f186395 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,5 +1,6 @@ # Release Workflow # References shared workflow from axci +# check + test must pass before release name: Release @@ -10,10 +11,17 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' jobs: + check: + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main + + test: + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main + release: + needs: [check, test] uses: arceos-hypervisor/axci/.github/workflows/release.yml@main with: verify_branch: true verify_version: true secrets: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index 77405edf6..75063ba92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.1" edition = "2021" authors = ["Debin "] description = "Address translation of system registers" -license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0" +license = "Apache-2.0" homepage = "https://github.com/arceos-org/arceos" repository = "https://github.com/arceos-org/aarch64_sysreg" documentation = "https://docs.rs/aarch64_sysreg" diff --git a/README.md b/README.md index 2f839e8a5..ce46168e4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Crates.io](https://img.shields.io/crates/v/aarch64_sysreg.svg)](https://crates.io/crates/aarch64_sysreg) [![Docs.rs](https://docs.rs/aarch64_sysreg/badge.svg)](https://docs.rs/aarch64_sysreg) -[![License](https://img.shields.io/badge/license-GPL--3.0%2FApache--2.0%2FMulanPSL--2.0-blue.svg)](LICENSE) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) AArch64 系统寄存器类型定义库,提供 ARM64 架构中操作类型、寄存器类型和系统寄存器的枚举定义。 @@ -76,9 +76,4 @@ fn main() { ## 许可证 -本项目采用以下许可证之一: -- GPL-3.0-or-later -- Apache-2.0 -- MulanPSL-2.0 - -详见 [LICENSE](LICENSE) 文件。 +本项目采用 Apache-2.0 许可证。详见 [LICENSE](LICENSE) 文件。 From 22577b29ec48f4eefa6fdd0dd043fc691ba01f7f Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 17:52:58 +0800 Subject: [PATCH 106/132] docs: improve README formatting and add Chinese translation --- README.md | 99 ++++++++++++++++++++++++++++++---------------- README_CN.md | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 34 deletions(-) create mode 100644 README_CN.md diff --git a/README.md b/README.md index ce46168e4..b48c83550 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,110 @@ -# aarch64_sysreg +

aarch64_sysreg

+

AArch64 System Register Type Definitions

+ +
[![Crates.io](https://img.shields.io/crates/v/aarch64_sysreg.svg)](https://crates.io/crates/aarch64_sysreg) [![Docs.rs](https://docs.rs/aarch64_sysreg/badge.svg)](https://docs.rs/aarch64_sysreg) -[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) +[![Rust](https://img.shields.io/badge/edition-2021-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-org/aarch64_sysreg/blob/main/LICENSE) +
+ +English | [中文](README_CN.md) -AArch64 系统寄存器类型定义库,提供 ARM64 架构中操作类型、寄存器类型和系统寄存器的枚举定义。 +# Introduction -## 特性 +A library providing type definitions for AArch64 system registers, including operation types, register types, and system register enumerations for the ARM64 architecture. -- `#![no_std]` - 可在裸机环境使用 -- `OperationType` - AArch64 指令操作类型枚举 -- `RegistersType` - 通用寄存器类型枚举 (W/X/V/B/H/S/D/Q 寄存器等) -- `SystemRegType` - 系统寄存器类型枚举 +## Features -## 安装 +- `#![no_std]` - Compatible with bare-metal environments +- `OperationType` - AArch64 instruction operation type enumeration +- `RegistersType` - General register type enumeration (W/X/V/B/H/S/D/Q registers, etc.) +- `SystemRegType` - System register type enumeration -在 `Cargo.toml` 中添加: +## Installation + +Add to your `Cargo.toml`: ```toml [dependencies] aarch64_sysreg = "0.1" ``` -## 使用示例 +## Usage ```rust use aarch64_sysreg::{OperationType, RegistersType, SystemRegType}; fn main() { - // 操作类型 + // Operation type let op = OperationType::ADD; println!("Operation: {}", op); // ADD println!("Value: 0x{:x}", op); // 0x6 - // 从数值转换 + // Convert from value let op_from = OperationType::from(0x6); assert_eq!(op_from, OperationType::ADD); - // 寄存器类型 + // Register type let reg = RegistersType::X0; println!("Register: {}", reg); // X0 - // 系统寄存器 + // System register let sys_reg = SystemRegType::MDSCR_EL1; println!("System Register: {}", sys_reg); // MDSCR_EL1 } ``` -## 类型说明 +## Type Reference ### OperationType -定义 AArch64 指令操作类型,包括: -- 算术运算: `ADD`, `SUB`, `MUL`, `DIV` 等 -- 逻辑运算: `AND`, `ORR`, `EOR`, `BIC` 等 -- 分支指令: `B`, `BL`, `BR`, `RET` 等 -- 加载存储: `LDR`, `STR`, `LDP`, `STP` 等 -- 系统指令: `MSR`, `MRS`, `SVC`, `HVC` 等 +Defines AArch64 instruction operation types, including: +- Arithmetic: `ADD`, `SUB`, `MUL`, `DIV`, etc. +- Logical: `AND`, `ORR`, `EOR`, `BIC`, etc. +- Branch: `B`, `BL`, `BR`, `RET`, etc. +- Load/Store: `LDR`, `STR`, `LDP`, `STP`, etc. +- System: `MSR`, `MRS`, `SVC`, `HVC`, etc. ### RegistersType -定义 AArch64 通用和向量寄存器: -- 32位通用寄存器: `W0` - `W30`, `WZR`, `WSP` -- 64位通用寄存器: `X0` - `X30`, `XZR`, `SP` -- 向量寄存器: `V0` - `V31`, `B0` - `B31`, `H0` - `H31`, `S0` - `S31`, `D0` - `D31`, `Q0` - `Q31` -- SVE 寄存器: `Z0` - `Z31`, `P0` - `P15` +Defines AArch64 general-purpose and vector registers: +- 32-bit GPR: `W0` - `W30`, `WZR`, `WSP` +- 64-bit GPR: `X0` - `X30`, `XZR`, `SP` +- Vector registers: `V0` - `V31`, `B0` - `B31`, `H0` - `H31`, `S0` - `S31`, `D0` - `D31`, `Q0` - `Q31` +- SVE registers: `Z0` - `Z31`, `P0` - `P15` ### SystemRegType -定义 AArch64 系统寄存器,编号格式为 `000000`: -- 调试寄存器: `DBGBCR*_EL1`, `DBGBVR*_EL1` 等 -- 跟踪寄存器: `TRC*` 系列 -- 性能寄存器: `PMEVCNTR*_EL0`, `PMEVTYPER*_EL0` 等 -- 系统控制寄存器: `SCTLR_EL1`, `TTBR*_EL1` 等 +Defines AArch64 system registers with encoding format `000000`: +- Debug registers: `DBGBCR*_EL1`, `DBGBVR*_EL1`, etc. +- Trace registers: `TRC*` series +- Performance registers: `PMEVCNTR*_EL0`, `PMEVTYPER*_EL0`, etc. +- System control registers: `SCTLR_EL1`, `TTBR*_EL1`, etc. + +# Verification + +Run local tests quickly: + +```bash +./scripts/test.sh +``` + +# Documentation + +## API Documentation + +```bash +cargo doc --no-deps --open +``` + +# Contributing + +1. Run local check: `./scripts/check.sh` +2. Run local tests: `./scripts/test.sh` +3. Submit PR and pass CI checks -## 许可证 +# License -本项目采用 Apache-2.0 许可证。详见 [LICENSE](LICENSE) 文件。 +Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details. diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 000000000..c4b9873ba --- /dev/null +++ b/README_CN.md @@ -0,0 +1,110 @@ +

aarch64_sysreg

+ +

AArch64 系统寄存器类型定义库

+ +
+[![Crates.io](https://img.shields.io/crates/v/aarch64_sysreg.svg)](https://crates.io/crates/aarch64_sysreg) +[![Docs.rs](https://docs.rs/aarch64_sysreg/badge.svg)](https://docs.rs/aarch64_sysreg) +[![Rust](https://img.shields.io/badge/edition-2021-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-org/aarch64_sysreg/blob/main/LICENSE) +
+ +[English](README.md) | 中文 + +# 简介 + +AArch64 系统寄存器类型定义库,提供 ARM64 架构中操作类型、寄存器类型和系统寄存器的枚举定义。 + +## 特性 + +- `#![no_std]` - 可在裸机环境使用 +- `OperationType` - AArch64 指令操作类型枚举 +- `RegistersType` - 通用寄存器类型枚举 (W/X/V/B/H/S/D/Q 寄存器等) +- `SystemRegType` - 系统寄存器类型枚举 + +## 安装 + +在 `Cargo.toml` 中添加: + +```toml +[dependencies] +aarch64_sysreg = "0.1" +``` + +## 使用示例 + +```rust +use aarch64_sysreg::{OperationType, RegistersType, SystemRegType}; + +fn main() { + // 操作类型 + let op = OperationType::ADD; + println!("Operation: {}", op); // ADD + println!("Value: 0x{:x}", op); // 0x6 + + // 从数值转换 + let op_from = OperationType::from(0x6); + assert_eq!(op_from, OperationType::ADD); + + // 寄存器类型 + let reg = RegistersType::X0; + println!("Register: {}", reg); // X0 + + // 系统寄存器 + let sys_reg = SystemRegType::MDSCR_EL1; + println!("System Register: {}", sys_reg); // MDSCR_EL1 +} +``` + +## 类型说明 + +### OperationType + +定义 AArch64 指令操作类型,包括: +- 算术运算: `ADD`, `SUB`, `MUL`, `DIV` 等 +- 逻辑运算: `AND`, `ORR`, `EOR`, `BIC` 等 +- 分支指令: `B`, `BL`, `BR`, `RET` 等 +- 加载存储: `LDR`, `STR`, `LDP`, `STP` 等 +- 系统指令: `MSR`, `MRS`, `SVC`, `HVC` 等 + +### RegistersType + +定义 AArch64 通用和向量寄存器: +- 32位通用寄存器: `W0` - `W30`, `WZR`, `WSP` +- 64位通用寄存器: `X0` - `X30`, `XZR`, `SP` +- 向量寄存器: `V0` - `V31`, `B0` - `B31`, `H0` - `H31`, `S0` - `S31`, `D0` - `D31`, `Q0` - `Q31` +- SVE 寄存器: `Z0` - `Z31`, `P0` - `P15` + +### SystemRegType + +定义 AArch64 系统寄存器,编号格式为 `000000`: +- 调试寄存器: `DBGBCR*_EL1`, `DBGBVR*_EL1` 等 +- 跟踪寄存器: `TRC*` 系列 +- 性能寄存器: `PMEVCNTR*_EL0`, `PMEVTYPER*_EL0` 等 +- 系统控制寄存器: `SCTLR_EL1`, `TTBR*_EL1` 等 + +# 验证 + +通过本地 `test.sh` 快速启动验证: + +```bash +./scripts/test.sh +``` + +# 文档 + +## API 文档 + +```bash +cargo doc --no-deps --open +``` + +# 贡献 + +1. 通过本地 `check.sh` +2. 通过本地 `test.sh` +3. 提交 PR 并通过 CI 检查 + +# 协议 + +本项目采用 Apache License, Version 2.0 许可证。详见 [LICENSE](LICENSE) 文件。 From 3fc6082fbae54495b15af634ed4736914fc18e51 Mon Sep 17 00:00:00 2001 From: YanLien Date: Wed, 18 Mar 2026 20:19:58 +0800 Subject: [PATCH 107/132] docs: enhance README with quick start guide and integration examples --- README.md | 129 +++++++++++++++++++++++++++++---------------------- README_CN.md | 129 +++++++++++++++++++++++++++++---------------------- 2 files changed, 148 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index b48c83550..d4f507a25 100644 --- a/README.md +++ b/README.md @@ -3,107 +3,126 @@

AArch64 System Register Type Definitions

+ [![Crates.io](https://img.shields.io/crates/v/aarch64_sysreg.svg)](https://crates.io/crates/aarch64_sysreg) [![Docs.rs](https://docs.rs/aarch64_sysreg/badge.svg)](https://docs.rs/aarch64_sysreg) [![Rust](https://img.shields.io/badge/edition-2021-orange.svg)](https://www.rust-lang.org/) [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-org/aarch64_sysreg/blob/main/LICENSE) +
English | [中文](README_CN.md) # Introduction -A library providing type definitions for AArch64 system registers, including operation types, register types, and system register enumerations for the ARM64 architecture. +A library providing type definitions for AArch64 system registers, including operation types, register types, and system register enumerations for the ARM64 architecture. Supports `#![no_std]` for bare-metal and OS kernel development. + +This library exports three core enumeration types: + +- **`OperationType`** — AArch64 instruction operation types (1000+ instructions) +- **`RegistersType`** — General-purpose and vector registers (W/X/V/B/H/S/D/Q/Z/P, etc.) +- **`SystemRegType`** — System registers (debug, trace, performance counters, system control, etc.) + +Each type implements `Display`, `From`, `LowerHex`, and `UpperHex` traits. + +## Quick Start + +### Requirements + +- Rust nightly toolchain +- Rust components: rust-src, clippy, rustfmt + +```bash +# Install rustup (if not installed) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# Install nightly toolchain and components +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly +``` + +### Run Check and Test + +```bash +# 1. Clone the repository +git clone https://github.com/arceos-org/aarch64_sysreg.git +cd aarch64_sysreg + +# 2. Code check (format + clippy + build + doc generation) +./scripts/check.sh + +# 3. Run tests +# Run all tests (unit tests + integration tests) +./scripts/test.sh + +# Run unit tests only +./scripts/test.sh unit + +# Run integration tests only +./scripts/test.sh integration -## Features +# List all available test suites +./scripts/test.sh list -- `#![no_std]` - Compatible with bare-metal environments -- `OperationType` - AArch64 instruction operation type enumeration -- `RegistersType` - General register type enumeration (W/X/V/B/H/S/D/Q registers, etc.) -- `SystemRegType` - System register type enumeration +# Specify unit test target +./scripts/test.sh unit --unit-targets x86_64-unknown-linux-gnu +``` -## Installation +## Integration + +### Installation Add to your `Cargo.toml`: ```toml [dependencies] -aarch64_sysreg = "0.1" +aarch64_sysreg = "0.1.1" ``` -## Usage +### Example ```rust use aarch64_sysreg::{OperationType, RegistersType, SystemRegType}; fn main() { - // Operation type + // Operation type: enum variant and value conversion let op = OperationType::ADD; - println!("Operation: {}", op); // ADD - println!("Value: 0x{:x}", op); // 0x6 + println!("{}", op); // ADD + println!("0x{:x}", op); // 0x6 + println!("0x{:X}", op); // 0x6 - // Convert from value let op_from = OperationType::from(0x6); assert_eq!(op_from, OperationType::ADD); // Register type let reg = RegistersType::X0; - println!("Register: {}", reg); // X0 + println!("{}", reg); // X0 + let reg_from = RegistersType::from(0x22); + assert_eq!(reg_from, RegistersType::X0); // System register let sys_reg = SystemRegType::MDSCR_EL1; - println!("System Register: {}", sys_reg); // MDSCR_EL1 + println!("{}", sys_reg); // MDSCR_EL1 + println!("0x{:x}", sys_reg); // 0x240004 } ``` -## Type Reference - -### OperationType - -Defines AArch64 instruction operation types, including: -- Arithmetic: `ADD`, `SUB`, `MUL`, `DIV`, etc. -- Logical: `AND`, `ORR`, `EOR`, `BIC`, etc. -- Branch: `B`, `BL`, `BR`, `RET`, etc. -- Load/Store: `LDR`, `STR`, `LDP`, `STP`, etc. -- System: `MSR`, `MRS`, `SVC`, `HVC`, etc. - -### RegistersType - -Defines AArch64 general-purpose and vector registers: -- 32-bit GPR: `W0` - `W30`, `WZR`, `WSP` -- 64-bit GPR: `X0` - `X30`, `XZR`, `SP` -- Vector registers: `V0` - `V31`, `B0` - `B31`, `H0` - `H31`, `S0` - `S31`, `D0` - `D31`, `Q0` - `Q31` -- SVE registers: `Z0` - `Z31`, `P0` - `P15` - -### SystemRegType - -Defines AArch64 system registers with encoding format `000000`: -- Debug registers: `DBGBCR*_EL1`, `DBGBVR*_EL1`, etc. -- Trace registers: `TRC*` series -- Performance registers: `PMEVCNTR*_EL0`, `PMEVTYPER*_EL0`, etc. -- System control registers: `SCTLR_EL1`, `TTBR*_EL1`, etc. - -# Verification - -Run local tests quickly: - -```bash -./scripts/test.sh -``` - -# Documentation +### Documentation -## API Documentation +Generate and view API documentation: ```bash cargo doc --no-deps --open ``` +Online documentation: [docs.rs/aarch64_sysreg](https://docs.rs/aarch64_sysreg) + # Contributing -1. Run local check: `./scripts/check.sh` -2. Run local tests: `./scripts/test.sh` -3. Submit PR and pass CI checks +1. Fork the repository and create a branch +2. Run local check: `./scripts/check.sh` +3. Run local tests: `./scripts/test.sh` +4. Submit PR and pass CI checks # License diff --git a/README_CN.md b/README_CN.md index c4b9873ba..70efd7c97 100644 --- a/README_CN.md +++ b/README_CN.md @@ -3,107 +3,126 @@

AArch64 系统寄存器类型定义库

+ [![Crates.io](https://img.shields.io/crates/v/aarch64_sysreg.svg)](https://crates.io/crates/aarch64_sysreg) [![Docs.rs](https://docs.rs/aarch64_sysreg/badge.svg)](https://docs.rs/aarch64_sysreg) [![Rust](https://img.shields.io/badge/edition-2021-orange.svg)](https://www.rust-lang.org/) [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-org/aarch64_sysreg/blob/main/LICENSE) +
[English](README.md) | 中文 # 简介 -AArch64 系统寄存器类型定义库,提供 ARM64 架构中操作类型、寄存器类型和系统寄存器的枚举定义。 +AArch64 系统寄存器类型定义库,提供 ARM64 架构中操作类型、寄存器类型和系统寄存器的枚举定义。支持 `#![no_std]`,可用于裸机和操作系统内核开发。 + +本库导出三个核心枚举类型: + +- **`OperationType`** — AArch64 指令操作类型(1000+ 种指令) +- **`RegistersType`** — 通用寄存器与向量寄存器(W/X/V/B/H/S/D/Q/Z/P 等) +- **`SystemRegType`** — 系统寄存器(调试、跟踪、性能计数、系统控制等) + +每个类型均实现了 `Display`、`From`、`LowerHex`、`UpperHex` trait。 + +## 快速上手 + +### 环境要求 + +- Rust nightly 工具链 +- Rust 组件: rust-src, clippy, rustfmt + +```bash +# 安装 rustup(如未安装) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# 安装 nightly 工具链及组件 +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly +``` + +### 运行检查和测试 + +```bash +# 1. 克隆仓库 +git clone https://github.com/arceos-org/aarch64_sysreg.git +cd aarch64_sysreg + +# 2. 代码检查(格式检查 + clippy + 构建 + 文档生成) +./scripts/check.sh + +# 3. 运行测试 +# 运行全部测试(单元测试 + 集成测试) +./scripts/test.sh + +# 仅运行单元测试 +./scripts/test.sh unit + +# 仅运行集成测试 +./scripts/test.sh integration -## 特性 +# 列出所有可用的测试套件 +./scripts/test.sh list -- `#![no_std]` - 可在裸机环境使用 -- `OperationType` - AArch64 指令操作类型枚举 -- `RegistersType` - 通用寄存器类型枚举 (W/X/V/B/H/S/D/Q 寄存器等) -- `SystemRegType` - 系统寄存器类型枚举 +# 指定单元测试目标 +./scripts/test.sh unit --unit-targets x86_64-unknown-linux-gnu +``` -## 安装 +## 集成使用 + +### 安装 在 `Cargo.toml` 中添加: ```toml [dependencies] -aarch64_sysreg = "0.1" +aarch64_sysreg = "0.1.1" ``` -## 使用示例 +### 使用示例 ```rust use aarch64_sysreg::{OperationType, RegistersType, SystemRegType}; fn main() { - // 操作类型 + // 操作类型:枚举变体与数值互转 let op = OperationType::ADD; - println!("Operation: {}", op); // ADD - println!("Value: 0x{:x}", op); // 0x6 + println!("{}", op); // ADD + println!("0x{:x}", op); // 0x6 + println!("0x{:X}", op); // 0x6 - // 从数值转换 let op_from = OperationType::from(0x6); assert_eq!(op_from, OperationType::ADD); // 寄存器类型 let reg = RegistersType::X0; - println!("Register: {}", reg); // X0 + println!("{}", reg); // X0 + let reg_from = RegistersType::from(0x22); + assert_eq!(reg_from, RegistersType::X0); // 系统寄存器 let sys_reg = SystemRegType::MDSCR_EL1; - println!("System Register: {}", sys_reg); // MDSCR_EL1 + println!("{}", sys_reg); // MDSCR_EL1 + println!("0x{:x}", sys_reg); // 0x240004 } ``` -## 类型说明 - -### OperationType - -定义 AArch64 指令操作类型,包括: -- 算术运算: `ADD`, `SUB`, `MUL`, `DIV` 等 -- 逻辑运算: `AND`, `ORR`, `EOR`, `BIC` 等 -- 分支指令: `B`, `BL`, `BR`, `RET` 等 -- 加载存储: `LDR`, `STR`, `LDP`, `STP` 等 -- 系统指令: `MSR`, `MRS`, `SVC`, `HVC` 等 - -### RegistersType - -定义 AArch64 通用和向量寄存器: -- 32位通用寄存器: `W0` - `W30`, `WZR`, `WSP` -- 64位通用寄存器: `X0` - `X30`, `XZR`, `SP` -- 向量寄存器: `V0` - `V31`, `B0` - `B31`, `H0` - `H31`, `S0` - `S31`, `D0` - `D31`, `Q0` - `Q31` -- SVE 寄存器: `Z0` - `Z31`, `P0` - `P15` - -### SystemRegType - -定义 AArch64 系统寄存器,编号格式为 `000000`: -- 调试寄存器: `DBGBCR*_EL1`, `DBGBVR*_EL1` 等 -- 跟踪寄存器: `TRC*` 系列 -- 性能寄存器: `PMEVCNTR*_EL0`, `PMEVTYPER*_EL0` 等 -- 系统控制寄存器: `SCTLR_EL1`, `TTBR*_EL1` 等 - -# 验证 - -通过本地 `test.sh` 快速启动验证: - -```bash -./scripts/test.sh -``` - -# 文档 +### 文档 -## API 文档 +生成并查看 API 文档: ```bash cargo doc --no-deps --open ``` +在线文档:[docs.rs/aarch64_sysreg](https://docs.rs/aarch64_sysreg) + # 贡献 -1. 通过本地 `check.sh` -2. 通过本地 `test.sh` -3. 提交 PR 并通过 CI 检查 +1. Fork 仓库并创建分支 +2. 运行本地检查:`./scripts/check.sh` +3. 运行本地测试:`./scripts/test.sh` +4. 提交 PR 并通过 CI 检查 # 协议 From 09bcc9aaa5fc118df85d1dcc2e784a31490921db Mon Sep 17 00:00:00 2001 From: aarkegz Date: Wed, 18 Mar 2026 22:45:56 +0800 Subject: [PATCH 108/132] fixes after merge, still wip --- Cargo.lock | 33 --------------------------- Cargo.toml | 3 +-- platform/riscv64-qemu-virt/Cargo.toml | 8 +++---- 3 files changed, 5 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e791bcd4..4c69006f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -619,17 +619,6 @@ dependencies = [ "spin 0.10.0", ] -[[package]] -name = "axfs_devfs" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b87ae981272ca8d5d8f106a4452c63f4b5ac36e17ee8f848ee1b250538b9f8" -dependencies = [ - "axfs_vfs", - "log", - "spin 0.9.8", -] - [[package]] name = "axfs" version = "0.2.0" @@ -651,17 +640,6 @@ dependencies = [ "scope-local", ] -[[package]] -name = "axfs_devfs" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b87ae981272ca8d5d8f106a4452c63f4b5ac36e17ee8f848ee1b250538b9f8" -dependencies = [ - "axfs_vfs", - "log", - "spin 0.9.8", -] - [[package]] name = "axfs_ramfs" version = "0.1.2" @@ -1782,17 +1760,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "crate_interface" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e5c7109dea31fc91ab584e99752baa997f76d46e49bab0a17b5e9679248df7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "crc" version = "3.4.0" diff --git a/Cargo.toml b/Cargo.toml index 6bea1ad67..58944e6bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,11 +35,10 @@ required-features = ["xtask"] [features] default = [] -ept-level-4 = ["axaddrspace/4-level-ept"] +ept-level-4 = [] fs = ["axstd/fs"] dyn-plat = ["axstd/plat-dyn"] # Driver features (from former driver crate) -rk3568-clk = ["dep:rk3568_clk"] rk3588-clk = ["dep:rk3588-clk"] sdmmc = ["dep:sdmmc"] rockchip-pm = ["dep:rockchip-pm"] diff --git a/platform/riscv64-qemu-virt/Cargo.toml b/platform/riscv64-qemu-virt/Cargo.toml index 8bfe9dc17..869095bea 100644 --- a/platform/riscv64-qemu-virt/Cargo.toml +++ b/platform/riscv64-qemu-virt/Cargo.toml @@ -24,10 +24,10 @@ sbi-rt = { version = "0.0.3", features = ["legacy"] } uart_16550 = "0.4.0" axconfig-macros = "0.2" -axcpu = { workspace = true } -axplat = { workspace = true } -axvisor_api = { workspace = true } -crate_interface = { workspace = true } +axcpu = { version = "0.3.0-preview.8", features = ["arm-el2"] } +axplat = "0.3.1-pre.6" +axvisor_api = "0.3" +crate_interface = "0.3" [package.metadata.docs.rs] targets = ["riscv64gc-unknown-none-elf"] From c1d3c2dd7dce1275fab90b61472ec8dc9a0ece86 Mon Sep 17 00:00:00 2001 From: YanLien Date: Thu, 19 Mar 2026 14:10:46 +0800 Subject: [PATCH 109/132] [Refactor]: Migrate CI workflows to shared axci and update project docs --- .github/config.json | 7 ++ .github/workflows/check.yml | 67 ++--------- .github/workflows/deploy.yml | 124 ++------------------- .github/workflows/push.yml | 145 ++---------------------- .github/workflows/release.yml | 159 +++----------------------- .github/workflows/test.yml | 43 +------ .gitignore | 8 ++ README.md | 202 ++++++++++++++++++++++----------- README_CN.md | 203 +++++++++++++++++++++++----------- rust-toolchain.toml | 10 ++ scripts/check.sh | 35 ++++++ scripts/test.sh | 34 ++++++ 12 files changed, 411 insertions(+), 626 deletions(-) create mode 100644 rust-toolchain.toml create mode 100755 scripts/check.sh create mode 100755 scripts/test.sh diff --git a/.github/config.json b/.github/config.json index f347e101e..de46bed95 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1,10 +1,17 @@ { + "component": { + "name": "axdevice", + "crate_name": "axdevice" + }, "targets": [ "aarch64-unknown-none-softfloat", "x86_64-unknown-linux-gnu", "x86_64-unknown-none", "riscv64gc-unknown-none-elf" ], + "unit_test_targets": [ + "x86_64-unknown-linux-gnu" + ], "rust_components": [ "rust-src", "clippy", diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 330fa15e9..018bbbeb2 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,66 +1,15 @@ -name: Quality Checks +# Quality Check Workflow +# References shared workflow from axci + +name: Check on: push: - branches: - - '**' - tags-ignore: - - '**' + branches: ['**'] + tags-ignore: ['**'] pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - rust_components: ${{ steps.config.outputs.rust_components }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) - - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT - check: - name: Check - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: ${{ needs.load-config.outputs.rust_components }} - targets: ${{ matrix.target }} - - - name: Check rust version - run: rustc --version --verbose - - - name: Check code format - run: cargo fmt --all -- --check - - - name: Build - run: cargo build --target ${{ matrix.target }} --all-features - - - name: Run clippy - run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings - - - name: Build documentation - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fda9a323d..37b00c09c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,3 +1,6 @@ +# Deploy Workflow +# References shared workflow from axci + name: Deploy on: @@ -5,122 +8,9 @@ on: tags: - 'v[0-9]+.[0-9]+.[0-9]+' -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: 'pages' - cancel-in-progress: false - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_deploy: ${{ steps.check.outputs.should_deploy }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check if tag is on main or master branch - id: check - run: | - git fetch origin main master || true - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Tag is on main or master branch" - echo "should_deploy=true" >> $GITHUB_OUTPUT - else - echo "✗ Tag is not on main or master branch, skipping deployment" - echo "Tag is on: $BRANCHES" - echo "should_deploy=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_deploy == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - test: - uses: ./.github/workflows/test.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - build: - name: Build documentation - runs-on: ubuntu-latest - needs: [verify-tag, check, test] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Build docs - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: | - # Build documentation - cargo doc --no-deps --all-features - - # Auto-detect documentation directory - # Check if doc exists in target/doc or target/*/doc - if [ -d "target/doc" ]; then - DOC_DIR="target/doc" - else - # Find doc directory under target/*/doc pattern - DOC_DIR=$(find target -type d -name doc -path "target/*/doc" | head -n 1) - if [ -z "$DOC_DIR" ]; then - echo "Error: Could not find documentation directory" - exit 1 - fi - fi - - echo "Documentation found in: $DOC_DIR" - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > "${DOC_DIR}/index.html" - echo "DOC_DIR=${DOC_DIR}" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ${{ env.DOC_DIR }} - deploy: - name: Deploy to GitHub Pages - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: [verify-tag, build] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main + with: + verify_branch: true + verify_version: true diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a18de4a04..3ff391a4a 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,147 +1,16 @@ -# ═══════════════════════════════════════════════════════════════════════════════ -# 组件仓库 GitHub Actions 配置模板 -# ═══════════════════════════════════════════════════════════════════════════════ -# -# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 -# -# 【使用步骤】 -# ───────────────────────────────────────────────────────────────────────────── -# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: -# cp scripts/push.yml <子仓库>/.github/workflows/push.yml -# -# 2. 在子仓库中配置 Secret: -# GitHub 仓库 → Settings → Secrets → Actions → New repository secret -# 名称: PARENT_REPO_TOKEN -# 值: 具有主仓库 repo 权限的 Personal Access Token -# -# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): -# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits -# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) -# -# 【Token 权限要求】 -# ───────────────────────────────────────────────────────────────────────────── -# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: -# - repo (Full control of private repositories) -# 或 -# - Fine-grained token: Contents (Read and Write) -# -# 【触发条件】 -# ───────────────────────────────────────────────────────────────────────────── -# - 自动触发:推送到 dev 或 main 分支时 -# - 手动触发:Actions → Notify Parent Repository → Run workflow -# -# 【工作流程】 -# ───────────────────────────────────────────────────────────────────────────── -# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull -# -# 【注意事项】 -# ───────────────────────────────────────────────────────────────────────────── -# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 -# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 -# -# ═══════════════════════════════════════════════════════════════════════════════ - name: Notify Parent Repository -# 当有新的推送时触发 on: push: branches: - main - - master + - zcs workflow_dispatch: jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Get repository info - id: repo - env: - GH_REPO_NAME: ${{ github.event.repository.name }} - GH_REF_NAME: ${{ github.ref_name }} - GH_SERVER_URL: ${{ github.server_url }} - GH_REPOSITORY: ${{ github.repository }} - run: | - # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 - COMPONENT="$GH_REPO_NAME" - BRANCH="$GH_REF_NAME" - # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list - REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" - - echo "component=${COMPONENT}" >> $GITHUB_OUTPUT - echo "branch=${BRANCH}" >> $GITHUB_OUTPUT - echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT - - echo "Component: ${COMPONENT}" - echo "Branch: ${BRANCH}" - echo "Repo URL: ${REPO_URL}" - - - name: Notify parent repository - env: - # ── 需要修改 ────────────────────────────────────────────────────────── - PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 - # ── 无需修改 ────────────────────────────────────────────────────────── - DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} - # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 - COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - GIT_ACTOR: ${{ github.actor }} - GIT_SHA: ${{ github.sha }} - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" - - # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 - PAYLOAD=$(jq -n \ - --arg component "$COMPONENT" \ - --arg branch "$BRANCH" \ - --arg repo_url "$REPO_URL" \ - --arg commit "$GIT_SHA" \ - --arg message "$COMMIT_MESSAGE" \ - --arg author "$GIT_ACTOR" \ - '{ - event_type: "subtree-update", - client_payload: { - component: $component, - branch: $branch, - repo_url: $repo_url, - commit: $commit, - message: $message, - author: $author - } - }') - - curl --fail --show-error -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${DISPATCH_TOKEN}" \ - https://api.github.com/repos/${PARENT_REPO}/dispatches \ - -d "$PAYLOAD" - - echo "Notification sent successfully" - - - name: Create summary - env: - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - GIT_SHA: ${{ github.sha }} - GIT_ACTOR: ${{ github.actor }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY - echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY + notify-parent: + name: Notify Parent Repository + # 调用 axci 仓库的可复用工作流 + uses: arceos-hypervisor/axci/.github/workflows/push.yml@main + secrets: + PARENT_REPO_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e857b48b..20f186395 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,7 @@ +# Release Workflow +# References shared workflow from axci +# check + test must pass before release + name: Release on: @@ -6,155 +10,18 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' -permissions: - contents: write - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - is_prerelease: ${{ steps.check.outputs.is_prerelease }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check tag type and branch - id: check - run: | - git fetch origin main master dev || true - - TAG="${{ github.ref_name }}" - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - echo "Tag: $TAG" - echo "Branches containing this tag: $BRANCHES" - - # Check if it's a prerelease tag - if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[0-9]+$ ]]; then - echo "📦 Detected prerelease tag" - echo "is_prerelease=true" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -q 'origin/dev'; then - echo "✓ Prerelease tag is on dev branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Prerelease tag must be on dev branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "📦 Detected stable release tag" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Stable release tag is on main or master branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Stable release tag must be on main or master branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - else - echo "✗ Unknown tag format, skipping release" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - echo "should_release=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_release == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main test: - uses: ./.github/workflows/test.yml - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main release: - name: Create GitHub Release - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate release notes - id: release_notes - run: | - CURRENT_TAG="${{ github.ref_name }}" - - # Get previous tag - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) - - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then - echo "No previous tag found, this is the first release" - CHANGELOG="Initial release" - else - echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" - - # Generate changelog with commit messages - CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") - - if [ -z "$CHANGELOG" ]; then - CHANGELOG="No changes" - fi - fi - - # Write changelog to output file (multi-line) - { - echo "changelog<> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} - body: | - ## Changes - ${{ steps.release_notes.outputs.changelog }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish: - name: Publish to crates.io - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Dry run publish - run: cargo publish --dry-run - - - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + needs: [check, test] + uses: arceos-hypervisor/axci/.github/workflows/release.yml@main + with: + verify_branch: true + verify_version: true + secrets: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc3b293d9..6a58ef8e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,3 +1,6 @@ +# Integration Test Workflow +# References shared workflow from axci + name: Test on: @@ -7,44 +10,8 @@ on: tags-ignore: - '**' pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - test: - name: Test - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - # - name: Run tests - # run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture - - # - name: Run doc tests - # run: cargo test --target ${{ matrix.target }} --doc - - name: Run tests - run: echo "Tests are skipped!" + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main diff --git a/.gitignore b/.gitignore index 55c0d4550..39ee77997 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,11 @@ rusty-tags.vi # We ignore Cargo.lock because `axvcpu` is just a library Cargo.lock + +# Test results (generated by shared test framework) +/test-results/ +/test_repos/ +*.log + +# Downloaded test framework +/scripts/.axci/ diff --git a/README.md b/README.md index 12ebcf797..6a3ef6375 100644 --- a/README.md +++ b/README.md @@ -1,78 +1,154 @@ -# axdevice - -**axdevice** is a reusable, OS-agnostic device abstraction layer designed for virtual machines. It allows dynamic device configuration and MMIO emulation in `no_std` environments, making it suitable for hypervisors or operating systems targeting RISC-V or AArch64. - -## ✨ Highlights - -- 📦 **Componentized**: Designed as a modular crate to be integrated into any OS or hypervisor. -- 🧩 **Flexible device abstraction**: Supports dynamic device registration and MMIO handling. -- 🛠️ **No `std` required**: Uses `alloc` and `core` only, suitable for bare-metal development. -- 🧵 **Thread-safe**: Devices are stored using `Arc`, ready for multicore use. -- 🧱 **Easily extensible**: Just plug in device types via `axdevice_base::BaseDeviceOps`. - -## 📦 Structure - -- `config.rs`: Defines `AxVmDeviceConfig`, a wrapper for device configuration input. -- `device.rs`: Defines `AxVmDevices`, manages and dispatches MMIO to registered devices. - -## 📐 Dependency Graph - -```text - +-------------------+ - | axvmconfig | <- defines EmulatedDeviceConfig - +-------------------+ - | - v -+------------------+ uses +-----------------------+ -| axdevice +-------------->+ axdevice_base::trait | -| (this crate) | +-----------------------+ -+------------------+ ^ - | | - v | -+------------------+ | -| axaddrspace | -- GuestPhysAddr ----+ -+------------------+ +

axdevice

+ +

OS-Agnostic Virtual Device Abstraction Layer

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/axdevice.svg)](https://crates.io/crates/axdevice) +[![Docs.rs](https://docs.rs/axdevice/badge.svg)](https://docs.rs/axdevice) +[![Rust](https://img.shields.io/badge/edition-2024-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/axdevice/blob/main/LICENSE) + +
+ +English | [中文](README_CN.md) + +# Introduction + +`axdevice` is a reusable, OS-agnostic device abstraction layer for virtual machines. It provides unified management for emulated devices and dispatches guest accesses to MMIO, system-register, and port-based devices in `#![no_std]` environments. + +This crate currently exports two core types: + +- **`AxVmDeviceConfig`** - Wraps a list of `EmulatedDeviceConfig` items used to initialize VM devices +- **`AxVmDevices`** - Manages device collections, dispatches device access requests, and allocates IVC channels + +The crate is suitable for hypervisors and low-level OS components targeting AArch64 or RISC-V. + +## Quick Start + +### Requirements + +- Rust nightly toolchain +- Rust components: rust-src, clippy, rustfmt + +```bash +# Install rustup (if not installed) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# Install nightly toolchain and components +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly ``` -## 🔁 Usage Flow - -```text -[1] Load VM device config (Vec) - ↓ -[2] Create AxVmDeviceConfig - ↓ -[3] Pass into AxVmDevices::new() - ↓ -[4] MMIO access triggers handle_mmio_{read,write} - ↓ -[5] Device selected by GuestPhysAddr - ↓ -[6] Forwarded to BaseDeviceOps::handle_{read,write}() +### Run Check and Test + +```bash +# 1. Enter the repository +cd axdevice + +# 2. Code check (format + clippy + build) +./scripts/check.sh + +# 3. Run tests +./scripts/test.sh ``` -## 🚀 Example +## Integration + +### Installation + +Add to your `Cargo.toml`: + +```toml +[dependencies] +axdevice = "0.2.2" +``` + +### Example ```rust +use std::sync::{Arc, Mutex}; + +use axaddrspace::device::AccessWidth; +use axaddrspace::{GuestPhysAddr, GuestPhysAddrRange}; use axdevice::{AxVmDeviceConfig, AxVmDevices}; +use axdevice_base::BaseDeviceOps; +use axerrno::AxResult; +use axvmconfig::EmulatedDeviceType; + +struct MockMmioDevice { + range: GuestPhysAddrRange, + last_write: Mutex>, +} + +impl MockMmioDevice { + fn new(base: usize, size: usize) -> Self { + Self { + range: GuestPhysAddrRange::new( + GuestPhysAddr::from(base), + GuestPhysAddr::from(base + size), + ), + last_write: Mutex::new(None), + } + } +} + +impl BaseDeviceOps for MockMmioDevice { + fn address_range(&self) -> GuestPhysAddrRange { + self.range + } + + fn emu_type(&self) -> EmulatedDeviceType { + EmulatedDeviceType::IVCChannel + } + + fn handle_read(&self, _addr: GuestPhysAddr, _width: AccessWidth) -> AxResult { + Ok(0xDEAD_BEEF) + } + + fn handle_write(&self, addr: GuestPhysAddr, _width: AccessWidth, val: usize) -> AxResult { + let offset = addr.as_usize() - self.range.start.as_usize(); + assert_eq!(offset, 0x40); + *self.last_write.lock().unwrap() = Some(val); + Ok(()) + } +} + +fn main() { + let config = AxVmDeviceConfig::new(vec![]); + let mut devices = AxVmDevices::new(config); + + let mock = Arc::new(MockMmioDevice::new(0x1000_0000, 0x1000)); + devices.add_mmio_dev(mock.clone()); + + let width = AccessWidth::try_from(4).unwrap(); + let addr = GuestPhysAddr::from(0x1000_0040); + + devices.handle_mmio_write(addr, width, 0x1234_5678).unwrap(); + let value = devices.handle_mmio_read(addr, width).unwrap(); + + assert_eq!(value, 0xDEAD_BEEF); + assert_eq!(*mock.last_write.lock().unwrap(), Some(0x1234_5678)); +} +``` -// Step 1: Load configuration (e.g. from .toml or hypervisor setup) -let config = AxVmDeviceConfig::new(vec![/* EmulatedDeviceConfig */]); +### Documentation -// Step 2: Initialize devices -let devices = AxVmDevices::new(config); +Generate and view API documentation: -// Step 3: Emulate MMIO access -let _ = devices.handle_mmio_read(0x1000_0000, 4); -devices.handle_mmio_write(0x1000_0000, 4, 0xdead_beef); +```bash +cargo doc --no-deps --open ``` -## 📦 Dependencies +Online documentation: [docs.rs/axdevice](https://docs.rs/axdevice) + +# Contributing -- [`axvmconfig`](https://github.com/arceos-hypervisor/axvmconfig.git) -- [`axaddrspace`](https://github.com/arceos-hypervisor/axaddrspace.git) -- [`axdevice_base`](https://github.com/arceos-hypervisor/axdevice_crates.git) -- `log`, `alloc`, `cfg-if`, `axerrno` +1. Fork the repository and create a branch +2. Run local check: `./scripts/check.sh` +3. Run local tests: `./scripts/test.sh` +4. Submit PR and pass CI checks -## License +# License -Axdevice is licensed under the Apache License, Version 2.0. See the [LICENSE](./LICENSE) file for details. +Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details. diff --git a/README_CN.md b/README_CN.md index df25881a1..9b804b1af 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,81 +1,154 @@ -# axdevice - -**axdevice** 是一个可复用、与操作系统无关的设备抽象层,专为虚拟机设计,支持在 `no_std` 环境中进行设备配置与 MMIO 模拟。适用于开发 hypervisor 或嵌入式操作系统。 - -## ✨ 特性亮点 - -- 📦 **模块化设计**:适用于任意操作系统或虚拟化平台的组件库。 -- 🧩 **灵活设备抽象**:通过配置动态加载和注册设备。 -- 🛠️ **无标准库依赖**:适配裸机、EL2 等场景,仅依赖 `core` 与 `alloc`。 -- 🧵 **线程安全**:所有设备均用 `Arc` 管理,支持多核并发。 -- 🧱 **便于扩展**:接入自定义设备只需实现 `BaseDeviceOps` trait。 - -## 📦 模块结构 - -- `config.rs`: 定义 `AxVmDeviceConfig`,用于初始化设备配置。 -- `device.rs`: 定义 `AxVmDevices`,管理设备并处理 MMIO 读写。 - -## 📐 依赖图 - -```text - +-------------------+ - | axvmconfig | <- 提供 EmulatedDeviceConfig - +-------------------+ - | - v -+------------------+ uses +-----------------------+ -| axdevice +-------------->+ axdevice_base::trait | -| (当前模块) | +-----------------------+ -+------------------+ ^ - | | - v | -+------------------+ | -| axaddrspace | -- GuestPhysAddr ----+ -+------------------+ +

axdevice

+ +

面向虚拟机的操作系统无关设备抽象层

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/axdevice.svg)](https://crates.io/crates/axdevice) +[![Docs.rs](https://docs.rs/axdevice/badge.svg)](https://docs.rs/axdevice) +[![Rust](https://img.shields.io/badge/edition-2024-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/axdevice/blob/main/LICENSE) + +
+ +[English](README.md) | 中文 + +# Introduction + +`axdevice` 是一个可复用、与操作系统无关的虚拟机设备抽象层。它在 `#![no_std]` 环境中为模拟设备提供统一管理能力,并将访存请求分发到 MMIO、系统寄存器和端口类设备。 + +该 crate 当前导出两个核心类型: + +- **`AxVmDeviceConfig`** - 封装用于初始化虚拟机设备的 `EmulatedDeviceConfig` 列表 +- **`AxVmDevices`** - 管理设备集合、分发设备访问请求,并提供 IVC 通道分配能力 + +该 crate 适用于面向 AArch64 或 RISC-V 的 hypervisor 及底层操作系统组件。 + +## Quick Start + +### Requirements + +- Rust nightly 工具链 +- Rust 组件:rust-src、clippy、rustfmt + +```bash +# 安装 rustup(如果尚未安装) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# 安装 nightly 工具链与所需组件 +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly ``` -## 🔁 使用流程 - -```text -[1] 加载设备配置 Vec - ↓ -[2] 构造 AxVmDeviceConfig - ↓ -[3] AxVmDevices::new() 初始化所有设备 - ↓ -[4] guest发起 MMIO 访问 - ↓ -[5] 匹配设备地址范围 - ↓ -[6] 调用设备 trait 接口 handle_read / handle_write +### Run Check and Test + +```bash +# 1. 进入仓库目录 +cd axdevice + +# 2. 代码检查(格式化 + clippy + 构建) +./scripts/check.sh + +# 3. 运行测试 +./scripts/test.sh ``` -## 🚀 示例代码 +## Integration + +### Installation + +将以下依赖加入 `Cargo.toml`: + +```toml +[dependencies] +axdevice = "0.2.2" +``` + +### Example ```rust +use std::sync::{Arc, Mutex}; + +use axaddrspace::device::AccessWidth; +use axaddrspace::{GuestPhysAddr, GuestPhysAddrRange}; use axdevice::{AxVmDeviceConfig, AxVmDevices}; +use axdevice_base::BaseDeviceOps; +use axerrno::AxResult; +use axvmconfig::EmulatedDeviceType; + +struct MockMmioDevice { + range: GuestPhysAddrRange, + last_write: Mutex>, +} + +impl MockMmioDevice { + fn new(base: usize, size: usize) -> Self { + Self { + range: GuestPhysAddrRange::new( + GuestPhysAddr::from(base), + GuestPhysAddr::from(base + size), + ), + last_write: Mutex::new(None), + } + } +} + +impl BaseDeviceOps for MockMmioDevice { + fn address_range(&self) -> GuestPhysAddrRange { + self.range + } + + fn emu_type(&self) -> EmulatedDeviceType { + EmulatedDeviceType::IVCChannel + } + + fn handle_read(&self, _addr: GuestPhysAddr, _width: AccessWidth) -> AxResult { + Ok(0xDEAD_BEEF) + } + + fn handle_write(&self, addr: GuestPhysAddr, _width: AccessWidth, val: usize) -> AxResult { + let offset = addr.as_usize() - self.range.start.as_usize(); + assert_eq!(offset, 0x40); + *self.last_write.lock().unwrap() = Some(val); + Ok(()) + } +} + +fn main() { + let config = AxVmDeviceConfig::new(vec![]); + let mut devices = AxVmDevices::new(config); + + let mock = Arc::new(MockMmioDevice::new(0x1000_0000, 0x1000)); + devices.add_mmio_dev(mock.clone()); + + let width = AccessWidth::try_from(4).unwrap(); + let addr = GuestPhysAddr::from(0x1000_0040); + + devices.handle_mmio_write(addr, width, 0x1234_5678).unwrap(); + let value = devices.handle_mmio_read(addr, width).unwrap(); + + assert_eq!(value, 0xDEAD_BEEF); + assert_eq!(*mock.last_write.lock().unwrap(), Some(0x1234_5678)); +} +``` -let config = AxVmDeviceConfig::new(vec![/* EmulatedDeviceConfig */]); +### Documentation -let devices = AxVmDevices::new(config); +生成并查看 API 文档: -let _ = devices.handle_mmio_read(0x1000_0000, 4); -devices.handle_mmio_write(0x1000_0000, 4, 0xdead_beef); +```bash +cargo doc --no-deps --open ``` -## 🔧 依赖组件 - -- [`axvmconfig`](https://github.com/arceos-hypervisor/axvmconfig.git) -- [`axaddrspace`](https://github.com/arceos-hypervisor/axaddrspace.git) -- [`axdevice_base`](https://github.com/arceos-hypervisor/axdevice_crates.git) +在线文档: [docs.rs/axdevice](https://docs.rs/axdevice) -其他依赖: +# Contributing -- `log` -- `alloc` -- `cfg-if` -- `axerrno` +1. Fork 仓库并创建分支 +2. 本地运行检查:`./scripts/check.sh` +3. 本地运行测试:`./scripts/test.sh` +4. 提交 PR 并通过 CI 检查 -## License +# License -Axdevice 采用 Apache License 2.0 开源协议。详见 [LICENSE](./LICENSE) 文件。 +本项目基于 Apache License 2.0 许可证发布。详见 [LICENSE](LICENSE)。 diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..8e03d91ab --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,10 @@ +[toolchain] +channel = "nightly-2025-05-20" +components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] +profile = "minimal" +targets = [ + "aarch64-unknown-none-softfloat", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-none", + "riscv64gc-unknown-none-elf" +] diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 000000000..8c95f0757 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# aarch64_sysreg 代码检查脚本 +# 下载并调用 axci 仓库中的检查脚本 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +COMPONENT_NAME="$(basename "$COMPONENT_DIR")" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行检查 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/check.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 000000000..751cb0ef1 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# +# aarch64_sysreg 测试脚本 +# 下载并调用 axci 仓库中的测试框架 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行测试,自动指定当前组件 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/tests.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" From ad5e56e38562bd433df0f37173dc30f401aedf2b Mon Sep 17 00:00:00 2001 From: szy Date: Thu, 19 Mar 2026 15:09:49 +0800 Subject: [PATCH 110/132] refactor: add scripts/tests/CI workflows and docs --- .github/config.json | 7 ++ .github/workflows/check.yml | 67 ++------------ .github/workflows/deploy.yml | 124 ++------------------------ .github/workflows/push.yml | 143 ++---------------------------- .github/workflows/release.yml | 159 +++------------------------------ .github/workflows/test.yml | 49 ++--------- .gitignore | 8 ++ README.md | 155 +++++++++++++++++++++++++++++++- README_CN.md | 160 ++++++++++++++++++++++++++++++++++ rust-toolchain.toml | 2 +- scripts/check.sh | 35 ++++++++ scripts/test.sh | 34 ++++++++ src/devops_impl.rs | 10 +-- tests/consts_tests.rs | 46 ++++++++++ tests/vplic_tests.rs | 67 ++++++++++++++ 15 files changed, 556 insertions(+), 510 deletions(-) create mode 100644 README_CN.md create mode 100755 scripts/check.sh create mode 100755 scripts/test.sh create mode 100644 tests/consts_tests.rs create mode 100644 tests/vplic_tests.rs diff --git a/.github/config.json b/.github/config.json index c750554c4..684673b7a 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1,7 +1,14 @@ { + "component": { + "name": "riscv_vplic", + "crate_name": "riscv_vplic" + }, "targets": [ "riscv64gc-unknown-none-elf" ], + "unit_test_targets": [ + "x86_64-unknown-linux-gnu" + ], "rust_components": [ "rust-src", "clippy", diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 330fa15e9..018bbbeb2 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,66 +1,15 @@ -name: Quality Checks +# Quality Check Workflow +# References shared workflow from axci + +name: Check on: push: - branches: - - '**' - tags-ignore: - - '**' + branches: ['**'] + tags-ignore: ['**'] pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - rust_components: ${{ steps.config.outputs.rust_components }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) - - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT - check: - name: Check - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: ${{ needs.load-config.outputs.rust_components }} - targets: ${{ matrix.target }} - - - name: Check rust version - run: rustc --version --verbose - - - name: Check code format - run: cargo fmt --all -- --check - - - name: Build - run: cargo build --target ${{ matrix.target }} --all-features - - - name: Run clippy - run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings - - - name: Build documentation - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9938e2cc2..37b00c09c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,3 +1,6 @@ +# Deploy Workflow +# References shared workflow from axci + name: Deploy on: @@ -5,122 +8,9 @@ on: tags: - 'v[0-9]+.[0-9]+.[0-9]+' -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: 'pages' - cancel-in-progress: false - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_deploy: ${{ steps.check.outputs.should_deploy }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check if tag is on main or master branch - id: check - run: | - git fetch origin main master || true - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Tag is on main or master branch" - echo "should_deploy=true" >> $GITHUB_OUTPUT - else - echo "✗ Tag is not on main or master branch, skipping deployment" - echo "Tag is on: $BRANCHES" - echo "should_deploy=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_deploy == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - test: - uses: ./.github/workflows/test.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - build: - name: Build documentation - runs-on: ubuntu-latest - needs: [verify-tag, check, test] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Build docs - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: | - # Build documentation - cargo doc --no-deps --all-features - - # Auto-detect documentation directory - # Check if doc exists in target/doc or target/*/doc - if [ -d "target/doc" ]; then - DOC_DIR="target/doc" - else - # Find doc directory under target/*/doc pattern - DOC_DIR=$(find target -type d -name doc -path "target/*/doc" | head -n 1) - if [ -z "$DOC_DIR" ]; then - echo "Error: Could not find documentation directory" - exit 1 - fi - fi - - echo "Documentation found in: $DOC_DIR" - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > "${DOC_DIR}/index.html" - echo "DOC_DIR=${DOC_DIR}" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ${{ env.DOC_DIR }} - deploy: - name: Deploy to GitHub Pages - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: [verify-tag, build] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 \ No newline at end of file + uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main + with: + verify_branch: true + verify_version: true diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a18de4a04..dda571781 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,49 +1,5 @@ -# ═══════════════════════════════════════════════════════════════════════════════ -# 组件仓库 GitHub Actions 配置模板 -# ═══════════════════════════════════════════════════════════════════════════════ -# -# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 -# -# 【使用步骤】 -# ───────────────────────────────────────────────────────────────────────────── -# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: -# cp scripts/push.yml <子仓库>/.github/workflows/push.yml -# -# 2. 在子仓库中配置 Secret: -# GitHub 仓库 → Settings → Secrets → Actions → New repository secret -# 名称: PARENT_REPO_TOKEN -# 值: 具有主仓库 repo 权限的 Personal Access Token -# -# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): -# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits -# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) -# -# 【Token 权限要求】 -# ───────────────────────────────────────────────────────────────────────────── -# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: -# - repo (Full control of private repositories) -# 或 -# - Fine-grained token: Contents (Read and Write) -# -# 【触发条件】 -# ───────────────────────────────────────────────────────────────────────────── -# - 自动触发:推送到 dev 或 main 分支时 -# - 手动触发:Actions → Notify Parent Repository → Run workflow -# -# 【工作流程】 -# ───────────────────────────────────────────────────────────────────────────── -# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull -# -# 【注意事项】 -# ───────────────────────────────────────────────────────────────────────────── -# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 -# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 -# -# ═══════════════════════════════════════════════════════════════════════════════ - name: Notify Parent Repository -# 当有新的推送时触发 on: push: branches: @@ -52,96 +8,9 @@ on: workflow_dispatch: jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Get repository info - id: repo - env: - GH_REPO_NAME: ${{ github.event.repository.name }} - GH_REF_NAME: ${{ github.ref_name }} - GH_SERVER_URL: ${{ github.server_url }} - GH_REPOSITORY: ${{ github.repository }} - run: | - # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 - COMPONENT="$GH_REPO_NAME" - BRANCH="$GH_REF_NAME" - # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list - REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" - - echo "component=${COMPONENT}" >> $GITHUB_OUTPUT - echo "branch=${BRANCH}" >> $GITHUB_OUTPUT - echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT - - echo "Component: ${COMPONENT}" - echo "Branch: ${BRANCH}" - echo "Repo URL: ${REPO_URL}" - - - name: Notify parent repository - env: - # ── 需要修改 ────────────────────────────────────────────────────────── - PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 - # ── 无需修改 ────────────────────────────────────────────────────────── - DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} - # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 - COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - GIT_ACTOR: ${{ github.actor }} - GIT_SHA: ${{ github.sha }} - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" - - # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 - PAYLOAD=$(jq -n \ - --arg component "$COMPONENT" \ - --arg branch "$BRANCH" \ - --arg repo_url "$REPO_URL" \ - --arg commit "$GIT_SHA" \ - --arg message "$COMMIT_MESSAGE" \ - --arg author "$GIT_ACTOR" \ - '{ - event_type: "subtree-update", - client_payload: { - component: $component, - branch: $branch, - repo_url: $repo_url, - commit: $commit, - message: $message, - author: $author - } - }') - - curl --fail --show-error -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${DISPATCH_TOKEN}" \ - https://api.github.com/repos/${PARENT_REPO}/dispatches \ - -d "$PAYLOAD" - - echo "Notification sent successfully" - - - name: Create summary - env: - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - GIT_SHA: ${{ github.sha }} - GIT_ACTOR: ${{ github.actor }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY - echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY + notify-parent: + name: Notify Parent Repository + # 调用 axci 仓库的可复用工作流 + uses: arceos-hypervisor/axci/.github/workflows/push.yml@main + secrets: + PARENT_REPO_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e857b48b..20f186395 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,7 @@ +# Release Workflow +# References shared workflow from axci +# check + test must pass before release + name: Release on: @@ -6,155 +10,18 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' -permissions: - contents: write - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - is_prerelease: ${{ steps.check.outputs.is_prerelease }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check tag type and branch - id: check - run: | - git fetch origin main master dev || true - - TAG="${{ github.ref_name }}" - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - echo "Tag: $TAG" - echo "Branches containing this tag: $BRANCHES" - - # Check if it's a prerelease tag - if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[0-9]+$ ]]; then - echo "📦 Detected prerelease tag" - echo "is_prerelease=true" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -q 'origin/dev'; then - echo "✓ Prerelease tag is on dev branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Prerelease tag must be on dev branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "📦 Detected stable release tag" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Stable release tag is on main or master branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Stable release tag must be on main or master branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - else - echo "✗ Unknown tag format, skipping release" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - echo "should_release=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_release == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main test: - uses: ./.github/workflows/test.yml - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main release: - name: Create GitHub Release - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate release notes - id: release_notes - run: | - CURRENT_TAG="${{ github.ref_name }}" - - # Get previous tag - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) - - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then - echo "No previous tag found, this is the first release" - CHANGELOG="Initial release" - else - echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" - - # Generate changelog with commit messages - CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") - - if [ -z "$CHANGELOG" ]; then - CHANGELOG="No changes" - fi - fi - - # Write changelog to output file (multi-line) - { - echo "changelog<> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} - body: | - ## Changes - ${{ steps.release_notes.outputs.changelog }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish: - name: Publish to crates.io - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Dry run publish - run: cargo publish --dry-run - - - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + needs: [check, test] + uses: arceos-hypervisor/axci/.github/workflows/release.yml@main + with: + verify_branch: true + verify_version: true + secrets: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc3b293d9..e7520475f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,50 +1,15 @@ +# Integration Test Workflow +# References shared workflow from axci + name: Test on: push: - branches: - - '**' - tags-ignore: - - '**' + branches: ['**'] + tags-ignore: ['**'] pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - test: - name: Test - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - # - name: Run tests - # run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture - - # - name: Run doc tests - # run: cargo test --target ${{ matrix.target }} --doc - - name: Run tests - run: echo "Tests are skipped!" + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main diff --git a/.gitignore b/.gitignore index 97e6d8325..3f3d87834 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,11 @@ /target Cargo.lock .vscode/ + +# Test results (generated by shared test framework) +/test-results/ +/test_repos/ +*.log + +# Downloaded test framework +/scripts/.axci/ \ No newline at end of file diff --git a/README.md b/README.md index 5313e3eac..0e77cbf43 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,160 @@ -# RISCV VPLIC - Virtual Platform-Level Interrupt Controller +

riscv_vplic

-A Virtual Platform-Level Interrupt Controller (VPLIC) implementation for RISC-V architecture, designed for the ArceOS hypervisor ecosystem. +

RISC-V Virtual Platform-Level Interrupt Controller

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/riscv_vplic.svg)](https://crates.io/crates/riscv_vplic) +[![Docs.rs](https://docs.rs/riscv_vplic/badge.svg)](https://docs.rs/riscv_vplic) +[![Rust](https://img.shields.io/badge/edition-2021-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/riscv_vplic/blob/main/LICENSE) + +
+ +English | [中文](README_CN.md) + +# Introduction + +RISC-V Virtual Platform-Level Interrupt Controller, providing a PLIC 1.0.0 compliant interrupt controller emulation for RISC-V Hypervisors. Supports `#![no_std]`, suitable for bare-metal and virtualization development. + +This crate exports the following core components: + +- **`VPlicGlobal`** — Virtual PLIC global controller, managing interrupt priorities, pending interrupts, and active interrupts +- **PLIC Constants** — PLIC 1.0.0 memory map constant definitions (priority, pending, enable, context control, etc.) + +## Key Features + +- PLIC 1.0.0 compliant memory map +- Interrupt priority, pending status, and enable management +- Context-based interrupt handling with Claim/Complete mechanism +- Integration with Hypervisor device emulation framework (implements `BaseDeviceOps` trait) + +# Quick Start + +### Prerequisites + +- Rust nightly toolchain +- Rust components: rust-src, clippy, rustfmt + +```bash +# Install rustup (if not installed) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# Install nightly toolchain and components +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly +``` + +### Running Checks and Tests + +```bash +# 1. Clone the repository +git clone https://github.com/arceos-hypervisor/riscv_vplic.git +cd riscv_vplic + +# 2. Code check (format check + clippy + build + doc generation) +./scripts/check.sh + +# 3. Run tests +# Run all tests (unit tests + integration tests) +./scripts/test.sh + +# Run unit tests only +./scripts/test.sh unit + +# Run integration tests only +./scripts/test.sh integration + +# List all available test suites +./scripts/test.sh list + +# Specify unit test target +./scripts/test.sh unit --unit-targets x86_64-unknown-linux-gnu +``` + +# Integration + +### Installation + +Add to your `Cargo.toml`: + +```toml +[dependencies] +riscv_vplic = "0.2.2" +``` + +### Usage Example + +```rust +use riscv_vplic::VPlicGlobal; +use axaddrspace::GuestPhysAddr; + +fn main() { + // Create a virtual PLIC with 2 contexts + let vplic = VPlicGlobal::new( + GuestPhysAddr::from(0x0c000000), + Some(0x4000), + 2 + ); + + // Access PLIC properties + println!("PLIC address: {:#x}", vplic.addr.as_usize()); + println!("PLIC size: {:#x}", vplic.size); + println!("Contexts: {}", vplic.contexts_num); + + // Check interrupt bitmap status + assert!(vplic.assigned_irqs.lock().is_empty()); + assert!(vplic.pending_irqs.lock().is_empty()); + assert!(vplic.active_irqs.lock().is_empty()); +} +``` + +### PLIC Memory Map Constants + +```rust +use riscv_vplic::*; + +// Number of interrupt sources (PLIC 1.0.0 defines 1024) +assert_eq!(PLIC_NUM_SOURCES, 1024); + +// Memory map offsets +assert_eq!(PLIC_PRIORITY_OFFSET, 0x000000); // Priority registers +assert_eq!(PLIC_PENDING_OFFSET, 0x001000); // Pending registers +assert_eq!(PLIC_ENABLE_OFFSET, 0x002000); // Enable registers +assert_eq!(PLIC_CONTEXT_CTRL_OFFSET, 0x200000); // Context control registers + +// Context strides +assert_eq!(PLIC_ENABLE_STRIDE, 0x80); // Enable region stride +assert_eq!(PLIC_CONTEXT_STRIDE, 0x1000); // Context stride + +// Context internal control offsets +assert_eq!(PLIC_CONTEXT_THRESHOLD_OFFSET, 0x00); // Threshold register +assert_eq!(PLIC_CONTEXT_CLAIM_COMPLETE_OFFSET, 0x04); // Claim/Complete register +``` + +### Documentation + +Generate and view API documentation: + +```bash +cargo doc --no-deps --open +``` + +Online documentation: [docs.rs/riscv_vplic](https://docs.rs/riscv_vplic) ## Related Projects - [axdevice_base](https://github.com/arceos-hypervisor/axdevice_base) - Basic device abstraction - [axvisor_api](https://github.com/arceos-hypervisor/axvisor_api) - Hypervisor API definitions - [axaddrspace](https://github.com/arceos-hypervisor/axaddrspace) - Address space management + +# Contributing + +1. Fork the repository and create a branch +2. Run local checks: `./scripts/check.sh` +3. Run local tests: `./scripts/test.sh` +4. Submit a PR and pass CI checks + +# License + +This project is licensed under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for details. diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 000000000..595d47923 --- /dev/null +++ b/README_CN.md @@ -0,0 +1,160 @@ +

riscv_vplic

+ +

RISC-V 虚拟平台级中断控制器

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/riscv_vplic.svg)](https://crates.io/crates/riscv_vplic) +[![Docs.rs](https://docs.rs/riscv_vplic/badge.svg)](https://docs.rs/riscv_vplic) +[![Rust](https://img.shields.io/badge/edition-2021-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/riscv_vplic/blob/main/LICENSE) + +
+ +[English](README.md) | 中文 + +# 简介 + +RISC-V 虚拟平台级中断控制器(Virtual Platform-Level Interrupt Controller),为 RISC-V Hypervisor 提供符合 PLIC 1.0.0 规范的中断控制器模拟实现。支持 `#![no_std]`,可用于裸机和虚拟化开发。 + +本库导出以下核心内容: + +- **`VPlicGlobal`** — 虚拟 PLIC 全局控制器,管理中断优先级、待处理中断和活跃中断 +- **PLIC 常量** — PLIC 1.0.0 内存映射常量定义(优先级、待处理、使能、上下文控制等) + +## 主要特性 + +- 符合 PLIC 1.0.0 规范的内存映射 +- 支持中断优先级、待处理状态和使能管理 +- 基于 Context 的中断处理,支持 Claim/Complete 机制 +- 与 Hypervisor 设备模拟框架集成(实现 `BaseDeviceOps` trait) + +## 快速上手 + +### 环境要求 + +- Rust nightly 工具链 +- Rust 组件: rust-src, clippy, rustfmt + +```bash +# 安装 rustup(如未安装) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# 安装 nightly 工具链及组件 +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly +``` + +### 运行检查和测试 + +```bash +# 1. 克隆仓库 +git clone https://github.com/arceos-hypervisor/riscv_vplic.git +cd riscv_vplic + +# 2. 代码检查(格式检查 + clippy + 构建 + 文档生成) +./scripts/check.sh + +# 3. 运行测试 +# 运行全部测试(单元测试 + 集成测试) +./scripts/test.sh + +# 仅运行单元测试 +./scripts/test.sh unit + +# 仅运行集成测试 +./scripts/test.sh integration + +# 列出所有可用的测试套件 +./scripts/test.sh list + +# 指定单元测试目标 +./scripts/test.sh unit --unit-targets x86_64-unknown-linux-gnu +``` + +## 集成使用 + +### 安装 + +在 `Cargo.toml` 中添加: + +```toml +[dependencies] +riscv_vplic = "0.2.2" +``` + +### 使用示例 + +```rust +use riscv_vplic::VPlicGlobal; +use axaddrspace::GuestPhysAddr; + +fn main() { + // 创建一个支持 2 个上下文的虚拟 PLIC + let vplic = VPlicGlobal::new( + GuestPhysAddr::from(0x0c000000), + Some(0x4000), + 2 + ); + + // 访问 PLIC 属性 + println!("PLIC address: {:#x}", vplic.addr.as_usize()); + println!("PLIC size: {:#x}", vplic.size); + println!("Contexts: {}", vplic.contexts_num); + + // 检查中断位图状态 + assert!(vplic.assigned_irqs.lock().is_empty()); + assert!(vplic.pending_irqs.lock().is_empty()); + assert!(vplic.active_irqs.lock().is_empty()); +} +``` + +### PLIC 内存映射常量 + +```rust +use riscv_vplic::*; + +// 中断源数量(PLIC 1.0.0 定义为 1024) +assert_eq!(PLIC_NUM_SOURCES, 1024); + +// 内存映射偏移量 +assert_eq!(PLIC_PRIORITY_OFFSET, 0x000000); // 优先级寄存器 +assert_eq!(PLIC_PENDING_OFFSET, 0x001000); // 待处理寄存器 +assert_eq!(PLIC_ENABLE_OFFSET, 0x002000); // 使能寄存器 +assert_eq!(PLIC_CONTEXT_CTRL_OFFSET, 0x200000); // 上下文控制寄存器 + +// Context 间距 +assert_eq!(PLIC_ENABLE_STRIDE, 0x80); // 使能区域间距 +assert_eq!(PLIC_CONTEXT_STRIDE, 0x1000); // 上下文间距 + +// 上下文内部控制偏移 +assert_eq!(PLIC_CONTEXT_THRESHOLD_OFFSET, 0x00); // 阈值寄存器 +assert_eq!(PLIC_CONTEXT_CLAIM_COMPLETE_OFFSET, 0x04); // Claim/Complete 寄存器 +``` + +### 文档 + +生成并查看 API 文档: + +```bash +cargo doc --no-deps --open +``` + +在线文档:[docs.rs/riscv_vplic](https://docs.rs/riscv_vplic) + +## 相关项目 + +- [axdevice_base](https://github.com/arceos-hypervisor/axdevice_base) - 基础设备抽象 +- [axvisor_api](https://github.com/arceos-hypervisor/axvisor_api) - Hypervisor API 定义 +- [axaddrspace](https://github.com/arceos-hypervisor/axaddrspace) - 地址空间管理 + +# 贡献 + +1. Fork 仓库并创建分支 +2. 运行本地检查:`./scripts/check.sh` +3. 运行本地测试:`./scripts/test.sh` +4. 提交 PR 并通过 CI 检查 + +# 协议 + +本项目采用 Apache License, Version 2.0 许可证。详见 [LICENSE](LICENSE) 文件。 diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 06e0e5b0f..8fff43970 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-05-20" +channel = "nightly-2026-03-18" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] profile = "minimal" targets = [ diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 000000000..a7966f2d3 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# riscv_vplic 代码检查脚本 +# 下载并调用 axci 仓库中的检查脚本 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +COMPONENT_NAME="$(basename "$COMPONENT_DIR")" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行检查 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/check.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 000000000..94b4c1f72 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# +# riscv_vplic 测试脚本 +# 下载并调用 axci 仓库中的测试框架 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行测试,自动指定当前组件 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/tests.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/src/devops_impl.rs b/src/devops_impl.rs index fa5c05cc6..78a49b2cb 100644 --- a/src/devops_impl.rs +++ b/src/devops_impl.rs @@ -55,7 +55,7 @@ impl BaseDeviceOps for VPlicGlobal { // threshold offset if offset >= PLIC_CONTEXT_CTRL_OFFSET - && (offset - PLIC_CONTEXT_CTRL_OFFSET) % PLIC_CONTEXT_STRIDE == 0 => + && (offset - PLIC_CONTEXT_CTRL_OFFSET).is_multiple_of(PLIC_CONTEXT_STRIDE) => { perform_mmio_read(host_addr, width) } @@ -63,8 +63,7 @@ impl BaseDeviceOps for VPlicGlobal { offset if offset >= PLIC_CONTEXT_CTRL_OFFSET && (offset - PLIC_CONTEXT_CTRL_OFFSET - PLIC_CONTEXT_CLAIM_COMPLETE_OFFSET) - % PLIC_CONTEXT_STRIDE - == 0 => + .is_multiple_of(PLIC_CONTEXT_STRIDE) => { let context_id = (offset - PLIC_CONTEXT_CTRL_OFFSET - PLIC_CONTEXT_CLAIM_COMPLETE_OFFSET) @@ -145,7 +144,7 @@ impl BaseDeviceOps for VPlicGlobal { // threshold offset if offset >= PLIC_CONTEXT_CTRL_OFFSET - && (offset - PLIC_CONTEXT_CTRL_OFFSET) % PLIC_CONTEXT_STRIDE == 0 => + && (offset - PLIC_CONTEXT_CTRL_OFFSET).is_multiple_of(PLIC_CONTEXT_STRIDE) => { perform_mmio_write(host_addr, width, val) } @@ -153,8 +152,7 @@ impl BaseDeviceOps for VPlicGlobal { offset if offset >= PLIC_CONTEXT_CTRL_OFFSET && (offset - PLIC_CONTEXT_CTRL_OFFSET - PLIC_CONTEXT_CLAIM_COMPLETE_OFFSET) - % PLIC_CONTEXT_STRIDE - == 0 => + .is_multiple_of(PLIC_CONTEXT_STRIDE) => { // info!("vPlicGlobal: Writing to CLAIM/COMPLETE reg {reg:#x} val {val:#x}"); let context_id = diff --git a/tests/consts_tests.rs b/tests/consts_tests.rs new file mode 100644 index 000000000..31f1f6e48 --- /dev/null +++ b/tests/consts_tests.rs @@ -0,0 +1,46 @@ +use riscv_vplic::*; + +#[test] +fn test_plic_num_sources() { + assert_eq!(PLIC_NUM_SOURCES, 1024); +} + +#[test] +fn test_plic_priority_offset() { + assert_eq!(PLIC_PRIORITY_OFFSET, 0x000000); +} + +#[test] +fn test_plic_pending_offset() { + assert_eq!(PLIC_PENDING_OFFSET, 0x001000); +} + +#[test] +fn test_plic_enable_offset() { + assert_eq!(PLIC_ENABLE_OFFSET, 0x002000); +} + +#[test] +fn test_plic_enable_stride() { + assert_eq!(PLIC_ENABLE_STRIDE, 0x80); +} + +#[test] +fn test_plic_context_ctrl_offset() { + assert_eq!(PLIC_CONTEXT_CTRL_OFFSET, 0x200000); +} + +#[test] +fn test_plic_context_stride() { + assert_eq!(PLIC_CONTEXT_STRIDE, 0x1000); +} + +#[test] +fn test_plic_context_threshold_offset() { + assert_eq!(PLIC_CONTEXT_THRESHOLD_OFFSET, 0x00); +} + +#[test] +fn test_plic_context_claim_complete_offset() { + assert_eq!(PLIC_CONTEXT_CLAIM_COMPLETE_OFFSET, 0x04); +} diff --git a/tests/vplic_tests.rs b/tests/vplic_tests.rs new file mode 100644 index 000000000..515f37c65 --- /dev/null +++ b/tests/vplic_tests.rs @@ -0,0 +1,67 @@ +use axaddrspace::GuestPhysAddr; +use riscv_vplic::{ + VPlicGlobal, PLIC_CONTEXT_CLAIM_COMPLETE_OFFSET, PLIC_CONTEXT_CTRL_OFFSET, PLIC_CONTEXT_STRIDE, +}; + +/// Calculate minimum required size for VPlicGlobal with given contexts +fn calculate_min_size(contexts_num: usize) -> usize { + contexts_num * PLIC_CONTEXT_STRIDE + + PLIC_CONTEXT_CTRL_OFFSET + + PLIC_CONTEXT_CLAIM_COMPLETE_OFFSET + + 0x1000 +} + +#[test] +fn test_vplic_global_creation() { + let addr = GuestPhysAddr::from(0x0c000000); + let contexts_num = 2; + let size = calculate_min_size(contexts_num); + + let vplic = VPlicGlobal::new(addr, Some(size), contexts_num); + + assert_eq!(vplic.addr, addr); + assert_eq!(vplic.size, size); + assert_eq!(vplic.contexts_num, contexts_num); +} + +#[test] +fn test_vplic_global_with_different_contexts() { + let addr = GuestPhysAddr::from(0x0c000000); + + // Test with 1 context + let vplic = VPlicGlobal::new(addr, Some(0x400000), 1); + assert_eq!(vplic.contexts_num, 1); + + // Test with 4 contexts + let vplic = VPlicGlobal::new(addr, Some(0x400000), 4); + assert_eq!(vplic.contexts_num, 4); + + // Test with 8 contexts + let vplic = VPlicGlobal::new(addr, Some(0x400000), 8); + assert_eq!(vplic.contexts_num, 8); +} + +#[test] +#[should_panic(expected = "Size must be specified")] +fn test_vplic_global_size_none_panics() { + let addr = GuestPhysAddr::from(0x0c000000); + let _ = VPlicGlobal::new(addr, None, 2); +} + +#[test] +#[should_panic(expected = "exceeds region")] +fn test_vplic_global_insufficient_size_panics() { + let addr = GuestPhysAddr::from(0x0c000000); + // Size too small for 2 contexts + let _ = VPlicGlobal::new(addr, Some(0x1000), 2); +} + +#[test] +fn test_vplic_global_bitmaps_initialized_empty() { + let addr = GuestPhysAddr::from(0x0c000000); + let vplic = VPlicGlobal::new(addr, Some(0x400000), 2); + + assert!(vplic.assigned_irqs.lock().is_empty()); + assert!(vplic.pending_irqs.lock().is_empty()); + assert!(vplic.active_irqs.lock().is_empty()); +} From 0d6098329860b020cfca2a66aea5f019e57f5c4b Mon Sep 17 00:00:00 2001 From: szy Date: Thu, 19 Mar 2026 15:25:22 +0800 Subject: [PATCH 111/132] add ci targets --- .github/workflows/check.yml | 3 +++ .github/workflows/deploy.yml | 2 ++ .github/workflows/release.yml | 5 +++++ .github/workflows/test.yml | 2 ++ 4 files changed, 12 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 018bbbeb2..19bb99a18 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -13,3 +13,6 @@ on: jobs: check: uses: arceos-hypervisor/axci/.github/workflows/check.yml@main + with: + targets: '["riscv64gc-unknown-none-elf"]' + rust_components: '["rust-src", "clippy", "rustfmt"]' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 37b00c09c..c9651c123 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -12,5 +12,7 @@ jobs: deploy: uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main with: + targets: '["riscv64gc-unknown-none-elf"]' + rust_components: '["rust-src", "clippy", "rustfmt"]' verify_branch: true verify_version: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20f186395..96a189b37 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,9 +13,14 @@ on: jobs: check: uses: arceos-hypervisor/axci/.github/workflows/check.yml@main + with: + targets: '["riscv64gc-unknown-none-elf"]' + rust_components: '["rust-src", "clippy", "rustfmt"]' test: uses: arceos-hypervisor/axci/.github/workflows/test.yml@main + with: + targets: '["x86_64-unknown-linux-gnu"]' release: needs: [check, test] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e7520475f..42b59e71b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,3 +13,5 @@ on: jobs: test: uses: arceos-hypervisor/axci/.github/workflows/test.yml@main + with: + targets: '["x86_64-unknown-linux-gnu"]' From 1bbdd23d8141da7aa82b8a99a1671db39cf5f246 Mon Sep 17 00:00:00 2001 From: szy Date: Thu, 19 Mar 2026 15:29:31 +0800 Subject: [PATCH 112/132] fix ci bug --- .github/workflows/check.yml | 2 +- .github/workflows/deploy.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 19bb99a18..7ef6e1582 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -15,4 +15,4 @@ jobs: uses: arceos-hypervisor/axci/.github/workflows/check.yml@main with: targets: '["riscv64gc-unknown-none-elf"]' - rust_components: '["rust-src", "clippy", "rustfmt"]' + rust_components: 'rust-src, clippy, rustfmt' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c9651c123..b5357157a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -13,6 +13,6 @@ jobs: uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main with: targets: '["riscv64gc-unknown-none-elf"]' - rust_components: '["rust-src", "clippy", "rustfmt"]' + rust_components: 'rust-src, clippy, rustfmt' verify_branch: true verify_version: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 96a189b37..5155ed368 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: uses: arceos-hypervisor/axci/.github/workflows/check.yml@main with: targets: '["riscv64gc-unknown-none-elf"]' - rust_components: '["rust-src", "clippy", "rustfmt"]' + rust_components: 'rust-src, clippy, rustfmt' test: uses: arceos-hypervisor/axci/.github/workflows/test.yml@main From 91ab1d5750b5ce00b37b2399d800ba7b4e3b8fd7 Mon Sep 17 00:00:00 2001 From: szy Date: Thu, 19 Mar 2026 15:49:08 +0800 Subject: [PATCH 113/132] fix ci bug --- .github/workflows/check.yml | 1 - .github/workflows/deploy.yml | 1 - .github/workflows/release.yml | 3 --- .github/workflows/test.yml | 4 +--- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 7ef6e1582..765b0535a 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -15,4 +15,3 @@ jobs: uses: arceos-hypervisor/axci/.github/workflows/check.yml@main with: targets: '["riscv64gc-unknown-none-elf"]' - rust_components: 'rust-src, clippy, rustfmt' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b5357157a..8f47dbe89 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -13,6 +13,5 @@ jobs: uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main with: targets: '["riscv64gc-unknown-none-elf"]' - rust_components: 'rust-src, clippy, rustfmt' verify_branch: true verify_version: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5155ed368..c6da7f462 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,12 +15,9 @@ jobs: uses: arceos-hypervisor/axci/.github/workflows/check.yml@main with: targets: '["riscv64gc-unknown-none-elf"]' - rust_components: 'rust-src, clippy, rustfmt' test: uses: arceos-hypervisor/axci/.github/workflows/test.yml@main - with: - targets: '["x86_64-unknown-linux-gnu"]' release: needs: [check, test] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 42b59e71b..46ae1764d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,6 +12,4 @@ on: jobs: test: - uses: arceos-hypervisor/axci/.github/workflows/test.yml@main - with: - targets: '["x86_64-unknown-linux-gnu"]' + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main \ No newline at end of file From ca2e7f4b4623a0a6c3c6f6a8e3740651d0f57a3a Mon Sep 17 00:00:00 2001 From: szy Date: Thu, 19 Mar 2026 15:52:42 +0800 Subject: [PATCH 114/132] fix ci bug --- .github/workflows/test.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 46ae1764d..dc91940d4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,8 +5,10 @@ name: Test on: push: - branches: ['**'] - tags-ignore: ['**'] + branches: + - '**' + tags-ignore: + - '**' pull_request: workflow_dispatch: From 8e60d3f9afab9805099305304f48ef8689c89b81 Mon Sep 17 00:00:00 2001 From: YanLien Date: Thu, 19 Mar 2026 16:20:44 +0800 Subject: [PATCH 115/132] chore: update script comments to reference axdevice --- scripts/check.sh | 2 +- scripts/test.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/check.sh b/scripts/check.sh index 8c95f0757..2b4c57962 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# aarch64_sysreg 代码检查脚本 +# axdevice 代码检查脚本 # 下载并调用 axci 仓库中的检查脚本 # diff --git a/scripts/test.sh b/scripts/test.sh index 751cb0ef1..824f06a48 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# aarch64_sysreg 测试脚本 +# axdevice 测试脚本 # 下载并调用 axci 仓库中的测试框架 # From 2c7ea9ef45eae4cc488e74e3b986c5b1e6375c50 Mon Sep 17 00:00:00 2001 From: YanLien Date: Thu, 19 Mar 2026 16:32:06 +0800 Subject: [PATCH 116/132] ci: integrate axci shared workflows and update docs --- .github/config.json | 7 ++ .github/workflows/check.yml | 67 ++------------ .github/workflows/deploy.yml | 107 ++--------------------- .github/workflows/push.yml | 145 ++----------------------------- .github/workflows/release.yml | 159 +++------------------------------- .github/workflows/test.yml | 43 ++------- .gitignore | 8 ++ README.md | 137 ++++++++++++++++++++--------- README_CN.md | 118 +++++++++++++++++++++++++ scripts/check.sh | 35 ++++++++ scripts/test.sh | 34 ++++++++ tests/test.rs | 100 +++++++++++++++++++++ 12 files changed, 438 insertions(+), 522 deletions(-) create mode 100644 README_CN.md create mode 100755 scripts/check.sh create mode 100755 scripts/test.sh create mode 100644 tests/test.rs diff --git a/.github/config.json b/.github/config.json index f347e101e..4bbc2c022 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1,10 +1,17 @@ { + "component": { + "name": "axhvc", + "crate_name": "axhvc" + }, "targets": [ "aarch64-unknown-none-softfloat", "x86_64-unknown-linux-gnu", "x86_64-unknown-none", "riscv64gc-unknown-none-elf" ], + "unit_test_targets": [ + "x86_64-unknown-linux-gnu" + ], "rust_components": [ "rust-src", "clippy", diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 330fa15e9..018bbbeb2 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,66 +1,15 @@ -name: Quality Checks +# Quality Check Workflow +# References shared workflow from axci + +name: Check on: push: - branches: - - '**' - tags-ignore: - - '**' + branches: ['**'] + tags-ignore: ['**'] pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - rust_components: ${{ steps.config.outputs.rust_components }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) - - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT - check: - name: Check - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: ${{ needs.load-config.outputs.rust_components }} - targets: ${{ matrix.target }} - - - name: Check rust version - run: rustc --version --verbose - - - name: Check code format - run: cargo fmt --all -- --check - - - name: Build - run: cargo build --target ${{ matrix.target }} --all-features - - - name: Run clippy - run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings - - - name: Build documentation - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 882ba9310..37b00c09c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,3 +1,6 @@ +# Deploy Workflow +# References shared workflow from axci + name: Deploy on: @@ -5,105 +8,9 @@ on: tags: - 'v[0-9]+.[0-9]+.[0-9]+' -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: 'pages' - cancel-in-progress: false - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_deploy: ${{ steps.check.outputs.should_deploy }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check if tag is on main or master branch - id: check - run: | - git fetch origin main master || true - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Tag is on main or master branch" - echo "should_deploy=true" >> $GITHUB_OUTPUT - else - echo "✗ Tag is not on main or master branch, skipping deployment" - echo "Tag is on: $BRANCHES" - echo "should_deploy=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_deploy == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - test: - uses: ./.github/workflows/test.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - build: - name: Build documentation - runs-on: ubuntu-latest - needs: [verify-tag, check, test] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Build docs - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: target/doc - deploy: - name: Deploy to GitHub Pages - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: [verify-tag, build] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main + with: + verify_branch: true + verify_version: true diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a18de4a04..3ff391a4a 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,147 +1,16 @@ -# ═══════════════════════════════════════════════════════════════════════════════ -# 组件仓库 GitHub Actions 配置模板 -# ═══════════════════════════════════════════════════════════════════════════════ -# -# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 -# -# 【使用步骤】 -# ───────────────────────────────────────────────────────────────────────────── -# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: -# cp scripts/push.yml <子仓库>/.github/workflows/push.yml -# -# 2. 在子仓库中配置 Secret: -# GitHub 仓库 → Settings → Secrets → Actions → New repository secret -# 名称: PARENT_REPO_TOKEN -# 值: 具有主仓库 repo 权限的 Personal Access Token -# -# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): -# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits -# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) -# -# 【Token 权限要求】 -# ───────────────────────────────────────────────────────────────────────────── -# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: -# - repo (Full control of private repositories) -# 或 -# - Fine-grained token: Contents (Read and Write) -# -# 【触发条件】 -# ───────────────────────────────────────────────────────────────────────────── -# - 自动触发:推送到 dev 或 main 分支时 -# - 手动触发:Actions → Notify Parent Repository → Run workflow -# -# 【工作流程】 -# ───────────────────────────────────────────────────────────────────────────── -# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull -# -# 【注意事项】 -# ───────────────────────────────────────────────────────────────────────────── -# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 -# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 -# -# ═══════════════════════════════════════════════════════════════════════════════ - name: Notify Parent Repository -# 当有新的推送时触发 on: push: branches: - main - - master + - zcs workflow_dispatch: jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Get repository info - id: repo - env: - GH_REPO_NAME: ${{ github.event.repository.name }} - GH_REF_NAME: ${{ github.ref_name }} - GH_SERVER_URL: ${{ github.server_url }} - GH_REPOSITORY: ${{ github.repository }} - run: | - # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 - COMPONENT="$GH_REPO_NAME" - BRANCH="$GH_REF_NAME" - # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list - REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" - - echo "component=${COMPONENT}" >> $GITHUB_OUTPUT - echo "branch=${BRANCH}" >> $GITHUB_OUTPUT - echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT - - echo "Component: ${COMPONENT}" - echo "Branch: ${BRANCH}" - echo "Repo URL: ${REPO_URL}" - - - name: Notify parent repository - env: - # ── 需要修改 ────────────────────────────────────────────────────────── - PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 - # ── 无需修改 ────────────────────────────────────────────────────────── - DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} - # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 - COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - GIT_ACTOR: ${{ github.actor }} - GIT_SHA: ${{ github.sha }} - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" - - # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 - PAYLOAD=$(jq -n \ - --arg component "$COMPONENT" \ - --arg branch "$BRANCH" \ - --arg repo_url "$REPO_URL" \ - --arg commit "$GIT_SHA" \ - --arg message "$COMMIT_MESSAGE" \ - --arg author "$GIT_ACTOR" \ - '{ - event_type: "subtree-update", - client_payload: { - component: $component, - branch: $branch, - repo_url: $repo_url, - commit: $commit, - message: $message, - author: $author - } - }') - - curl --fail --show-error -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${DISPATCH_TOKEN}" \ - https://api.github.com/repos/${PARENT_REPO}/dispatches \ - -d "$PAYLOAD" - - echo "Notification sent successfully" - - - name: Create summary - env: - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - GIT_SHA: ${{ github.sha }} - GIT_ACTOR: ${{ github.actor }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY - echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY + notify-parent: + name: Notify Parent Repository + # 调用 axci 仓库的可复用工作流 + uses: arceos-hypervisor/axci/.github/workflows/push.yml@main + secrets: + PARENT_REPO_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e857b48b..20f186395 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,7 @@ +# Release Workflow +# References shared workflow from axci +# check + test must pass before release + name: Release on: @@ -6,155 +10,18 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' -permissions: - contents: write - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - is_prerelease: ${{ steps.check.outputs.is_prerelease }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check tag type and branch - id: check - run: | - git fetch origin main master dev || true - - TAG="${{ github.ref_name }}" - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - echo "Tag: $TAG" - echo "Branches containing this tag: $BRANCHES" - - # Check if it's a prerelease tag - if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[0-9]+$ ]]; then - echo "📦 Detected prerelease tag" - echo "is_prerelease=true" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -q 'origin/dev'; then - echo "✓ Prerelease tag is on dev branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Prerelease tag must be on dev branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "📦 Detected stable release tag" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Stable release tag is on main or master branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Stable release tag must be on main or master branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - else - echo "✗ Unknown tag format, skipping release" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - echo "should_release=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_release == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main test: - uses: ./.github/workflows/test.yml - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main release: - name: Create GitHub Release - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate release notes - id: release_notes - run: | - CURRENT_TAG="${{ github.ref_name }}" - - # Get previous tag - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) - - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then - echo "No previous tag found, this is the first release" - CHANGELOG="Initial release" - else - echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" - - # Generate changelog with commit messages - CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") - - if [ -z "$CHANGELOG" ]; then - CHANGELOG="No changes" - fi - fi - - # Write changelog to output file (multi-line) - { - echo "changelog<> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} - body: | - ## Changes - ${{ steps.release_notes.outputs.changelog }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish: - name: Publish to crates.io - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Dry run publish - run: cargo publish --dry-run - - - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + needs: [check, test] + uses: arceos-hypervisor/axci/.github/workflows/release.yml@main + with: + verify_branch: true + verify_version: true + secrets: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc3b293d9..6a58ef8e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,3 +1,6 @@ +# Integration Test Workflow +# References shared workflow from axci + name: Test on: @@ -7,44 +10,8 @@ on: tags-ignore: - '**' pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - test: - name: Test - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - # - name: Run tests - # run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture - - # - name: Run doc tests - # run: cargo test --target ${{ matrix.target }} --doc - - name: Run tests - run: echo "Tests are skipped!" + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main diff --git a/.gitignore b/.gitignore index ff78c42af..22d7b02fd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,11 @@ /.vscode .DS_Store Cargo.lock + +# Test results (generated by shared test framework) +/test-results/ +/test_repos/ +*.log + +# Downloaded test framework +/scripts/.axci/ diff --git a/README.md b/README.md index ce5cb55cf..c62e22d28 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,118 @@ -# axhvc +

axhvc

-[![Crates.io](https://img.shields.io/crates/v/axhvc)](https://crates.io/crates/axhvc) +

AxVisor HyperCall Definitions

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/axhvc.svg)](https://crates.io/crates/axhvc) [![Docs.rs](https://docs.rs/axhvc/badge.svg)](https://docs.rs/axhvc) -[![CI](https://github.com/arceos-hypervisor/axhvc/actions/workflows/ci.yml/badge.svg)](https://github.com/arceos-hypervisor/axhvc/actions/workflows/ci.yml) +[![Rust](https://img.shields.io/badge/edition-2024-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/axhvc/blob/main/LICENSE) + +
+ +English | [中文](README_CN.md) + +# Introduction + +`axhvc` provides AxVisor hypercall definitions for guest-hypervisor communication. It is a lightweight `#![no_std]` crate intended for bare-metal guests, hypervisors, and low-level virtualization components across x86_64, RISC-V, and AArch64 platforms. + +This library exports three core public types: -AxVisor HyperCall definitions for guest-hypervisor communication. +- **`HyperCallCode`** - Enumerates all supported AxVisor hypercall operations +- **`InvalidHyperCallCode`** - Represents conversion errors for invalid numeric hypercall values +- **`HyperCallResult`** - Alias of `AxResult` used by hypercall handlers -## Overview +`HyperCallCode` supports `TryFrom` conversion and includes variants for hypervisor control and IVC channel management. -This crate provides the hypercall interface for [AxVisor](https://github.com/arceos-hypervisor/axvisor), a type-1 hypervisor based on [ArceOS](https://github.com/arceos-org/arceos). It defines the hypercall codes and result types used for communication between guest VMs and the hypervisor. +## Quick Start -## Features +### Requirements -- `no_std` compatible - suitable for bare-metal and embedded environments -- Defines all supported hypercall operations -- Provides type-safe hypercall codes with numeric enum conversion -- Cross-platform support (x86_64, RISC-V, AArch64) +- Rust nightly toolchain +- Rust components: rust-src, clippy, rustfmt -## Supported Hypercalls +```bash +# Install rustup (if not installed) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# Install nightly toolchain and components +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly +``` + +### Run Check and Test + +```bash +# 1. Enter the repository +cd axhvc + +# 2. Code check +./scripts/check.sh + +# 3. Run tests +./scripts/test.sh +``` -| Code | Name | Description | -|------|------|-------------| -| 0 | `HypervisorDisable` | Disable the hypervisor | -| 1 | `HyperVisorPrepareDisable` | Prepare to disable the hypervisor | -| 2 | `HyperVisorDebug` | Debug hypercall (development only) | -| 3 | `HIVCPublishChannel` | Publish an IVC shared memory channel | -| 4 | `HIVCSubscribChannel` | Subscribe to an IVC shared memory channel | -| 5 | `HIVCUnPublishChannel` | Unpublish an IVC shared memory channel | -| 6 | `HIVCUnSubscribChannel` | Unsubscribe from an IVC shared memory channel | +## Integration -## Usage +### Installation -Add this to your `Cargo.toml`: +Add to your `Cargo.toml`: ```toml [dependencies] -axhvc = "0.1" +axhvc = "0.2.0" ``` ### Example -```rust,ignore -use axhvc::{HyperCallCode, HyperCallResult}; - -fn handle_hypercall(code: HyperCallCode) -> HyperCallResult { - match code { - HyperCallCode::HypervisorDisable => { - // Handle hypervisor disable request - Ok(0) - } - HyperCallCode::HIVCPublishChannel => { - // Handle IVC channel publish request - Ok(0) - } - _ => Err(axerrno::AxError::Unsupported), - } +```rust +use axerrno::ax_err; +use axhvc::{HyperCallCode, HyperCallResult, InvalidHyperCallCode}; + +fn handle_hypercall(code: u32) -> Result { + let code = HyperCallCode::try_from(code)?; + + let result = match code { + HyperCallCode::HypervisorDisable => Ok(0), + HyperCallCode::HyperVisorPrepareDisable => Ok(0), + HyperCallCode::HIVCPublishChannel => Ok(0x1000), + _ => ax_err!(Unsupported), + }; + + Ok(result) +} + +fn main() { + let code = HyperCallCode::try_from(3u32).unwrap(); + assert_eq!(code as u32, 3); + + let result = handle_hypercall(code as u32).unwrap().unwrap(); + assert_eq!(result, 0x1000); + + let invalid = HyperCallCode::try_from(7u32); + assert!(invalid.is_err()); } ``` -## License +### Documentation + +Generate and view API documentation: + +```bash +cargo doc --no-deps --open +``` + +Online documentation: [docs.rs/axhvc](https://docs.rs/axhvc) + +# Contributing + +1. Fork the repository and create a branch +2. Run local check: `./scripts/check.sh` +3. Run local tests: `./scripts/test.sh` +4. Submit PR and pass CI checks + +# License -Axhvc is licensed under the Apache License, Version 2.0. See the [LICENSE](./LICENSE) file for details. \ No newline at end of file +Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details. diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 000000000..cb93e9bd8 --- /dev/null +++ b/README_CN.md @@ -0,0 +1,118 @@ +

axhvc

+ +

AxVisor HyperCall 定义库

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/axhvc.svg)](https://crates.io/crates/axhvc) +[![Docs.rs](https://docs.rs/axhvc/badge.svg)](https://docs.rs/axhvc) +[![Rust](https://img.shields.io/badge/edition-2024-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/axhvc/blob/main/LICENSE) + +
+ +[English](README.md) | 中文 + +# Introduction + +`axhvc` 提供 AxVisor 中 guest 与 hypervisor 通信所需的 hypercall 定义。它是一个轻量级的 `#![no_std]` crate,适用于裸机 guest、hypervisor 以及 x86_64、RISC-V、AArch64 等平台上的底层虚拟化组件。 + +该库导出三个核心公开类型: + +- **`HyperCallCode`** - 枚举所有受支持的 AxVisor hypercall 操作 +- **`InvalidHyperCallCode`** - 表示非法数值转换为 hypercall 编码时的错误 +- **`HyperCallResult`** - Hypercall 处理函数使用的 `AxResult` 类型别名 + +`HyperCallCode` 支持 `TryFrom` 转换,当前覆盖 hypervisor 控制和 IVC 通道管理两类操作。 + +## Quick Start + +### Requirements + +- Rust nightly 工具链 +- Rust 组件:rust-src、clippy、rustfmt + +```bash +# 安装 rustup(如果尚未安装) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# 安装 nightly 工具链与所需组件 +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly +``` + +### Run Check and Test + +```bash +# 1. 进入仓库目录 +cd axhvc + +# 2. 代码检查 +./scripts/check.sh + +# 3. 运行测试 +./scripts/test.sh +``` + +## Integration + +### Installation + +将以下依赖加入 `Cargo.toml`: + +```toml +[dependencies] +axhvc = "0.2.0" +``` + +### Example + +```rust +use axerrno::ax_err; +use axhvc::{HyperCallCode, HyperCallResult, InvalidHyperCallCode}; + +fn handle_hypercall(code: u32) -> Result { + let code = HyperCallCode::try_from(code)?; + + let result = match code { + HyperCallCode::HypervisorDisable => Ok(0), + HyperCallCode::HyperVisorPrepareDisable => Ok(0), + HyperCallCode::HIVCPublishChannel => Ok(0x1000), + _ => ax_err!(Unsupported), + }; + + Ok(result) +} + +fn main() { + let code = HyperCallCode::try_from(3u32).unwrap(); + assert_eq!(code as u32, 3); + + let result = handle_hypercall(code as u32).unwrap().unwrap(); + assert_eq!(result, 0x1000); + + let invalid = HyperCallCode::try_from(7u32); + assert!(invalid.is_err()); +} +``` + +### Documentation + +生成并查看 API 文档: + +```bash +cargo doc --no-deps --open +``` + +在线文档: [docs.rs/axhvc](https://docs.rs/axhvc) + +# Contributing + +1. Fork 仓库并创建分支 +2. 本地运行检查:`./scripts/check.sh` +3. 本地运行测试:`./scripts/test.sh` +4. 提交 PR 并通过 CI 检查 + +# License + +本项目基于 Apache License 2.0 许可证发布。详见 [LICENSE](LICENSE)。 diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 000000000..01c72be57 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# axhvc 代码检查脚本 +# 下载并调用 axci 仓库中的检查脚本 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +COMPONENT_NAME="$(basename "$COMPONENT_DIR")" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行检查 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/check.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 000000000..421bed113 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# +# axhvc 测试脚本 +# 下载并调用 axci 仓库中的测试框架 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行测试,自动指定当前组件 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/tests.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 000000000..16be1d375 --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,100 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use axhvc::{HyperCallCode, InvalidHyperCallCode}; + +#[test] +fn test_hypercall_code_from_u32_valid() { + assert_eq!( + HyperCallCode::try_from(0u32).unwrap(), + HyperCallCode::HypervisorDisable + ); + assert_eq!( + HyperCallCode::try_from(1u32).unwrap(), + HyperCallCode::HyperVisorPrepareDisable + ); + assert_eq!( + HyperCallCode::try_from(2u32).unwrap(), + HyperCallCode::HyperVisorDebug + ); + assert_eq!( + HyperCallCode::try_from(3u32).unwrap(), + HyperCallCode::HIVCPublishChannel + ); + assert_eq!( + HyperCallCode::try_from(4u32).unwrap(), + HyperCallCode::HIVCSubscribChannel + ); + assert_eq!( + HyperCallCode::try_from(5u32).unwrap(), + HyperCallCode::HIVCUnPublishChannel + ); + assert_eq!( + HyperCallCode::try_from(6u32).unwrap(), + HyperCallCode::HIVCUnSubscribChannel + ); +} + +#[test] +fn test_hypercall_code_from_u32_invalid() { + assert!(HyperCallCode::try_from(7u32).is_err()); + assert!(HyperCallCode::try_from(100u32).is_err()); + assert!(HyperCallCode::try_from(u32::MAX).is_err()); +} + +#[test] +fn test_hypercall_code_to_u32() { + assert_eq!(HyperCallCode::HypervisorDisable as u32, 0); + assert_eq!(HyperCallCode::HyperVisorPrepareDisable as u32, 1); + assert_eq!(HyperCallCode::HyperVisorDebug as u32, 2); + assert_eq!(HyperCallCode::HIVCPublishChannel as u32, 3); + assert_eq!(HyperCallCode::HIVCSubscribChannel as u32, 4); + assert_eq!(HyperCallCode::HIVCUnPublishChannel as u32, 5); + assert_eq!(HyperCallCode::HIVCUnSubscribChannel as u32, 6); +} + +#[test] +fn test_hypercall_code_equality() { + assert_eq!( + HyperCallCode::HypervisorDisable, + HyperCallCode::HypervisorDisable + ); + assert_ne!( + HyperCallCode::HypervisorDisable, + HyperCallCode::HyperVisorDebug + ); +} + +#[test] +fn test_invalid_hypercall_code_display() { + let err = InvalidHyperCallCode(0xFF); + assert_eq!(format!("{}", err), "invalid hypercall code: 0xff"); + + let err = InvalidHyperCallCode(7); + assert_eq!(format!("{}", err), "invalid hypercall code: 0x7"); +} + +#[test] +fn test_invalid_hypercall_code_debug() { + let err = InvalidHyperCallCode(42); + assert_eq!(format!("{:?}", err), "InvalidHyperCallCode(42)"); +} + +#[test] +fn test_invalid_hypercall_code_from_try_from() { + let result = HyperCallCode::try_from(999u32); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(err.0, 999); +} From b56cf99403e5b49b8e607c48a45c60a252c7be0a Mon Sep 17 00:00:00 2001 From: szy Date: Thu, 19 Mar 2026 16:50:24 +0800 Subject: [PATCH 117/132] refactor: add scripts/tests/CI workflows and docs --- .github/config.json | 7 + .github/workflows/check.yml | 69 +----- .github/workflows/deploy.yml | 125 +--------- .github/workflows/push.yml | 143 +----------- .github/workflows/release.yml | 161 ++----------- .github/workflows/test.yml | 43 +--- .gitignore | 8 + README.md | 7 +- README_CN.md | 204 ++++++++++++++++ rust-toolchain.toml | 2 +- scripts/check.sh | 35 +++ scripts/test.sh | 34 +++ tests/register_tests.rs | 425 ++++++++++++++++++++++++++++++++++ 13 files changed, 763 insertions(+), 500 deletions(-) create mode 100644 README_CN.md create mode 100755 scripts/check.sh create mode 100755 scripts/test.sh create mode 100644 tests/register_tests.rs diff --git a/.github/config.json b/.github/config.json index c750554c4..9c42e8300 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1,7 +1,14 @@ { + "component": { + "name": "riscv-h", + "crate_name": "riscv_h" + }, "targets": [ "riscv64gc-unknown-none-elf" ], + "unit_test_targets": [ + "x86_64-unknown-linux-gnu" + ], "rust_components": [ "rust-src", "clippy", diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 330fa15e9..765b0535a 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,66 +1,17 @@ -name: Quality Checks +# Quality Check Workflow +# References shared workflow from axci + +name: Check on: push: - branches: - - '**' - tags-ignore: - - '**' + branches: ['**'] + tags-ignore: ['**'] pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - rust_components: ${{ steps.config.outputs.rust_components }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) - - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT - check: - name: Check - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: ${{ needs.load-config.outputs.rust_components }} - targets: ${{ matrix.target }} - - - name: Check rust version - run: rustc --version --verbose - - - name: Check code format - run: cargo fmt --all -- --check - - - name: Build - run: cargo build --target ${{ matrix.target }} --all-features - - - name: Run clippy - run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings - - - name: Build documentation - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main + with: + targets: '["riscv64gc-unknown-none-elf"]' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fda9a323d..8f47dbe89 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,3 +1,6 @@ +# Deploy Workflow +# References shared workflow from axci + name: Deploy on: @@ -5,122 +8,10 @@ on: tags: - 'v[0-9]+.[0-9]+.[0-9]+' -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: 'pages' - cancel-in-progress: false - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_deploy: ${{ steps.check.outputs.should_deploy }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check if tag is on main or master branch - id: check - run: | - git fetch origin main master || true - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Tag is on main or master branch" - echo "should_deploy=true" >> $GITHUB_OUTPUT - else - echo "✗ Tag is not on main or master branch, skipping deployment" - echo "Tag is on: $BRANCHES" - echo "should_deploy=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_deploy == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - test: - uses: ./.github/workflows/test.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - build: - name: Build documentation - runs-on: ubuntu-latest - needs: [verify-tag, check, test] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Build docs - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: | - # Build documentation - cargo doc --no-deps --all-features - - # Auto-detect documentation directory - # Check if doc exists in target/doc or target/*/doc - if [ -d "target/doc" ]; then - DOC_DIR="target/doc" - else - # Find doc directory under target/*/doc pattern - DOC_DIR=$(find target -type d -name doc -path "target/*/doc" | head -n 1) - if [ -z "$DOC_DIR" ]; then - echo "Error: Could not find documentation directory" - exit 1 - fi - fi - - echo "Documentation found in: $DOC_DIR" - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > "${DOC_DIR}/index.html" - echo "DOC_DIR=${DOC_DIR}" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ${{ env.DOC_DIR }} - deploy: - name: Deploy to GitHub Pages - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: [verify-tag, build] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main + with: + targets: '["riscv64gc-unknown-none-elf"]' + verify_branch: true + verify_version: true diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a18de4a04..dda571781 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,49 +1,5 @@ -# ═══════════════════════════════════════════════════════════════════════════════ -# 组件仓库 GitHub Actions 配置模板 -# ═══════════════════════════════════════════════════════════════════════════════ -# -# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 -# -# 【使用步骤】 -# ───────────────────────────────────────────────────────────────────────────── -# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: -# cp scripts/push.yml <子仓库>/.github/workflows/push.yml -# -# 2. 在子仓库中配置 Secret: -# GitHub 仓库 → Settings → Secrets → Actions → New repository secret -# 名称: PARENT_REPO_TOKEN -# 值: 具有主仓库 repo 权限的 Personal Access Token -# -# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): -# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits -# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) -# -# 【Token 权限要求】 -# ───────────────────────────────────────────────────────────────────────────── -# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: -# - repo (Full control of private repositories) -# 或 -# - Fine-grained token: Contents (Read and Write) -# -# 【触发条件】 -# ───────────────────────────────────────────────────────────────────────────── -# - 自动触发:推送到 dev 或 main 分支时 -# - 手动触发:Actions → Notify Parent Repository → Run workflow -# -# 【工作流程】 -# ───────────────────────────────────────────────────────────────────────────── -# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull -# -# 【注意事项】 -# ───────────────────────────────────────────────────────────────────────────── -# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 -# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 -# -# ═══════════════════════════════════════════════════════════════════════════════ - name: Notify Parent Repository -# 当有新的推送时触发 on: push: branches: @@ -52,96 +8,9 @@ on: workflow_dispatch: jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Get repository info - id: repo - env: - GH_REPO_NAME: ${{ github.event.repository.name }} - GH_REF_NAME: ${{ github.ref_name }} - GH_SERVER_URL: ${{ github.server_url }} - GH_REPOSITORY: ${{ github.repository }} - run: | - # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 - COMPONENT="$GH_REPO_NAME" - BRANCH="$GH_REF_NAME" - # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list - REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" - - echo "component=${COMPONENT}" >> $GITHUB_OUTPUT - echo "branch=${BRANCH}" >> $GITHUB_OUTPUT - echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT - - echo "Component: ${COMPONENT}" - echo "Branch: ${BRANCH}" - echo "Repo URL: ${REPO_URL}" - - - name: Notify parent repository - env: - # ── 需要修改 ────────────────────────────────────────────────────────── - PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 - # ── 无需修改 ────────────────────────────────────────────────────────── - DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} - # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 - COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - GIT_ACTOR: ${{ github.actor }} - GIT_SHA: ${{ github.sha }} - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" - - # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 - PAYLOAD=$(jq -n \ - --arg component "$COMPONENT" \ - --arg branch "$BRANCH" \ - --arg repo_url "$REPO_URL" \ - --arg commit "$GIT_SHA" \ - --arg message "$COMMIT_MESSAGE" \ - --arg author "$GIT_ACTOR" \ - '{ - event_type: "subtree-update", - client_payload: { - component: $component, - branch: $branch, - repo_url: $repo_url, - commit: $commit, - message: $message, - author: $author - } - }') - - curl --fail --show-error -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${DISPATCH_TOKEN}" \ - https://api.github.com/repos/${PARENT_REPO}/dispatches \ - -d "$PAYLOAD" - - echo "Notification sent successfully" - - - name: Create summary - env: - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - GIT_SHA: ${{ github.sha }} - GIT_ACTOR: ${{ github.actor }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY - echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY + notify-parent: + name: Notify Parent Repository + # 调用 axci 仓库的可复用工作流 + uses: arceos-hypervisor/axci/.github/workflows/push.yml@main + secrets: + PARENT_REPO_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e857b48b..c6da7f462 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,7 @@ +# Release Workflow +# References shared workflow from axci +# check + test must pass before release + name: Release on: @@ -6,155 +10,20 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' -permissions: - contents: write - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - is_prerelease: ${{ steps.check.outputs.is_prerelease }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check tag type and branch - id: check - run: | - git fetch origin main master dev || true - - TAG="${{ github.ref_name }}" - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - echo "Tag: $TAG" - echo "Branches containing this tag: $BRANCHES" - - # Check if it's a prerelease tag - if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[0-9]+$ ]]; then - echo "📦 Detected prerelease tag" - echo "is_prerelease=true" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -q 'origin/dev'; then - echo "✓ Prerelease tag is on dev branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Prerelease tag must be on dev branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "📦 Detected stable release tag" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Stable release tag is on main or master branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Stable release tag must be on main or master branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - else - echo "✗ Unknown tag format, skipping release" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - echo "should_release=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_release == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main + with: + targets: '["riscv64gc-unknown-none-elf"]' test: - uses: ./.github/workflows/test.yml - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main release: - name: Create GitHub Release - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate release notes - id: release_notes - run: | - CURRENT_TAG="${{ github.ref_name }}" - - # Get previous tag - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) - - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then - echo "No previous tag found, this is the first release" - CHANGELOG="Initial release" - else - echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" - - # Generate changelog with commit messages - CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") - - if [ -z "$CHANGELOG" ]; then - CHANGELOG="No changes" - fi - fi - - # Write changelog to output file (multi-line) - { - echo "changelog<> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} - body: | - ## Changes - ${{ steps.release_notes.outputs.changelog }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish: - name: Publish to crates.io - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Dry run publish - run: cargo publish --dry-run - - - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + needs: [check, test] + uses: arceos-hypervisor/axci/.github/workflows/release.yml@main + with: + verify_branch: true + verify_version: true + secrets: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc3b293d9..6a58ef8e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,3 +1,6 @@ +# Integration Test Workflow +# References shared workflow from axci + name: Test on: @@ -7,44 +10,8 @@ on: tags-ignore: - '**' pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - test: - name: Test - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - # - name: Run tests - # run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture - - # - name: Run doc tests - # run: cargo test --target ${{ matrix.target }} --doc - - name: Run tests - run: echo "Tests are skipped!" + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main diff --git a/.gitignore b/.gitignore index 07283388f..6f3a252e4 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,11 @@ target # Added by cargo /target + +# Test results (generated by shared test framework) +/test-results/ +/test_repos/ +*.log + +# Downloaded test framework +/scripts/.axci/ diff --git a/README.md b/README.md index 31523c42f..38e200e30 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ # riscv-h -[![CI](https://github.com/arceos-hypervisor/riscv-h/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/arceos-hypervisor/riscv-h/actions/workflows/ci.yml) [![Crates.io](https://img.shields.io/crates/v/riscv-h.svg)](https://crates.io/crates/riscv-h) -[![Documentation](https://docs.rs/riscv-h/badge.svg)](https://docs.rs/riscv-h) +[![Docs.rs](https://docs.rs/riscv-h/badge.svg)](https://docs.rs/riscv-h) +[![Rust](https://img.shields.io/badge/edition-2024-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/riscv-h/blob/main/LICENSE) + +English | [中文](README_CN.md) RISC-V Hypervisor Extension Register Support diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 000000000..4a488c233 --- /dev/null +++ b/README_CN.md @@ -0,0 +1,204 @@ +

riscv-h

+ +

RISC-V 虚拟化扩展寄存器支持库

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/riscv-h.svg)](https://crates.io/crates/riscv-h) +[![Docs.rs](https://docs.rs/riscv-h/badge.svg)](https://docs.rs/riscv-h) +[![Rust](https://img.shields.io/badge/edition-2024-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/riscv-h/blob/main/LICENSE) + +
+ +[English](README.md) | 中文 + +# 简介 + +RISC-V 虚拟化扩展寄存器支持库,提供 RISC-V Hypervisor Extension 中定义的控制和状态寄存器(CSR)的低级访问接口。支持 `#![no_std]`,可用于裸机和操作系统内核开发。 + +本库导出以下核心模块: + +- **`register::hstatus`** — 虚拟化管理员状态寄存器 +- **`register::hgatp`** — 虚拟化客户地址翻译和保护寄存器 +- **`register::hvip`** — 虚拟化虚拟中断挂起寄存器 +- **`register::vsstatus`** — 虚拟管理员状态寄存器 +- **`register::vsatp`** — 虚拟管理员地址翻译和保护寄存器 + +所有寄存器类型均实现了 `Copy`、`Clone`、`Debug` trait,并提供类型安全的位字段访问方法。 + +## 快速上手 + +### 环境要求 + +- Rust nightly 工具链 +- Rust 组件: rust-src, clippy, rustfmt, llvm-tools +- 目标平台: riscv64gc-unknown-none-elf + +```bash +# 安装 rustup(如未安装) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# 安装 nightly 工具链及组件 +rustup install nightly +rustup component add rust-src clippy rustfmt llvm-tools --toolchain nightly + +# 添加 RISC-V 目标 +rustup target add riscv64gc-unknown-none-elf --toolchain nightly +``` + +### 运行检查和测试 + +```bash +# 1. 克隆仓库 +git clone https://github.com/arceos-hypervisor/riscv-h.git +cd riscv-h + +# 2. 代码检查(格式检查 + clippy + 构建 + 文档生成) +./scripts/check.sh + +# 3. 运行测试 +# 运行全部测试(单元测试 + 集成测试) +./scripts/test.sh + +# 仅运行单元测试 +./scripts/test.sh unit + +# 仅运行集成测试 +./scripts/test.sh integration + +# 列出所有可用的测试套件 +./scripts/test.sh list + +# 指定单元测试目标 +./scripts/test.sh unit --unit-targets x86_64-unknown-linux-gnu +``` + +## 集成使用 + +### 安装 + +在 `Cargo.toml` 中添加: + +```toml +[dependencies] +riscv-h = "0.2.0" +``` + +### 使用示例 + +```rust +#![no_std] + +use riscv_h::register::{hstatus, hgatp, hvip}; + +fn main() { + // 读取虚拟化管理员状态寄存器 + let hstatus = hstatus::read(); + + // 检查是否处于虚拟化模式 + if hstatus.spv() { + // 访问各个字段 + let vsxl = hstatus.vsxl(); // 虚拟管理员 XLEN + let vtw = hstatus.vtw(); // 陷阱 WFI + let vtsr = hstatus.vtsr(); // 陷阱 SRET + let vgein = hstatus.vgein(); // 虚拟客户外部中断号 + + setup_guest_translation(); + } + + // 配置虚拟中断挂起 + let mut hvip_val = hvip::Hvip::from_bits(0); + hvip_val.set_vssip(true); // 设置虚拟管理员软件中断挂起 + hvip_val.set_vstip(true); // 设置虚拟管理员定时器中断挂起 + hvip_val.set_vseip(true); // 设置虚拟管理员外部中断挂起 + + unsafe { + hvip_val.write(); + } +} + +fn setup_guest_translation() { + unsafe { + // 配置客户地址翻译 + let mut hgatp = hgatp::Hgatp::from_bits(0); + hgatp.set_mode(hgatp::HgatpValues::Sv48x4); // 使用 Sv48x4 模式 + hgatp.set_vmid(1); // 设置 VMID + hgatp.set_ppn(0x1000); // 设置根页表 PPN + hgatp.write(); + } +} +``` + +### 异常和中断委托 + +```rust +use riscv_h::register::{hedeleg, hideleg}; + +unsafe { + // 将常见异常委托给 VS-mode + hedeleg::set_ex2(true); // 非法指令 + hedeleg::set_ex8(true); // U-mode 环境调用 + hedeleg::set_ex12(true); // 指令页面错误 + hedeleg::set_ex13(true); // 加载页面错误 + hedeleg::set_ex15(true); // 存储页面错误 + + // 将定时器和软件中断委托给 VS-mode + hideleg::set_vstie(true); // VS-mode 定时器中断 + hideleg::set_vssie(true); // VS-mode 软件中断 +} +``` + +## 支持的寄存器 + +### 虚拟化控制寄存器 + +| 寄存器 | 描述 | CSR 地址 | +|--------|------|----------| +| `hstatus` | 虚拟化管理员状态寄存器 | 0x600 | +| `hedeleg` | 虚拟化异常委托寄存器 | 0x602 | +| `hideleg` | 虚拟化中断委托寄存器 | 0x603 | +| `hie` | 虚拟化中断使能寄存器 | 0x604 | +| `hcounteren` | 虚拟化计数器使能寄存器 | 0x606 | +| `hgatp` | 虚拟化客户地址翻译和保护寄存器 | 0x680 | + +### 虚拟管理员寄存器 + +| 寄存器 | 描述 | CSR 地址 | +|--------|------|----------| +| `vsstatus` | 虚拟管理员状态寄存器 | 0x200 | +| `vsie` | 虚拟管理员中断使能寄存器 | 0x204 | +| `vstvec` | 虚拟管理员陷阱向量寄存器 | 0x205 | +| `vsscratch` | 虚拟管理员暂存寄存器 | 0x240 | +| `vsepc` | 虚拟管理员异常程序计数器 | 0x241 | +| `vscause` | 虚拟管理员原因寄存器 | 0x242 | +| `vstval` | 虚拟管理员陷阱值寄存器 | 0x243 | +| `vsatp` | 虚拟管理员地址翻译和保护寄存器 | 0x280 | + +### 其他寄存器 + +- **中断管理**: `hip`, `hvip`, `hgeie`, `hgeip` +- **时间管理**: `htimedelta`, `htimedeltah` +- **陷阱信息**: `htval`, `htinst` +- **虚拟管理员中断**: `vsip` + +### 文档 + +生成并查看 API 文档: + +```bash +cargo doc --no-deps --open +``` + +在线文档:[docs.rs/riscv-h](https://docs.rs/riscv-h) + +# 贡献 + +1. Fork 仓库并创建分支 +2. 运行本地检查:`./scripts/check.sh` +3. 运行本地测试:`./scripts/test.sh` +4. 提交 PR 并通过 CI 检查 + +# 协议 + +本项目采用 Apache License, Version 2.0 许可证。详见 [LICENSE](LICENSE) 文件。 diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 62898214e..adb3ba8db 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-05-20" +channel = "nightly-2026-03-18" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] profile = "minimal" targets = [ diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 000000000..1613cbc49 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# riscv-h 代码检查脚本 +# 下载并调用 axci 仓库中的检查脚本 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +COMPONENT_NAME="$(basename "$COMPONENT_DIR")" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行检查 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/check.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 000000000..a9489285b --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# +# riscv-h 测试脚本 +# 下载并调用 axci 仓库中的测试框架 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行测试,自动指定当前组件 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/tests.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/tests/register_tests.rs b/tests/register_tests.rs new file mode 100644 index 000000000..0d91b623c --- /dev/null +++ b/tests/register_tests.rs @@ -0,0 +1,425 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Unit tests for individual RISC-V hypervisor registers + +use riscv_h::register::{hcounteren, hie, hip, hvip, vsatp, vsie, vsip, vstvec}; + +// ============================================================================ +// Hypervisor Control Registers Tests +// ============================================================================ + +mod hvip_tests { + use super::*; + + #[test] + fn test_hvip_bit_fields() { + let mut hvip = hvip::Hvip::from_bits(0); + + // Test VSSIP (bit 2) + assert!(!hvip.vssip()); + hvip.set_vssip(true); + assert!(hvip.vssip()); + assert_eq!(hvip.bits(), 0b100); + + // Test VSTIP (bit 6) + hvip.set_vstip(true); + assert!(hvip.vstip()); + assert_eq!(hvip.bits(), 0b100_0100); + + // Test VSEIP (bit 10) + hvip.set_vseip(true); + assert!(hvip.vseip()); + // bit 2 + bit 6 + bit 10 = 4 + 64 + 1024 = 1092 + assert_eq!(hvip.bits(), (1 << 2) | (1 << 6) | (1 << 10)); + } + + #[test] + fn test_hvip_bit_isolation() { + let mut hvip = hvip::Hvip::from_bits(0); + + // Set all interrupt pending bits + hvip.set_vssip(true); + hvip.set_vstip(true); + hvip.set_vseip(true); + + // Clear one at a time and verify others unchanged + hvip.set_vssip(false); + assert!(!hvip.vssip()); + assert!(hvip.vstip()); + assert!(hvip.vseip()); + + hvip.set_vstip(false); + assert!(!hvip.vssip()); + assert!(!hvip.vstip()); + assert!(hvip.vseip()); + } +} + +mod hcounteren_tests { + use super::*; + + #[test] + fn test_hcounteren_basic_fields() { + let mut hcounteren = hcounteren::Hcounteren::from_bits(0); + + // Test CY (bit 0) + hcounteren.set_cy(true); + assert!(hcounteren.cy()); + + // Test TM (bit 1) + hcounteren.set_tm(true); + assert!(hcounteren.tm()); + + // Test IR (bit 2) + hcounteren.set_ir(true); + assert!(hcounteren.ir()); + + assert_eq!(hcounteren.bits(), 0b111); + } + + #[test] + fn test_hcounteren_hpm_fields() { + let mut hcounteren = hcounteren::Hcounteren::from_bits(0); + + // Test a few HPM counters + hcounteren.set_hpm3(true); + hcounteren.set_hpm10(true); + hcounteren.set_hpm31(true); + + assert!(hcounteren.hpm3()); + assert!(hcounteren.hpm10()); + assert!(hcounteren.hpm31()); + + // Verify bits are at correct positions + assert_eq!(hcounteren.bits() & (1 << 3), 1 << 3); // hpm3 + assert_eq!(hcounteren.bits() & (1 << 10), 1 << 10); // hpm10 + assert_eq!(hcounteren.bits() & (1 << 31), 1 << 31); // hpm31 + } +} + +mod hie_tests { + use super::*; + + #[test] + fn test_hie_bit_fields() { + let mut hie = hie::Hie::from_bits(0); + + // Test VSSIE (bit 2) + hie.set_vssie(true); + assert!(hie.vssie()); + + // Test VSTIE (bit 6) + hie.set_vstie(true); + assert!(hie.vstie()); + + // Test VSEIE (bit 10) + hie.set_vseie(true); + assert!(hie.vseie()); + + // Test SGEIE (bit 12) + hie.set_sgeie(true); + assert!(hie.sgeie()); + + assert_eq!(hie.bits(), (1 << 2) | (1 << 6) | (1 << 10) | (1 << 12)); + } + + #[test] + fn test_hie_bit_isolation() { + let mut hie = hie::Hie::from_bits(0); + + hie.set_vssie(true); + hie.set_vstie(true); + hie.set_vseie(true); + hie.set_sgeie(true); + + // Clear one at a time + hie.set_vssie(false); + assert!(!hie.vssie()); + assert!(hie.vstie()); + assert!(hie.vseie()); + assert!(hie.sgeie()); + } +} + +mod hip_tests { + use super::*; + + #[test] + fn test_hip_bit_fields() { + let mut hip = hip::Hip::from_bits(0); + + // Test VSSIP (bit 2) + hip.set_vssip(true); + assert!(hip.vssip()); + + // Test VSTIP (bit 6) + hip.set_vstip(true); + assert!(hip.vstip()); + + // Test VSEIP (bit 10) + hip.set_vseip(true); + assert!(hip.vseip()); + + // Test SGEIP (bit 12) + hip.set_sgeip(true); + assert!(hip.sgeip()); + + assert_eq!(hip.bits(), (1 << 2) | (1 << 6) | (1 << 10) | (1 << 12)); + } + + #[test] + fn test_hip_bit_isolation() { + let mut hip = hip::Hip::from_bits(0); + + hip.set_vssip(true); + hip.set_vstip(true); + hip.set_vseip(true); + hip.set_sgeip(true); + + // Clear one at a time + hip.set_sgeip(false); + assert!(hip.vssip()); + assert!(hip.vstip()); + assert!(hip.vseip()); + assert!(!hip.sgeip()); + } +} + +// ============================================================================ +// Virtual Supervisor Registers Tests +// ============================================================================ + +mod vsatp_tests { + use super::*; + + #[test] + fn test_vsatp_mode() { + let mut vsatp = vsatp::Vsatp::from_bits(0); + + vsatp.set_mode(vsatp::HgatpValues::Bare); + assert!(matches!(vsatp.mode(), vsatp::HgatpValues::Bare)); + + vsatp.set_mode(vsatp::HgatpValues::Sv39x4); + assert!(matches!(vsatp.mode(), vsatp::HgatpValues::Sv39x4)); + + vsatp.set_mode(vsatp::HgatpValues::Sv48x4); + assert!(matches!(vsatp.mode(), vsatp::HgatpValues::Sv48x4)); + } + + #[test] + fn test_vsatp_asid() { + let mut vsatp = vsatp::Vsatp::from_bits(0); + + // ASID is 16 bits (bits 44-59) + vsatp.set_asid(0); + assert_eq!(vsatp.asid(), 0); + + vsatp.set_asid(0xFFFF); + assert_eq!(vsatp.asid(), 0xFFFF); + + vsatp.set_asid(0x1234); + assert_eq!(vsatp.asid(), 0x1234); + } + + #[test] + fn test_vsatp_ppn() { + let mut vsatp = vsatp::Vsatp::from_bits(0); + + // PPN is 44 bits (bits 0-43) + vsatp.set_ppn(0); + assert_eq!(vsatp.ppn(), 0); + + vsatp.set_ppn(0xFFFFFFFFFFF); + assert_eq!(vsatp.ppn(), 0xFFFFFFFFFFF); + } + + #[test] + fn test_vsatp_field_isolation() { + let mut vsatp = vsatp::Vsatp::from_bits(0); + + vsatp.set_mode(vsatp::HgatpValues::Sv48x4); + vsatp.set_asid(0xABCD); + vsatp.set_ppn(0x123456789); + + // Verify all fields are independent + assert!(matches!(vsatp.mode(), vsatp::HgatpValues::Sv48x4)); + assert_eq!(vsatp.asid(), 0xABCD); + assert_eq!(vsatp.ppn(), 0x123456789); + + // Modify one field shouldn't affect others + vsatp.set_asid(0); + assert!(matches!(vsatp.mode(), vsatp::HgatpValues::Sv48x4)); + assert_eq!(vsatp.asid(), 0); + assert_eq!(vsatp.ppn(), 0x123456789); + } +} + +mod vstvec_tests { + use super::*; + + #[test] + fn test_vstvec_base() { + let mut vstvec = vstvec::Vstvec::from_bits(0); + + // Base is bits 2-63, must be 4-byte aligned + vstvec.set_base(0x1000); + assert_eq!(vstvec.base(), 0x1000); + + vstvec.set_base(0x80000000); + assert_eq!(vstvec.base(), 0x80000000); + } + + #[test] + fn test_vstvec_mode() { + let mut vstvec = vstvec::Vstvec::from_bits(0); + + // Mode is bits 0-1 + vstvec.set_mode(0); // Direct + assert_eq!(vstvec.mode(), 0); + + vstvec.set_mode(1); // Vectored + assert_eq!(vstvec.mode(), 1); + } + + #[test] + fn test_vstvec_field_isolation() { + let mut vstvec = vstvec::Vstvec::from_bits(0); + + vstvec.set_base(0x12345678); + vstvec.set_mode(1); + + // base() returns the raw bits 2-63 value + assert_eq!(vstvec.base(), 0x12345678); + assert_eq!(vstvec.mode(), 1); + } +} + +mod vsie_tests { + use super::*; + + #[test] + fn test_vsie_bit_fields() { + let mut vsie = vsie::Vsie::from_bits(0); + + // Test SSIE (bit 1) + vsie.set_ssie(true); + assert!(vsie.ssie()); + + // Test STIE (bit 5) + vsie.set_stie(true); + assert!(vsie.stie()); + + // Test SEIE (bit 9) + vsie.set_seie(true); + assert!(vsie.seie()); + + assert_eq!(vsie.bits(), (1 << 1) | (1 << 5) | (1 << 9)); + } + + #[test] + fn test_vsie_bit_isolation() { + let mut vsie = vsie::Vsie::from_bits(0); + + vsie.set_ssie(true); + vsie.set_stie(true); + vsie.set_seie(true); + + vsie.set_ssie(false); + assert!(!vsie.ssie()); + assert!(vsie.stie()); + assert!(vsie.seie()); + } +} + +mod vsip_tests { + use super::*; + + #[test] + fn test_vsip_bit_fields() { + let mut vsip = vsip::Vsip::from_bits(0); + + // Test SSIP (bit 1) + vsip.set_ssip(true); + assert!(vsip.ssip()); + + // Test STIP (bit 5) + vsip.set_stip(true); + assert!(vsip.stip()); + + // Test SEIP (bit 9) + vsip.set_seip(true); + assert!(vsip.seip()); + + assert_eq!(vsip.bits(), (1 << 1) | (1 << 5) | (1 << 9)); + } + + #[test] + fn test_vsip_bit_isolation() { + let mut vsip = vsip::Vsip::from_bits(0); + + vsip.set_ssip(true); + vsip.set_stip(true); + vsip.set_seip(true); + + vsip.set_ssip(false); + assert!(!vsip.ssip()); + assert!(vsip.stip()); + assert!(vsip.seip()); + } +} + +// ============================================================================ +// Copy/Clone Trait Tests +// ============================================================================ + +mod trait_tests { + use super::*; + + #[test] + fn test_all_registers_copy_clone() { + // Test that all register types implement Copy and Clone + let hvip = hvip::Hvip::from_bits(0x123); + let _copy = hvip; + let _clone = hvip.clone(); + + let hcounteren = hcounteren::Hcounteren::from_bits(0xFF); + let _copy = hcounteren; + let _clone = hcounteren.clone(); + + let vsatp = vsatp::Vsatp::from_bits(0xABC); + let _copy = vsatp; + let _clone = vsatp.clone(); + + let vstvec = vstvec::Vstvec::from_bits(0x1000); + let _copy = vstvec; + let _clone = vstvec.clone(); + } + + #[test] + fn test_all_registers_debug() { + // Test that all register types implement Debug + let hvip = hvip::Hvip::from_bits(0x123); + assert!(format!("{:?}", hvip).contains("Hvip")); + + let hcounteren = hcounteren::Hcounteren::from_bits(0xFF); + assert!(format!("{:?}", hcounteren).contains("Hcounteren")); + + let vsatp = vsatp::Vsatp::from_bits(0xABC); + assert!(format!("{:?}", vsatp).contains("Vsatp")); + + let vstvec = vstvec::Vstvec::from_bits(0x1000); + assert!(format!("{:?}", vstvec).contains("Vstvec")); + } +} From af5b20bcf8875b63f1d203b05bb32a4a5f00d2f6 Mon Sep 17 00:00:00 2001 From: YanLien <128586861+YanLien@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:01:46 +0800 Subject: [PATCH 118/132] refactor(ci): migrate workflows to axci shared framework and move tests to integration (#36) * refactor(ci): migrate workflows to axci shared framework and move tests to integration * chore(scripts): update comments to axaddrspace --- .github/config.json | 7 + .github/workflows/check.yml | 67 +----- .github/workflows/deploy.yml | 107 +--------- .github/workflows/push.yml | 145 +------------ .github/workflows/release.yml | 159 ++------------- .github/workflows/test.yml | 43 +--- .gitignore | 8 + README.md | 226 ++++++++++++--------- README_CN.md | 179 ++++++++++++++++ scripts/check.sh | 35 ++++ scripts/test.sh | 34 ++++ src/address_space/mod.rs | 325 ----------------------------- src/frame.rs | 89 -------- src/lib.rs | 3 - src/memory_accessor.rs | 264 ------------------------ src/npt/arch/x86_64.rs | 17 +- tests/address_space.rs | 337 +++++++++++++++++++++++++++++++ tests/frame.rs | 104 ++++++++++ tests/memory_accessor.rs | 269 ++++++++++++++++++++++++ {src => tests}/test_utils/mod.rs | 38 ++-- 20 files changed, 1174 insertions(+), 1282 deletions(-) create mode 100644 README_CN.md create mode 100755 scripts/check.sh create mode 100755 scripts/test.sh create mode 100644 tests/address_space.rs create mode 100644 tests/frame.rs create mode 100644 tests/memory_accessor.rs rename {src => tests}/test_utils/mod.rs (85%) diff --git a/.github/config.json b/.github/config.json index f347e101e..2a1297146 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1,10 +1,17 @@ { + "component": { + "name": "axaddrspace", + "crate_name": "axaddrspace" + }, "targets": [ "aarch64-unknown-none-softfloat", "x86_64-unknown-linux-gnu", "x86_64-unknown-none", "riscv64gc-unknown-none-elf" ], + "unit_test_targets": [ + "x86_64-unknown-linux-gnu" + ], "rust_components": [ "rust-src", "clippy", diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 330fa15e9..018bbbeb2 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,66 +1,15 @@ -name: Quality Checks +# Quality Check Workflow +# References shared workflow from axci + +name: Check on: push: - branches: - - '**' - tags-ignore: - - '**' + branches: ['**'] + tags-ignore: ['**'] pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - rust_components: ${{ steps.config.outputs.rust_components }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) - - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT - check: - name: Check - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: ${{ needs.load-config.outputs.rust_components }} - targets: ${{ matrix.target }} - - - name: Check rust version - run: rustc --version --verbose - - - name: Check code format - run: cargo fmt --all -- --check - - - name: Build - run: cargo build --target ${{ matrix.target }} --all-features - - - name: Run clippy - run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings - - - name: Build documentation - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 882ba9310..37b00c09c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,3 +1,6 @@ +# Deploy Workflow +# References shared workflow from axci + name: Deploy on: @@ -5,105 +8,9 @@ on: tags: - 'v[0-9]+.[0-9]+.[0-9]+' -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: 'pages' - cancel-in-progress: false - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_deploy: ${{ steps.check.outputs.should_deploy }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check if tag is on main or master branch - id: check - run: | - git fetch origin main master || true - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Tag is on main or master branch" - echo "should_deploy=true" >> $GITHUB_OUTPUT - else - echo "✗ Tag is not on main or master branch, skipping deployment" - echo "Tag is on: $BRANCHES" - echo "should_deploy=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_deploy == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - test: - uses: ./.github/workflows/test.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - build: - name: Build documentation - runs-on: ubuntu-latest - needs: [verify-tag, check, test] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Build docs - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: target/doc - deploy: - name: Deploy to GitHub Pages - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: [verify-tag, build] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main + with: + verify_branch: true + verify_version: true diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a18de4a04..3ff391a4a 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,147 +1,16 @@ -# ═══════════════════════════════════════════════════════════════════════════════ -# 组件仓库 GitHub Actions 配置模板 -# ═══════════════════════════════════════════════════════════════════════════════ -# -# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 -# -# 【使用步骤】 -# ───────────────────────────────────────────────────────────────────────────── -# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: -# cp scripts/push.yml <子仓库>/.github/workflows/push.yml -# -# 2. 在子仓库中配置 Secret: -# GitHub 仓库 → Settings → Secrets → Actions → New repository secret -# 名称: PARENT_REPO_TOKEN -# 值: 具有主仓库 repo 权限的 Personal Access Token -# -# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): -# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits -# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) -# -# 【Token 权限要求】 -# ───────────────────────────────────────────────────────────────────────────── -# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: -# - repo (Full control of private repositories) -# 或 -# - Fine-grained token: Contents (Read and Write) -# -# 【触发条件】 -# ───────────────────────────────────────────────────────────────────────────── -# - 自动触发:推送到 dev 或 main 分支时 -# - 手动触发:Actions → Notify Parent Repository → Run workflow -# -# 【工作流程】 -# ───────────────────────────────────────────────────────────────────────────── -# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull -# -# 【注意事项】 -# ───────────────────────────────────────────────────────────────────────────── -# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 -# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 -# -# ═══════════════════════════════════════════════════════════════════════════════ - name: Notify Parent Repository -# 当有新的推送时触发 on: push: branches: - main - - master + - zcs workflow_dispatch: jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Get repository info - id: repo - env: - GH_REPO_NAME: ${{ github.event.repository.name }} - GH_REF_NAME: ${{ github.ref_name }} - GH_SERVER_URL: ${{ github.server_url }} - GH_REPOSITORY: ${{ github.repository }} - run: | - # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 - COMPONENT="$GH_REPO_NAME" - BRANCH="$GH_REF_NAME" - # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list - REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" - - echo "component=${COMPONENT}" >> $GITHUB_OUTPUT - echo "branch=${BRANCH}" >> $GITHUB_OUTPUT - echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT - - echo "Component: ${COMPONENT}" - echo "Branch: ${BRANCH}" - echo "Repo URL: ${REPO_URL}" - - - name: Notify parent repository - env: - # ── 需要修改 ────────────────────────────────────────────────────────── - PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 - # ── 无需修改 ────────────────────────────────────────────────────────── - DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} - # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 - COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - GIT_ACTOR: ${{ github.actor }} - GIT_SHA: ${{ github.sha }} - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" - - # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 - PAYLOAD=$(jq -n \ - --arg component "$COMPONENT" \ - --arg branch "$BRANCH" \ - --arg repo_url "$REPO_URL" \ - --arg commit "$GIT_SHA" \ - --arg message "$COMMIT_MESSAGE" \ - --arg author "$GIT_ACTOR" \ - '{ - event_type: "subtree-update", - client_payload: { - component: $component, - branch: $branch, - repo_url: $repo_url, - commit: $commit, - message: $message, - author: $author - } - }') - - curl --fail --show-error -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${DISPATCH_TOKEN}" \ - https://api.github.com/repos/${PARENT_REPO}/dispatches \ - -d "$PAYLOAD" - - echo "Notification sent successfully" - - - name: Create summary - env: - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - GIT_SHA: ${{ github.sha }} - GIT_ACTOR: ${{ github.actor }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY - echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY + notify-parent: + name: Notify Parent Repository + # 调用 axci 仓库的可复用工作流 + uses: arceos-hypervisor/axci/.github/workflows/push.yml@main + secrets: + PARENT_REPO_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e857b48b..20f186395 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,7 @@ +# Release Workflow +# References shared workflow from axci +# check + test must pass before release + name: Release on: @@ -6,155 +10,18 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' -permissions: - contents: write - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - is_prerelease: ${{ steps.check.outputs.is_prerelease }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check tag type and branch - id: check - run: | - git fetch origin main master dev || true - - TAG="${{ github.ref_name }}" - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - echo "Tag: $TAG" - echo "Branches containing this tag: $BRANCHES" - - # Check if it's a prerelease tag - if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[0-9]+$ ]]; then - echo "📦 Detected prerelease tag" - echo "is_prerelease=true" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -q 'origin/dev'; then - echo "✓ Prerelease tag is on dev branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Prerelease tag must be on dev branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "📦 Detected stable release tag" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Stable release tag is on main or master branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Stable release tag must be on main or master branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - else - echo "✗ Unknown tag format, skipping release" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - echo "should_release=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_release == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main test: - uses: ./.github/workflows/test.yml - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main release: - name: Create GitHub Release - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate release notes - id: release_notes - run: | - CURRENT_TAG="${{ github.ref_name }}" - - # Get previous tag - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) - - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then - echo "No previous tag found, this is the first release" - CHANGELOG="Initial release" - else - echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" - - # Generate changelog with commit messages - CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") - - if [ -z "$CHANGELOG" ]; then - CHANGELOG="No changes" - fi - fi - - # Write changelog to output file (multi-line) - { - echo "changelog<> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} - body: | - ## Changes - ${{ steps.release_notes.outputs.changelog }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish: - name: Publish to crates.io - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Dry run publish - run: cargo publish --dry-run - - - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + needs: [check, test] + uses: arceos-hypervisor/axci/.github/workflows/release.yml@main + with: + verify_branch: true + verify_version: true + secrets: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc3b293d9..6a58ef8e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,3 +1,6 @@ +# Integration Test Workflow +# References shared workflow from axci + name: Test on: @@ -7,44 +10,8 @@ on: tags-ignore: - '**' pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - test: - name: Test - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - # - name: Run tests - # run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture - - # - name: Run doc tests - # run: cargo test --target ${{ matrix.target }} --doc - - name: Run tests - run: echo "Tests are skipped!" + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main diff --git a/.gitignore b/.gitignore index ff78c42af..22d7b02fd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,11 @@ /.vscode .DS_Store Cargo.lock + +# Test results (generated by shared test framework) +/test-results/ +/test_repos/ +*.log + +# Downloaded test framework +/scripts/.axci/ diff --git a/README.md b/README.md index c626dd6b9..3fc839875 100644 --- a/README.md +++ b/README.md @@ -1,143 +1,181 @@ -# axaddrspace +

axaddrspace

-**ArceOS-Hypervisor guest VM address space management module** +

ArceOS-Hypervisor guest VM address space management module

-[![CI](https://github.com/arceos-hypervisor/axaddrspace/actions/workflows/ci.yml/badge.svg)](https://github.com/arceos-hypervisor/axaddrspace/actions/workflows/ci.yml) -[![Crates.io](https://img.shields.io/crates/v/axaddrspace)](https://crates.io/crates/axaddrspace) -[![License](https://img.shields.io/badge/license-Apache%202.0%20OR%20MIT-blue)](LICENSE) +
-## Overview +[![Crates.io](https://img.shields.io/crates/v/axaddrspace.svg)](https://crates.io/crates/axaddrspace) +[![Docs.rs](https://docs.rs/axaddrspace/badge.svg)](https://docs.rs/axaddrspace) +[![Rust](https://img.shields.io/badge/edition-2024-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/axaddrspace/blob/main/LICENSE) -`axaddrspace` is a core component of the [ArceOS-Hypervisor](https://github.com/arceos-hypervisor/) project that provides guest virtual machine address space management capabilities. The crate implements nested page tables and address translation for hypervisor environments, supporting multiple architectures including x86_64, AArch64, and RISC-V. +
-## Features +English | [中文](README_CN.md) -- **Multi-architecture support**: x86_64 (VMX EPT), AArch64 (Stage 2 page tables), and RISC-V nested page tables -- **Flexible memory mapping backends**: - - **Linear mapping**: For contiguous physical memory regions with known addresses - - **Allocation mapping**: Dynamic allocation with optional lazy loading support -- **Nested page fault handling**: Comprehensive page fault management for guest VMs -- **Hardware abstraction layer**: Clean interface for memory management operations -- **No-std compatible**: Designed for bare-metal hypervisor environments +# Introduction -## Architecture Support +`axaddrspace` is the guest address space management crate for the +[ArceOS-Hypervisor](https://github.com/arceos-hypervisor/) project. It provides +nested page table management, guest physical address translation, memory +mapping backends, and nested page fault handling for hypervisor environments. -### x86_64 -- VMX Extended Page Tables (EPT) -- Memory type configuration (WriteBack, Uncached, etc.) -- Execute permissions for user-mode addresses +This crate supports multiple architectures: -### AArch64 -- VMSAv8-64 Stage 2 translation tables -- Configurable MAIR_EL2 memory attributes -- EL2 privilege level support +- **x86_64** - VMX Extended Page Tables (EPT) +- **AArch64** - Stage-2 page tables +- **RISC-V** - Nested page tables based on the hypervisor extension -### RISC-V -- Nested page table implementation -- Hypervisor fence instructions (`hfence.vvma`) -- Sv39 metadata support +Key capabilities include: -## Core Components +- **`AddrSpace`** - address space creation, mapping, unmapping, and translation +- **`AxMmHal`** - hardware abstraction trait for frame allocation and address conversion +- **Linear mapping backend** - map known contiguous host physical memory ranges +- **Allocation mapping backend** - allocate frames eagerly or lazily on page faults +- **Guest memory helpers** - translate guest addresses to accessible host buffers -### Address Space Management -The `AddrSpace` struct provides: -- Virtual address range management -- Page table root address tracking -- Memory area organization -- Address translation services +Supports `#![no_std]` and is intended for bare-metal hypervisor and kernel use. -### Memory Mapping Backends -Two types of mapping backends are supported: +## Quick Start -1. **Linear Backend**: Direct mapping with constant offset between virtual and physical addresses -2. **Allocation Backend**: Dynamic memory allocation with optional population strategies +### Requirements -### Nested Page Tables -Architecture-specific nested page table implementations: -- **x86_64**: `ExtendedPageTable` with EPT entries -- **AArch64**: Stage 2 page tables with descriptor attributes -- **RISC-V**: Sv39-based nested page tables +- Rust nightly toolchain +- Rust components: `rust-src`, `clippy`, `rustfmt` -## Usage +```bash +# Install rustup (if not installed) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -Add this to your `Cargo.toml`: - -```toml -[dependencies] -axaddrspace = "0.1.0" +# Install nightly toolchain and components +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly ``` -### Basic Example +### Run Check and Test -```rust -use axaddrspace::{AddrSpace, MappingFlags, GuestPhysAddr}; -use page_table_multiarch::PagingHandler; +```bash +# 1. Clone the repository +git clone https://github.com/arceos-hypervisor/axaddrspace.git +cd axaddrspace + +# 2. Code check +./scripts/check.sh + +# 3. Run tests +./scripts/test.sh -// Create a new address space -let mut addr_space = AddrSpace::::new_empty( - GuestPhysAddr::from(0x1000_0000), - 0x1000_0000, // 256MB -)?; - -// Create a linear mapping -addr_space.map_linear( - GuestPhysAddr::from(0x1000_0000), // Guest virtual address - PhysAddr::from(0x8000_0000), // Host physical address - 0x10_0000, // 1MB - MappingFlags::READ | MappingFlags::WRITE, -)?; - -// Handle a nested page fault -let fault_handled = addr_space.handle_page_fault( - GuestPhysAddr::from(0x1000_1000), - MappingFlags::READ, -); +# 4. Run a specific integration test target directly +cargo test --test address_space ``` -### Hardware Abstraction Layer +The helper scripts download the shared `axci` test/check framework on first run. + +## Integration + +### Installation + +Add to your `Cargo.toml`: + +```toml +[dependencies] +axaddrspace = "0.3.0" +``` -Implement the `AxMmHal` trait for your platform: +### Example ```rust -use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; +use axaddrspace::{AddrSpace, AxMmHal, GuestPhysAddr, HostPhysAddr, HostVirtAddr, MappingFlags}; +use memory_addr::{PhysAddr, VirtAddr}; +use page_table_multiarch::PagingHandler; struct MyHal; impl AxMmHal for MyHal { fn alloc_frame() -> Option { - // Your frame allocation implementation + unimplemented!() } - fn dealloc_frame(paddr: HostPhysAddr) { - // Your frame deallocation implementation + fn dealloc_frame(_paddr: HostPhysAddr) { + unimplemented!() } - fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr { - // Your physical to virtual address conversion + fn phys_to_virt(_paddr: HostPhysAddr) -> HostVirtAddr { + unimplemented!() } - fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr { - // Your virtual to physical address conversion + fn virt_to_phys(_vaddr: HostVirtAddr) -> HostPhysAddr { + unimplemented!() } } + +impl PagingHandler for MyHal { + fn alloc_frame() -> Option { + ::alloc_frame() + } + + fn dealloc_frame(paddr: PhysAddr) { + ::dealloc_frame(paddr) + } + + fn phys_to_virt(paddr: PhysAddr) -> VirtAddr { + ::phys_to_virt(paddr) + } +} + +fn example() -> axerrno::AxResult<()> { + let base = GuestPhysAddr::from_usize(0x1000_0000); + let mut addr_space = AddrSpace::::new_empty(4, base, 0x20_0000)?; + + addr_space.map_linear( + base, + PhysAddr::from_usize(0x8000_0000), + 0x10_0000, + MappingFlags::READ | MappingFlags::WRITE, + )?; + + addr_space.map_alloc( + base + 0x10_0000, + 0x2000, + MappingFlags::READ | MappingFlags::WRITE, + false, + )?; + + let fault_handled = addr_space.handle_page_fault( + base + 0x10_0000, + MappingFlags::READ, + ); + assert!(fault_handled); + + let host_paddr = addr_space.translate(base).unwrap(); + assert_eq!(host_paddr, PhysAddr::from_usize(0x8000_0000)); + + Ok(()) +} ``` -## Configuration +### Features + +- `arm-el2`: enable AArch64 EL2 support +- `default`: includes `arm-el2` -### Feature Flags +### Documentation -- `arm-el2`: Enable AArch64 EL2 support (default) -- `default`: Includes `arm-el2` feature +Generate and view API documentation: -## Contributing +```bash +cargo doc --no-deps --open +``` -Contributions are welcome! Please feel free to submit a Pull Request. +Online documentation: [docs.rs/axaddrspace](https://docs.rs/axaddrspace) -## Repository +# Contributing -- [GitHub Repository](https://github.com/arceos-hypervisor/axaddrspace) -- [ArceOS-Hypervisor Project](https://github.com/arceos-hypervisor/) +1. Fork the repository and create a branch +2. Run local check: `./scripts/check.sh` +3. Run local tests: `./scripts/test.sh` +4. Submit PR and pass CI checks -## License +# License -Axaddrspace is licensed under the Apache License, Version 2.0. See the [LICENSE](./LICENSE) file for details. +Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details. diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 000000000..91fcf28a2 --- /dev/null +++ b/README_CN.md @@ -0,0 +1,179 @@ +

axaddrspace

+ +

ArceOS-Hypervisor 客户机虚拟机地址空间管理模块

+ +
+ +[![Crates.io](https://img.shields.io/crates/v/axaddrspace.svg)](https://crates.io/crates/axaddrspace) +[![Docs.rs](https://docs.rs/axaddrspace/badge.svg)](https://docs.rs/axaddrspace) +[![Rust](https://img.shields.io/badge/edition-2024-orange.svg)](https://www.rust-lang.org/) +[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://github.com/arceos-hypervisor/axaddrspace/blob/main/LICENSE) + +
+ +[English](README.md) | 中文 + +# 简介 + +`axaddrspace` 是 [ArceOS-Hypervisor](https://github.com/arceos-hypervisor/) +项目中的客户机地址空间管理 crate,提供嵌套页表管理、客户机物理地址转换、内存映射后端以及嵌套页错误处理能力,面向 Hypervisor 场景使用。 + +该 crate 支持多种体系结构: + +- **x86_64** - VMX Extended Page Tables(EPT) +- **AArch64** - Stage-2 页表 +- **RISC-V** - 基于 Hypervisor 扩展的嵌套页表 + +核心能力包括: + +- **`AddrSpace`** - 地址空间创建、映射、解除映射与地址转换 +- **`AxMmHal`** - 用于页帧分配与地址转换的硬件抽象 trait +- **线性映射后端** - 映射已知的连续宿主物理内存区域 +- **分配映射后端** - 支持预分配或缺页时惰性分配页帧 +- **客户机内存辅助接口** - 将客户机地址转换为宿主可访问缓冲区 + +该库支持 `#![no_std]`,适用于裸机 Hypervisor 和内核环境。 + +## 快速开始 + +### 环境要求 + +- Rust nightly 工具链 +- Rust 组件:`rust-src`、`clippy`、`rustfmt` + +```bash +# 安装 rustup(如未安装) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# 安装 nightly 工具链和组件 +rustup install nightly +rustup component add rust-src clippy rustfmt --toolchain nightly +``` + +### 运行检查和测试 + +```bash +# 1. 克隆仓库 +git clone https://github.com/arceos-hypervisor/axaddrspace.git +cd axaddrspace + +# 2. 代码检查 +./scripts/check.sh + +# 3. 运行测试 +./scripts/test.sh + +# 4. 直接运行指定集成测试目标 +cargo test --test address_space +``` + +辅助脚本会在首次运行时自动下载共享的 `axci` 检查/测试框架。 + +## 集成方式 + +### 安装 + +在 `Cargo.toml` 中添加: + +```toml +[dependencies] +axaddrspace = "0.3.0" +``` + +### 示例 + +```rust +use axaddrspace::{AddrSpace, AxMmHal, GuestPhysAddr, HostPhysAddr, HostVirtAddr, MappingFlags}; +use memory_addr::{PhysAddr, VirtAddr}; +use page_table_multiarch::PagingHandler; + +struct MyHal; + +impl AxMmHal for MyHal { + fn alloc_frame() -> Option { + unimplemented!() + } + + fn dealloc_frame(_paddr: HostPhysAddr) { + unimplemented!() + } + + fn phys_to_virt(_paddr: HostPhysAddr) -> HostVirtAddr { + unimplemented!() + } + + fn virt_to_phys(_vaddr: HostVirtAddr) -> HostPhysAddr { + unimplemented!() + } +} + +impl PagingHandler for MyHal { + fn alloc_frame() -> Option { + ::alloc_frame() + } + + fn dealloc_frame(paddr: PhysAddr) { + ::dealloc_frame(paddr) + } + + fn phys_to_virt(paddr: PhysAddr) -> VirtAddr { + ::phys_to_virt(paddr) + } +} + +fn example() -> axerrno::AxResult<()> { + let base = GuestPhysAddr::from_usize(0x1000_0000); + let mut addr_space = AddrSpace::::new_empty(4, base, 0x20_0000)?; + + addr_space.map_linear( + base, + PhysAddr::from_usize(0x8000_0000), + 0x10_0000, + MappingFlags::READ | MappingFlags::WRITE, + )?; + + addr_space.map_alloc( + base + 0x10_0000, + 0x2000, + MappingFlags::READ | MappingFlags::WRITE, + false, + )?; + + let fault_handled = addr_space.handle_page_fault( + base + 0x10_0000, + MappingFlags::READ, + ); + assert!(fault_handled); + + let host_paddr = addr_space.translate(base).unwrap(); + assert_eq!(host_paddr, PhysAddr::from_usize(0x8000_0000)); + + Ok(()) +} +``` + +### 特性 + +- `arm-el2`:启用 AArch64 EL2 支持 +- `default`:默认包含 `arm-el2` + +### 文档 + +生成并查看 API 文档: + +```bash +cargo doc --no-deps --open +``` + +在线文档:[docs.rs/axaddrspace](https://docs.rs/axaddrspace) + +# 贡献 + +1. Fork 仓库并创建分支 +2. 本地运行检查:`./scripts/check.sh` +3. 本地运行测试:`./scripts/test.sh` +4. 提交 PR 并通过 CI 检查 + +# 许可证 + +本项目采用 Apache License 2.0。详见 [LICENSE](LICENSE)。 diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 000000000..1e4092ca4 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# axaddrspace 代码检查脚本 +# 下载并调用 axci 仓库中的检查脚本 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +COMPONENT_NAME="$(basename "$COMPONENT_DIR")" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行检查 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/check.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 000000000..0ac99ecd7 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# +# axaddrspace 测试脚本 +# 下载并调用 axci 仓库中的测试框架 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行测试,自动指定当前组件 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/tests.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/src/address_space/mod.rs b/src/address_space/mod.rs index 8cf6ebd7e..a5664810a 100644 --- a/src/address_space/mod.rs +++ b/src/address_space/mod.rs @@ -275,328 +275,3 @@ impl Drop for AddrSpace { self.clear(); } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::test_utils::{ - ALLOC_COUNT, BASE_PADDR, DEALLOC_COUNT, MEMORY_LEN, MockHal, mock_hal_test, - test_dealloc_count, - }; - use axin::axin; - use core::sync::atomic::Ordering; - - /// Generate an address space for the test - fn setup_test_addr_space() -> (AddrSpace, GuestPhysAddr, usize) { - const BASE: GuestPhysAddr = GuestPhysAddr::from_usize(0x10000); - const SIZE: usize = 0x10000; - let addr_space = AddrSpace::::new_empty(4, BASE, SIZE).unwrap(); - (addr_space, BASE, SIZE) - } - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] - /// Check whether an address_space can be created correctly. - /// When creating a new address_space, a frame will be allocated for the page table, - /// thus triggering an alloc_frame operation. - fn test_addrspace_creation() { - let (addr_space, base, size) = setup_test_addr_space(); - assert_eq!(addr_space.base(), base); - assert_eq!(addr_space.size(), size); - assert_eq!(addr_space.end(), base + size); - assert_eq!(ALLOC_COUNT.load(Ordering::SeqCst), 1); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_contains_range() { - let (addr_space, base, size) = setup_test_addr_space(); - - // Within range - assert!(addr_space.contains_range(base, 0x1000)); - assert!(addr_space.contains_range(base + 0x1000, 0x2000)); - assert!(addr_space.contains_range(base, size)); - - // Out of range - assert!(!addr_space.contains_range(base - 0x1000, 0x1000)); - assert!(!addr_space.contains_range(base + size, 0x1000)); - assert!(!addr_space.contains_range(base, size + 0x1000)); - - // Partially out of range - assert!(!addr_space.contains_range(base + 0x3000, 0xf000)); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_map_linear() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x18000); - let paddr = PhysAddr::from_usize(0x10000); - let map_linear_size = 0x8000; // 32KB - let flags = MappingFlags::READ | MappingFlags::WRITE; - - addr_space - .map_linear(vaddr, paddr, map_linear_size, flags) - .unwrap(); - - assert_eq!(addr_space.translate(vaddr).unwrap(), paddr); - assert_eq!( - addr_space.translate(vaddr + 0x1000).unwrap(), - paddr + 0x1000 - ); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_map_alloc_populate() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x10000); - let map_alloc_size = 0x2000; // 8KB - let flags = MappingFlags::READ | MappingFlags::WRITE; - - // Frame count before allocation: 1 root page table - let initial_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - assert_eq!(initial_allocs, 1); - - // Allocate physical frames immediately - addr_space - .map_alloc(vaddr, map_alloc_size, flags, true) - .unwrap(); - - // Verify additional frames were allocated - let final_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - assert!(final_allocs > initial_allocs); - - // Verify mappings exist and addresses are valid - let paddr1 = addr_space.translate(vaddr).unwrap(); - let paddr2 = addr_space.translate(vaddr + 0x1000).unwrap(); - - // Verify physical addresses are within valid range - assert!(paddr1.as_usize() >= BASE_PADDR && paddr1.as_usize() < BASE_PADDR + MEMORY_LEN); - assert!(paddr2.as_usize() >= BASE_PADDR && paddr2.as_usize() < BASE_PADDR + MEMORY_LEN); - - // Verify two pages have different physical addresses - assert_ne!(paddr1, paddr2); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_map_alloc_lazy() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x13000); - let map_alloc_size = 0x1000; - let flags = MappingFlags::READ | MappingFlags::WRITE; - - let initial_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - - // Lazy allocation - don't allocate physical frames immediately - addr_space - .map_alloc(vaddr, map_alloc_size, flags, false) - .unwrap(); - - // Frame count should only increase for page table structure, not data pages - let after_map_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - assert!(after_map_allocs >= initial_allocs); // May have allocated intermediate page tables - assert!(addr_space.translate(vaddr).is_none()); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_page_fault_handling() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x14000); - let map_alloc_size = 0x1000; - let flags = MappingFlags::READ | MappingFlags::WRITE; - - // Create lazy allocation mapping - addr_space - .map_alloc(vaddr, map_alloc_size, flags, false) - .unwrap(); - - let before_pf_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - - // Simulate page fault - let handled = addr_space.handle_page_fault(vaddr, MappingFlags::READ); - - // Page fault should be handled - assert!(handled); - - // Should have allocated physical frames - let after_pf_allocs = ALLOC_COUNT.load(Ordering::SeqCst); - assert!(after_pf_allocs > before_pf_allocs); - - // Translation should succeed now - let paddr = addr_space.translate(vaddr); - assert!(paddr.is_some()); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_unmap() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x15000); - let map_alloc_size = 0x2000; - let flags = MappingFlags::READ | MappingFlags::WRITE; - - // Create mapping - addr_space - .map_alloc(vaddr, map_alloc_size, flags, true) - .unwrap(); - - // Verify mapping exists - assert!(addr_space.translate(vaddr).is_some()); - assert!(addr_space.translate(vaddr + 0x1000).is_some()); - - let before_unmap_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); - - // Unmap - addr_space.unmap(vaddr, map_alloc_size).unwrap(); - - // Verify mapping is removed - assert!(addr_space.translate(vaddr).is_none()); - assert!(addr_space.translate(vaddr + 0x1000).is_none()); - - // Verify frames were deallocated - let after_unmap_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); - assert!(after_unmap_deallocs > before_unmap_deallocs); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_clear() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr1 = GuestPhysAddr::from_usize(0x16000); - let vaddr2 = GuestPhysAddr::from_usize(0x17000); - let flags = MappingFlags::READ | MappingFlags::WRITE; - let map_alloc_size = 0x1000; - - // Create multiple mappings - addr_space - .map_alloc(vaddr1, map_alloc_size, flags, true) - .unwrap(); - addr_space - .map_alloc(vaddr2, map_alloc_size, flags, true) - .unwrap(); - - // Verify mappings exist - assert!(addr_space.translate(vaddr1).is_some()); - assert!(addr_space.translate(vaddr2).is_some()); - - let before_clear_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); - - // Clear all mappings - addr_space.clear(); - - // Verify all mappings are removed - assert!(addr_space.translate(vaddr1).is_none()); - assert!(addr_space.translate(vaddr2).is_none()); - - // Verify frames were deallocated - let after_clear_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); - assert!(after_clear_deallocs > before_clear_deallocs); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_translate() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x18000); - let map_alloc_size = 0x1000; - let flags = MappingFlags::READ | MappingFlags::WRITE; - - // Create mapping - addr_space - .map_alloc(vaddr, map_alloc_size, flags, true) - .unwrap(); - - // Verify translation succeeds - let paddr = addr_space.translate(vaddr).expect("Translation failed"); - assert!(paddr.as_usize() >= BASE_PADDR); - assert!(paddr.as_usize() < BASE_PADDR + MEMORY_LEN); - - // Verify unmapped address translation fails - let unmapped_vaddr = GuestPhysAddr::from_usize(0x19000); - assert!(addr_space.translate(unmapped_vaddr).is_none()); - - // Verify out-of-range address translation fails - let out_of_range = GuestPhysAddr::from_usize(0x30000); - assert!(addr_space.translate(out_of_range).is_none()); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_translated_byte_buffer() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x19000); - let map_alloc_size = 0x2000; // 8KB - let flags = MappingFlags::READ | MappingFlags::WRITE; - let buffer_size = 0x1100; - - // Create mapping - addr_space - .map_alloc(vaddr, map_alloc_size, flags, true) - .unwrap(); - - // Verify byte buffer can be obtained - let mut buffer = addr_space - .translated_byte_buffer(vaddr, buffer_size) - .expect("Failed to get byte buffer"); - - // Verify data write and read - // Fill with values ranging from 0 to 0x100 - for buffer_segment in buffer.iter_mut() { - for (i, byte) in buffer_segment.iter_mut().enumerate() { - *byte = (i % 0x100) as u8; - } - } - - // Verify data read correctness - for buffer_segment in buffer.iter_mut() { - for (i, byte) in buffer_segment.iter_mut().enumerate() { - assert_eq!(*byte, (i % 0x100) as u8); - } - } - - // Verify exceeding area size returns None - assert!( - addr_space - .translated_byte_buffer(vaddr, map_alloc_size + 0x1000) - .is_none() - ); - - // Verify unmapped address returns None - let unmapped_vaddr = GuestPhysAddr::from_usize(0x1D000); - assert!( - addr_space - .translated_byte_buffer(unmapped_vaddr, 0x100) - .is_none() - ); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_translate_and_get_limit() { - let (mut addr_space, _base, _size) = setup_test_addr_space(); - let vaddr = GuestPhysAddr::from_usize(0x1A000); - let map_alloc_size = 0x3000; // 12KB - let flags = MappingFlags::READ | MappingFlags::WRITE; - - // Create mapping - addr_space - .map_alloc(vaddr, map_alloc_size, flags, true) - .unwrap(); - - // Verify translation and area size retrieval - let (paddr, area_size) = addr_space.translate_and_get_limit(vaddr).unwrap(); - assert!(paddr.as_usize() >= BASE_PADDR && paddr.as_usize() < BASE_PADDR + MEMORY_LEN); - assert_eq!(area_size, map_alloc_size); - - // Verify unmapped address returns None - let unmapped_vaddr = GuestPhysAddr::from_usize(0x1E000); - assert!(addr_space.translate_and_get_limit(unmapped_vaddr).is_none()); - - // Verify out-of-range address returns None - let out_of_range = GuestPhysAddr::from_usize(0x30000); - assert!(addr_space.translate_and_get_limit(out_of_range).is_none()); - } -} diff --git a/src/frame.rs b/src/frame.rs index 891f7fd19..b130264a1 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -86,92 +86,3 @@ impl Drop for PhysFrame { } } } - -#[cfg(test)] -mod test { - use super::*; - use crate::test_utils::{BASE_PADDR, MockHal, mock_hal_test, test_dealloc_count}; - use alloc::vec::Vec; - use assert_matches::assert_matches; - use axin::axin; - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] - fn test_alloc_dealloc_cycle() { - let frame = PhysFrame::::alloc() - .unwrap_or_else(|e| panic!("Failed to allocate frame: {:?}", e)); - assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); - // frame is dropped here, dealloc_frame should be called - } - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] - fn test_alloc_zero() { - let frame = PhysFrame::::alloc_zero() - .unwrap_or_else(|e| panic!("Failed to allocate zero frame: {:?}", e)); - assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); - let ptr = frame.as_mut_ptr(); - let page = unsafe { &*(ptr as *const [u8; PAGE_SIZE]) }; - assert!(page.iter().all(|&x| x == 0)); - } - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] - fn test_fill_operation() { - let mut frame = PhysFrame::::alloc() - .unwrap_or_else(|e| panic!("Failed to allocate frame: {:?}", e)); - assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); - frame.fill(0xAA); - let ptr = frame.as_mut_ptr(); - let page = unsafe { &*(ptr as *const [u8; PAGE_SIZE]) }; - assert!(page.iter().all(|&x| x == 0xAA)); - } - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(5)))] - fn test_fill_multiple_frames() { - const NUM_FRAMES: usize = 5; - - let mut frames = Vec::new(); - let mut patterns = Vec::new(); - - for i in 0..NUM_FRAMES { - let mut frame = PhysFrame::::alloc().unwrap(); - let pattern = (0xA0 + i) as u8; - frame.fill(pattern); - frames.push(frame); - patterns.push(pattern); - } - - for i in 0..NUM_FRAMES { - let actual_page = unsafe { &*(frames[i].as_mut_ptr() as *mut [u8; PAGE_SIZE]) }; - let expected_page = &[patterns[i]; PAGE_SIZE]; - - assert_eq!( - actual_page, expected_page, - "Frame verification failed for frame index {i}: Expected pattern 0x{:02x}", - patterns[i] - ); - } - } - - #[test] - #[should_panic(expected = "uninitialized PhysFrame")] - fn test_uninit_access() { - // This test verifies that accessing an uninitialized PhysFrame (created with `unsafe { uninit() }`) - // leads to a panic when trying to retrieve its physical address. - let frame = unsafe { PhysFrame::::uninit() }; - frame.start_paddr(); // This should panic - } - - #[test] - #[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(0)))] - fn test_alloc_no_memory() { - // Configure MockHal to simulate an allocation failure. - MockHal::set_alloc_fail(true); - let result = PhysFrame::::alloc(); - // Assert that allocation failed and verify the specific error type. - assert_matches!(result, Err(axerrno::AxError::NoMemory)); - MockHal::set_alloc_fail(false); // Reset for other tests - } -} diff --git a/src/lib.rs b/src/lib.rs index e91f573f0..b4989cc55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,3 @@ fn mapping_err_to_ax_err(err: MappingError) -> AxError { MappingError::BadState => AxError::BadState, } } - -#[cfg(test)] -pub(crate) mod test_utils; diff --git a/src/memory_accessor.rs b/src/memory_accessor.rs index a0855a2fe..7da85b576 100644 --- a/src/memory_accessor.rs +++ b/src/memory_accessor.rs @@ -197,267 +197,3 @@ pub trait GuestMemoryAccessor { self.write_obj(guest_addr, val) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::test_utils::{BASE_PADDR, mock_hal_test}; - use axin::axin; - use memory_addr::PhysAddr; - - /// Mock implementation of GuestMemoryAccessor for testing - struct MockTranslator { - base_addr: PhysAddr, - memory_size: usize, - } - - impl MockTranslator { - pub fn new(base_addr: PhysAddr, memory_size: usize) -> Self { - Self { - base_addr, - memory_size, - } - } - } - - impl GuestMemoryAccessor for MockTranslator { - fn translate_and_get_limit(&self, guest_addr: GuestPhysAddr) -> Option<(PhysAddr, usize)> { - // Simple mapping: guest address directly maps to mock memory region - let offset = guest_addr.as_usize(); - if offset < self.memory_size { - // Convert physical address to virtual address for actual memory access - let phys_addr = - PhysAddr::from_usize(BASE_PADDR + self.base_addr.as_usize() + offset); - let virt_addr = crate::test_utils::MockHal::mock_phys_to_virt(phys_addr); - let accessible_size = self.memory_size - offset; - Some((PhysAddr::from_usize(virt_addr.as_usize()), accessible_size)) - } else { - None - } - } - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_basic_read_write_operations() { - let translator = - MockTranslator::new(PhysAddr::from_usize(0), crate::test_utils::MEMORY_LEN); - - // Test u32 read/write operations - let test_addr = GuestPhysAddr::from_usize(0x100); - let test_value: u32 = 0x12345678; - - // Write a u32 value - translator - .write_obj(test_addr, test_value) - .expect("Failed to write u32 value"); - - // Read back the u32 value - let read_value: u32 = translator - .read_obj(test_addr) - .expect("Failed to read u32 value"); - - assert_eq!( - read_value, test_value, - "Read value should match written value" - ); - - // Test buffer read/write operations - let buffer_addr = GuestPhysAddr::from_usize(0x200); - let test_buffer = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]; - - // Write buffer - translator - .write_buffer(buffer_addr, &test_buffer) - .expect("Failed to write buffer"); - - // Read buffer back - let mut read_buffer = [0u8; 8]; - translator - .read_buffer(buffer_addr, &mut read_buffer) - .expect("Failed to read buffer"); - - assert_eq!( - read_buffer, test_buffer, - "Read buffer should match written buffer" - ); - - // Test error handling with invalid address - let invalid_addr = GuestPhysAddr::from_usize(crate::test_utils::MEMORY_LEN + 0x1000); - let result: AxResult = translator.read_obj(invalid_addr); - assert!(result.is_err(), "Reading from invalid address should fail"); - - let result = translator.write_obj(invalid_addr, 42u32); - assert!(result.is_err(), "Writing to invalid address should fail"); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_two_vm_isolation() { - // Create two different translators to simulate two different VMs - let vm1_translator = - MockTranslator::new(PhysAddr::from_usize(0), crate::test_utils::MEMORY_LEN / 2); // Offset for VM1 - let vm2_translator = MockTranslator::new( - PhysAddr::from_usize(crate::test_utils::MEMORY_LEN / 2), - crate::test_utils::MEMORY_LEN, - ); // Offset for VM2 - - // Both VMs write to the same guest address but different host memory regions - let guest_addr = GuestPhysAddr::from_usize(0x100); - let vm1_data: u64 = 0xDEADBEEFCAFEBABE; - let vm2_data: u64 = 0x1234567890ABCDEF; - - // VM1 writes its data - vm1_translator - .write_obj(guest_addr, vm1_data) - .expect("VM1 failed to write data"); - - // VM2 writes its data - vm2_translator - .write_obj(guest_addr, vm2_data) - .expect("VM2 failed to write data"); - - // Both VMs read back their own data - should be isolated - let vm1_read: u64 = vm1_translator - .read_obj(guest_addr) - .expect("VM1 failed to read data"); - let vm2_read: u64 = vm2_translator - .read_obj(guest_addr) - .expect("VM2 failed to read data"); - - // Verify isolation: each VM should read its own data - assert_eq!(vm1_read, vm1_data, "VM1 should read its own data"); - assert_eq!(vm2_read, vm2_data, "VM2 should read its own data"); - assert_ne!( - vm1_read, vm2_read, - "VM1 and VM2 should have different data (isolation)" - ); - - // Test buffer operations with different patterns - let buffer_addr = GuestPhysAddr::from_usize(0x200); - let vm1_buffer = [0xAA; 16]; // Pattern for VM1 - let vm2_buffer = [0x55; 16]; // Pattern for VM2 - - // Both VMs write their patterns - vm1_translator - .write_buffer(buffer_addr, &vm1_buffer) - .expect("VM1 failed to write buffer"); - vm2_translator - .write_buffer(buffer_addr, &vm2_buffer) - .expect("VM2 failed to write buffer"); - - // Read back and verify isolation - let mut vm1_read_buffer = [0u8; 16]; - let mut vm2_read_buffer = [0u8; 16]; - - vm1_translator - .read_buffer(buffer_addr, &mut vm1_read_buffer) - .expect("VM1 failed to read buffer"); - vm2_translator - .read_buffer(buffer_addr, &mut vm2_read_buffer) - .expect("VM2 failed to read buffer"); - - assert_eq!( - vm1_read_buffer, vm1_buffer, - "VM1 should read its own buffer pattern" - ); - assert_eq!( - vm2_read_buffer, vm2_buffer, - "VM2 should read its own buffer pattern" - ); - assert_ne!( - vm1_read_buffer, vm2_read_buffer, - "VM buffers should be isolated" - ); - - // Test that VM1 cannot access VM2's address space (beyond its limit) - let vm2_only_addr = GuestPhysAddr::from_usize(crate::test_utils::MEMORY_LEN / 2 + 0x100); - let result: AxResult = vm1_translator.read_obj(vm2_only_addr); - assert!( - result.is_err(), - "VM1 should not be able to access VM2's exclusive address space" - ); - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_cross_page_access() { - let translator = - MockTranslator::new(PhysAddr::from_usize(0), crate::test_utils::MEMORY_LEN); - - // Test cross-region buffer operations - // Place buffer near a region boundary to test multi-region access - let cross_region_addr = GuestPhysAddr::from_usize(4096 - 8); // 8 bytes before 4K boundary - let test_data = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, - 0x0F, 0x10, - ]; // 16 bytes - - // Write cross-region data - translator - .write_buffer(cross_region_addr, &test_data) - .expect("Failed to write cross-region buffer"); - - // Read cross-region data back - let mut read_data = [0u8; 16]; - translator - .read_buffer(cross_region_addr, &mut read_data) - .expect("Failed to read cross-region buffer"); - - assert_eq!( - read_data, test_data, - "Cross-region read should match written data" - ); - - // Test individual byte access across region boundary - for (i, &expected_byte) in test_data.iter().enumerate() { - let byte_addr = GuestPhysAddr::from_usize(cross_region_addr.as_usize() + i); - let read_byte: u8 = translator - .read_obj(byte_addr) - .expect("Failed to read individual byte"); - assert_eq!( - read_byte, expected_byte, - "Byte at offset {} should match", - i - ); - } - } - - #[test] - #[axin(decorator(mock_hal_test))] - fn test_region_boundary_edge_cases() { - let translator = - MockTranslator::new(PhysAddr::from_usize(0), crate::test_utils::MEMORY_LEN); - - let boundary_addr = GuestPhysAddr::from_usize(4096); - let boundary_data = [0xAB, 0xCD, 0xEF, 0x12]; - - translator - .write_buffer(boundary_addr, &boundary_data) - .expect("Failed to write at boundary"); - - let mut read_boundary = [0u8; 4]; - translator - .read_buffer(boundary_addr, &mut read_boundary) - .expect("Failed to read at boundary"); - - assert_eq!(read_boundary, boundary_data, "Boundary data should match"); - - // Test zero-size buffer (should not fail) - let empty_buffer: &[u8] = &[]; - translator - .write_buffer(boundary_addr, empty_buffer) - .expect("Empty buffer write should succeed"); - - let mut empty_read: &mut [u8] = &mut []; - translator - .read_buffer(boundary_addr, &mut empty_read) - .expect("Empty buffer read should succeed"); - - // Test single byte at boundary (should work fine) - let single_byte = [0x42]; - translator - .write_buffer(boundary_addr, &single_byte) - .expect("Single byte write should succeed"); - } -} diff --git a/src/npt/arch/x86_64.rs b/src/npt/arch/x86_64.rs index 65b62617f..82a952c17 100644 --- a/src/npt/arch/x86_64.rs +++ b/src/npt/arch/x86_64.rs @@ -188,15 +188,18 @@ impl PagingMetaData for ExtendedPageTableMetadata { type VirtAddr = GuestPhysAddr; - // Under the x86 architecture, the flush_tlb operation will invoke the ring0 instruction, - // causing the test to trigger a SIGSEGV exception. + // Under the x86 architecture, flushing the TLB requires privileged + // instructions. Hosted binaries such as integration tests run in ring 3, + // so issue TLB invalidations only for bare-metal targets. #[allow(unused_variables)] fn flush_tlb(vaddr: Option) { - #[cfg(not(test))] - if let Some(vaddr) = vaddr { - unsafe { x86::tlb::flush(vaddr.into()) } - } else { - unsafe { x86::tlb::flush_all() } + #[cfg(target_os = "none")] + { + if let Some(vaddr) = vaddr { + unsafe { x86::tlb::flush(vaddr.into()) } + } else { + unsafe { x86::tlb::flush_all() } + } } } } diff --git a/tests/address_space.rs b/tests/address_space.rs new file mode 100644 index 000000000..e78285ca2 --- /dev/null +++ b/tests/address_space.rs @@ -0,0 +1,337 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod test_utils; + +use axaddrspace::{AddrSpace, GuestPhysAddr, MappingFlags}; +use axin::axin; +use core::sync::atomic::Ordering; +use memory_addr::PhysAddr; +use test_utils::{ + ALLOC_COUNT, BASE_PADDR, DEALLOC_COUNT, MEMORY_LEN, MockHal, mock_hal_test, test_dealloc_count, +}; + +/// Generate an address space for the test +fn setup_test_addr_space() -> (AddrSpace, GuestPhysAddr, usize) { + const BASE: GuestPhysAddr = GuestPhysAddr::from_usize(0x10000); + const SIZE: usize = 0x10000; + let addr_space = AddrSpace::::new_empty(4, BASE, SIZE).unwrap(); + (addr_space, BASE, SIZE) +} + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] +/// Check whether an address_space can be created correctly. +/// When creating a new address_space, a frame will be allocated for the page table, +/// thus triggering an alloc_frame operation. +fn test_addrspace_creation() { + let (addr_space, base, size) = setup_test_addr_space(); + assert_eq!(addr_space.base(), base); + assert_eq!(addr_space.size(), size); + assert_eq!(addr_space.end(), base + size); + assert_eq!(ALLOC_COUNT.load(Ordering::SeqCst), 1); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_contains_range() { + let (addr_space, base, size) = setup_test_addr_space(); + + // Within range + assert!(addr_space.contains_range(base, 0x1000)); + assert!(addr_space.contains_range(base + 0x1000, 0x2000)); + assert!(addr_space.contains_range(base, size)); + + // Out of range + assert!(!addr_space.contains_range(base - 0x1000, 0x1000)); + assert!(!addr_space.contains_range(base + size, 0x1000)); + assert!(!addr_space.contains_range(base, size + 0x1000)); + + // Partially out of range + assert!(!addr_space.contains_range(base + 0x3000, 0xf000)); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_map_linear() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x18000); + let paddr = PhysAddr::from_usize(0x10000); + let map_linear_size = 0x8000; // 32KB + let flags = MappingFlags::READ | MappingFlags::WRITE; + + addr_space + .map_linear(vaddr, paddr, map_linear_size, flags) + .unwrap(); + + assert_eq!(addr_space.translate(vaddr).unwrap(), paddr); + assert_eq!( + addr_space.translate(vaddr + 0x1000).unwrap(), + paddr + 0x1000 + ); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_map_alloc_populate() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x10000); + let map_alloc_size = 0x2000; // 8KB + let flags = MappingFlags::READ | MappingFlags::WRITE; + + // Frame count before allocation: 1 root page table + let initial_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + assert_eq!(initial_allocs, 1); + + // Allocate physical frames immediately + addr_space + .map_alloc(vaddr, map_alloc_size, flags, true) + .unwrap(); + + // Verify additional frames were allocated + let final_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + assert!(final_allocs > initial_allocs); + + // Verify mappings exist and addresses are valid + let paddr1 = addr_space.translate(vaddr).unwrap(); + let paddr2 = addr_space.translate(vaddr + 0x1000).unwrap(); + + // Verify physical addresses are within valid range + assert!(paddr1.as_usize() >= BASE_PADDR && paddr1.as_usize() < BASE_PADDR + MEMORY_LEN); + assert!(paddr2.as_usize() >= BASE_PADDR && paddr2.as_usize() < BASE_PADDR + MEMORY_LEN); + + // Verify two pages have different physical addresses + assert_ne!(paddr1, paddr2); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_map_alloc_lazy() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x13000); + let map_alloc_size = 0x1000; + let flags = MappingFlags::READ | MappingFlags::WRITE; + + let initial_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + + // Lazy allocation - don't allocate physical frames immediately + addr_space + .map_alloc(vaddr, map_alloc_size, flags, false) + .unwrap(); + + // Frame count should only increase for page table structure, not data pages + let after_map_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + assert!(after_map_allocs >= initial_allocs); // May have allocated intermediate page tables + assert!(addr_space.translate(vaddr).is_none()); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_page_fault_handling() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x14000); + let map_alloc_size = 0x1000; + let flags = MappingFlags::READ | MappingFlags::WRITE; + + // Create lazy allocation mapping + addr_space + .map_alloc(vaddr, map_alloc_size, flags, false) + .unwrap(); + + let before_pf_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + + // Simulate page fault + let handled = addr_space.handle_page_fault(vaddr, MappingFlags::READ); + + // Page fault should be handled + assert!(handled); + + // Should have allocated physical frames + let after_pf_allocs = ALLOC_COUNT.load(Ordering::SeqCst); + assert!(after_pf_allocs > before_pf_allocs); + + // Translation should succeed now + let paddr = addr_space.translate(vaddr); + assert!(paddr.is_some()); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_unmap() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x15000); + let map_alloc_size = 0x2000; + let flags = MappingFlags::READ | MappingFlags::WRITE; + + // Create mapping + addr_space + .map_alloc(vaddr, map_alloc_size, flags, true) + .unwrap(); + + // Verify mapping exists + assert!(addr_space.translate(vaddr).is_some()); + assert!(addr_space.translate(vaddr + 0x1000).is_some()); + + let before_unmap_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); + + // Unmap + addr_space.unmap(vaddr, map_alloc_size).unwrap(); + + // Verify mapping is removed + assert!(addr_space.translate(vaddr).is_none()); + assert!(addr_space.translate(vaddr + 0x1000).is_none()); + + // Verify frames were deallocated + let after_unmap_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); + assert!(after_unmap_deallocs > before_unmap_deallocs); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_clear() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr1 = GuestPhysAddr::from_usize(0x16000); + let vaddr2 = GuestPhysAddr::from_usize(0x17000); + let flags = MappingFlags::READ | MappingFlags::WRITE; + let map_alloc_size = 0x1000; + + // Create multiple mappings + addr_space + .map_alloc(vaddr1, map_alloc_size, flags, true) + .unwrap(); + addr_space + .map_alloc(vaddr2, map_alloc_size, flags, true) + .unwrap(); + + // Verify mappings exist + assert!(addr_space.translate(vaddr1).is_some()); + assert!(addr_space.translate(vaddr2).is_some()); + + let before_clear_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); + + // Clear all mappings + addr_space.clear(); + + // Verify all mappings are removed + assert!(addr_space.translate(vaddr1).is_none()); + assert!(addr_space.translate(vaddr2).is_none()); + + // Verify frames were deallocated + let after_clear_deallocs = DEALLOC_COUNT.load(Ordering::SeqCst); + assert!(after_clear_deallocs > before_clear_deallocs); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_translate() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x18000); + let map_alloc_size = 0x1000; + let flags = MappingFlags::READ | MappingFlags::WRITE; + + // Create mapping + addr_space + .map_alloc(vaddr, map_alloc_size, flags, true) + .unwrap(); + + // Verify translation succeeds + let paddr = addr_space.translate(vaddr).expect("Translation failed"); + assert!(paddr.as_usize() >= BASE_PADDR); + assert!(paddr.as_usize() < BASE_PADDR + MEMORY_LEN); + + // Verify unmapped address translation fails + let unmapped_vaddr = GuestPhysAddr::from_usize(0x19000); + assert!(addr_space.translate(unmapped_vaddr).is_none()); + + // Verify out-of-range address translation fails + let out_of_range = GuestPhysAddr::from_usize(0x30000); + assert!(addr_space.translate(out_of_range).is_none()); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_translated_byte_buffer() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x19000); + let map_alloc_size = 0x2000; // 8KB + let flags = MappingFlags::READ | MappingFlags::WRITE; + let buffer_size = 0x1100; + + // Create mapping + addr_space + .map_alloc(vaddr, map_alloc_size, flags, true) + .unwrap(); + + // Verify byte buffer can be obtained + let mut buffer = addr_space + .translated_byte_buffer(vaddr, buffer_size) + .expect("Failed to get byte buffer"); + + // Verify data write and read + // Fill with values ranging from 0 to 0x100 + for buffer_segment in buffer.iter_mut() { + for (i, byte) in buffer_segment.iter_mut().enumerate() { + *byte = (i % 0x100) as u8; + } + } + + // Verify data read correctness + for buffer_segment in buffer.iter_mut() { + for (i, byte) in buffer_segment.iter_mut().enumerate() { + assert_eq!(*byte, (i % 0x100) as u8); + } + } + + // Verify exceeding area size returns None + assert!( + addr_space + .translated_byte_buffer(vaddr, map_alloc_size + 0x1000) + .is_none() + ); + + // Verify unmapped address returns None + let unmapped_vaddr = GuestPhysAddr::from_usize(0x1D000); + assert!( + addr_space + .translated_byte_buffer(unmapped_vaddr, 0x100) + .is_none() + ); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_translate_and_get_limit() { + let (mut addr_space, _base, _size) = setup_test_addr_space(); + let vaddr = GuestPhysAddr::from_usize(0x1A000); + let map_alloc_size = 0x3000; // 12KB + let flags = MappingFlags::READ | MappingFlags::WRITE; + + // Create mapping + addr_space + .map_alloc(vaddr, map_alloc_size, flags, true) + .unwrap(); + + // Verify translation and area size retrieval + let (paddr, area_size) = addr_space.translate_and_get_limit(vaddr).unwrap(); + assert!(paddr.as_usize() >= BASE_PADDR && paddr.as_usize() < BASE_PADDR + MEMORY_LEN); + assert_eq!(area_size, map_alloc_size); + + // Verify unmapped address returns None + let unmapped_vaddr = GuestPhysAddr::from_usize(0x1E000); + assert!(addr_space.translate_and_get_limit(unmapped_vaddr).is_none()); + + // Verify out-of-range address returns None + let out_of_range = GuestPhysAddr::from_usize(0x30000); + assert!(addr_space.translate_and_get_limit(out_of_range).is_none()); +} diff --git a/tests/frame.rs b/tests/frame.rs new file mode 100644 index 000000000..eab6bd228 --- /dev/null +++ b/tests/frame.rs @@ -0,0 +1,104 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate alloc; + +mod test_utils; + +use alloc::vec::Vec; +use assert_matches::assert_matches; +use axaddrspace::PhysFrame; +use axin::axin; +use memory_addr::PAGE_SIZE_4K as PAGE_SIZE; +use test_utils::{BASE_PADDR, MockHal, mock_hal_test, test_dealloc_count}; + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] +fn test_alloc_dealloc_cycle() { + let frame = PhysFrame::::alloc() + .unwrap_or_else(|e| panic!("Failed to allocate frame: {:?}", e)); + assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); + // frame is dropped here, dealloc_frame should be called +} + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] +fn test_alloc_zero() { + let frame = PhysFrame::::alloc_zero() + .unwrap_or_else(|e| panic!("Failed to allocate zero frame: {:?}", e)); + assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); + let ptr = frame.as_mut_ptr(); + let page = unsafe { &*(ptr as *const [u8; PAGE_SIZE]) }; + assert!(page.iter().all(|&x| x == 0)); +} + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(1)))] +fn test_fill_operation() { + let mut frame = PhysFrame::::alloc() + .unwrap_or_else(|e| panic!("Failed to allocate frame: {:?}", e)); + assert_eq!(frame.start_paddr().as_usize(), BASE_PADDR); + frame.fill(0xAA); + let ptr = frame.as_mut_ptr(); + let page = unsafe { &*(ptr as *const [u8; PAGE_SIZE]) }; + assert!(page.iter().all(|&x| x == 0xAA)); +} + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(5)))] +fn test_fill_multiple_frames() { + const NUM_FRAMES: usize = 5; + + let mut frames = Vec::new(); + let mut patterns = Vec::new(); + + for i in 0..NUM_FRAMES { + let mut frame = PhysFrame::::alloc().unwrap(); + let pattern = (0xA0 + i) as u8; + frame.fill(pattern); + frames.push(frame); + patterns.push(pattern); + } + + for i in 0..NUM_FRAMES { + let actual_page = unsafe { &*(frames[i].as_mut_ptr() as *mut [u8; PAGE_SIZE]) }; + let expected_page = &[patterns[i]; PAGE_SIZE]; + + assert_eq!( + actual_page, expected_page, + "Frame verification failed for frame index {i}: Expected pattern 0x{:02x}", + patterns[i] + ); + } +} + +#[test] +#[should_panic(expected = "uninitialized PhysFrame")] +fn test_uninit_access() { + // This test verifies that accessing an uninitialized PhysFrame (created with `unsafe { uninit() }`) + // leads to a panic when trying to retrieve its physical address. + let frame = unsafe { PhysFrame::::uninit() }; + frame.start_paddr(); // This should panic +} + +#[test] +#[axin(decorator(mock_hal_test), on_exit(test_dealloc_count(0)))] +fn test_alloc_no_memory() { + // Configure MockHal to simulate an allocation failure. + MockHal::set_alloc_fail(true); + let result = PhysFrame::::alloc(); + // Assert that allocation failed and verify the specific error type. + assert_matches!(result, Err(axerrno::AxError::NoMemory)); + MockHal::set_alloc_fail(false); // Reset for other tests +} diff --git a/tests/memory_accessor.rs b/tests/memory_accessor.rs new file mode 100644 index 000000000..95f4dc460 --- /dev/null +++ b/tests/memory_accessor.rs @@ -0,0 +1,269 @@ +// Copyright 2025 The Axvisor Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod test_utils; + +use axaddrspace::{GuestMemoryAccessor, GuestPhysAddr}; +use axerrno::AxResult; +use axin::axin; +use memory_addr::PhysAddr; +use test_utils::{BASE_PADDR, MEMORY_LEN, MockHal, mock_hal_test}; + +/// Mock implementation of GuestMemoryAccessor for testing +struct MockTranslator { + base_addr: PhysAddr, + memory_size: usize, +} + +impl MockTranslator { + pub fn new(base_addr: PhysAddr, memory_size: usize) -> Self { + Self { + base_addr, + memory_size, + } + } +} + +impl GuestMemoryAccessor for MockTranslator { + fn translate_and_get_limit(&self, guest_addr: GuestPhysAddr) -> Option<(PhysAddr, usize)> { + // Simple mapping: guest address directly maps to mock memory region + let offset = guest_addr.as_usize(); + if offset < self.memory_size { + // Convert physical address to virtual address for actual memory access + let phys_addr = PhysAddr::from_usize(BASE_PADDR + self.base_addr.as_usize() + offset); + let virt_addr = MockHal::mock_phys_to_virt(phys_addr); + let accessible_size = self.memory_size - offset; + Some((PhysAddr::from_usize(virt_addr.as_usize()), accessible_size)) + } else { + None + } + } +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_basic_read_write_operations() { + let translator = MockTranslator::new(PhysAddr::from_usize(0), MEMORY_LEN); + + // Test u32 read/write operations + let test_addr = GuestPhysAddr::from_usize(0x100); + let test_value: u32 = 0x12345678; + + // Write a u32 value + translator + .write_obj(test_addr, test_value) + .expect("Failed to write u32 value"); + + // Read back the u32 value + let read_value: u32 = translator + .read_obj(test_addr) + .expect("Failed to read u32 value"); + + assert_eq!( + read_value, test_value, + "Read value should match written value" + ); + + // Test buffer read/write operations + let buffer_addr = GuestPhysAddr::from_usize(0x200); + let test_buffer = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]; + + // Write buffer + translator + .write_buffer(buffer_addr, &test_buffer) + .expect("Failed to write buffer"); + + // Read buffer back + let mut read_buffer = [0u8; 8]; + translator + .read_buffer(buffer_addr, &mut read_buffer) + .expect("Failed to read buffer"); + + assert_eq!( + read_buffer, test_buffer, + "Read buffer should match written buffer" + ); + + // Test error handling with invalid address + let invalid_addr = GuestPhysAddr::from_usize(MEMORY_LEN + 0x1000); + let result: AxResult = translator.read_obj(invalid_addr); + assert!(result.is_err(), "Reading from invalid address should fail"); + + let result = translator.write_obj(invalid_addr, 42u32); + assert!(result.is_err(), "Writing to invalid address should fail"); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_two_vm_isolation() { + // Create two different translators to simulate two different VMs + let vm1_translator = MockTranslator::new(PhysAddr::from_usize(0), MEMORY_LEN / 2); // Offset for VM1 + let vm2_translator = MockTranslator::new(PhysAddr::from_usize(MEMORY_LEN / 2), MEMORY_LEN); // Offset for VM2 + + // Both VMs write to the same guest address but different host memory regions + let guest_addr = GuestPhysAddr::from_usize(0x100); + let vm1_data: u64 = 0xDEADBEEFCAFEBABE; + let vm2_data: u64 = 0x1234567890ABCDEF; + + // VM1 writes its data + vm1_translator + .write_obj(guest_addr, vm1_data) + .expect("VM1 failed to write data"); + + // VM2 writes its data + vm2_translator + .write_obj(guest_addr, vm2_data) + .expect("VM2 failed to write data"); + + // Both VMs read back their own data - should be isolated + let vm1_read: u64 = vm1_translator + .read_obj(guest_addr) + .expect("VM1 failed to read data"); + let vm2_read: u64 = vm2_translator + .read_obj(guest_addr) + .expect("VM2 failed to read data"); + + // Verify isolation: each VM should read its own data + assert_eq!(vm1_read, vm1_data, "VM1 should read its own data"); + assert_eq!(vm2_read, vm2_data, "VM2 should read its own data"); + assert_ne!( + vm1_read, vm2_read, + "VM1 and VM2 should have different data (isolation)" + ); + + // Test buffer operations with different patterns + let buffer_addr = GuestPhysAddr::from_usize(0x200); + let vm1_buffer = [0xAA; 16]; // Pattern for VM1 + let vm2_buffer = [0x55; 16]; // Pattern for VM2 + + // Both VMs write their patterns + vm1_translator + .write_buffer(buffer_addr, &vm1_buffer) + .expect("VM1 failed to write buffer"); + vm2_translator + .write_buffer(buffer_addr, &vm2_buffer) + .expect("VM2 failed to write buffer"); + + // Read back and verify isolation + let mut vm1_read_buffer = [0u8; 16]; + let mut vm2_read_buffer = [0u8; 16]; + + vm1_translator + .read_buffer(buffer_addr, &mut vm1_read_buffer) + .expect("VM1 failed to read buffer"); + vm2_translator + .read_buffer(buffer_addr, &mut vm2_read_buffer) + .expect("VM2 failed to read buffer"); + + assert_eq!( + vm1_read_buffer, vm1_buffer, + "VM1 should read its own buffer pattern" + ); + assert_eq!( + vm2_read_buffer, vm2_buffer, + "VM2 should read its own buffer pattern" + ); + assert_ne!( + vm1_read_buffer, vm2_read_buffer, + "VM buffers should be isolated" + ); + + // Test that VM1 cannot access VM2's address space (beyond its limit) + let vm2_only_addr = GuestPhysAddr::from_usize(MEMORY_LEN / 2 + 0x100); + let result: AxResult = vm1_translator.read_obj(vm2_only_addr); + assert!( + result.is_err(), + "VM1 should not be able to access VM2's exclusive address space" + ); +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_cross_page_access() { + let translator = MockTranslator::new(PhysAddr::from_usize(0), MEMORY_LEN); + + // Test cross-region buffer operations + // Place buffer near a region boundary to test multi-region access + let cross_region_addr = GuestPhysAddr::from_usize(4096 - 8); // 8 bytes before 4K boundary + let test_data = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, + ]; // 16 bytes + + // Write cross-region data + translator + .write_buffer(cross_region_addr, &test_data) + .expect("Failed to write cross-region buffer"); + + // Read cross-region data back + let mut read_data = [0u8; 16]; + translator + .read_buffer(cross_region_addr, &mut read_data) + .expect("Failed to read cross-region buffer"); + + assert_eq!( + read_data, test_data, + "Cross-region read should match written data" + ); + + // Test individual byte access across region boundary + for (i, &expected_byte) in test_data.iter().enumerate() { + let byte_addr = GuestPhysAddr::from_usize(cross_region_addr.as_usize() + i); + let read_byte: u8 = translator + .read_obj(byte_addr) + .expect("Failed to read individual byte"); + assert_eq!( + read_byte, expected_byte, + "Byte at offset {} should match", + i + ); + } +} + +#[test] +#[axin(decorator(mock_hal_test))] +fn test_region_boundary_edge_cases() { + let translator = MockTranslator::new(PhysAddr::from_usize(0), MEMORY_LEN); + + let boundary_addr = GuestPhysAddr::from_usize(4096); + let boundary_data = [0xAB, 0xCD, 0xEF, 0x12]; + + translator + .write_buffer(boundary_addr, &boundary_data) + .expect("Failed to write at boundary"); + + let mut read_boundary = [0u8; 4]; + translator + .read_buffer(boundary_addr, &mut read_boundary) + .expect("Failed to read at boundary"); + + assert_eq!(read_boundary, boundary_data, "Boundary data should match"); + + // Test zero-size buffer (should not fail) + let empty_buffer: &[u8] = &[]; + translator + .write_buffer(boundary_addr, empty_buffer) + .expect("Empty buffer write should succeed"); + + let mut empty_read: &mut [u8] = &mut []; + translator + .read_buffer(boundary_addr, &mut empty_read) + .expect("Empty buffer read should succeed"); + + // Test single byte at boundary (should work fine) + let single_byte = [0x42]; + translator + .write_buffer(boundary_addr, &single_byte) + .expect("Single byte write should succeed"); +} diff --git a/src/test_utils/mod.rs b/tests/test_utils/mod.rs similarity index 85% rename from src/test_utils/mod.rs rename to tests/test_utils/mod.rs index e2d6a4709..cc0a17b35 100644 --- a/src/test_utils/mod.rs +++ b/tests/test_utils/mod.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{AxMmHal, HostPhysAddr, HostVirtAddr}; +use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use lazy_static::lazy_static; use memory_addr::{PhysAddr, VirtAddr}; @@ -23,17 +23,17 @@ use memory_addr::PAGE_SIZE_4K as PAGE_SIZE; /// The starting physical address for the simulated memory region in tests. /// This offset is used to map simulated physical addresses to the `MEMORY` array's virtual address space. -pub(crate) const BASE_PADDR: usize = 0x1000; +pub const BASE_PADDR: usize = 0x1000; /// Static variables to simulate global state of a memory allocator in tests. -pub(crate) static NEXT_PADDR: AtomicUsize = AtomicUsize::new(BASE_PADDR); +pub static NEXT_PADDR: AtomicUsize = AtomicUsize::new(BASE_PADDR); /// Total length of the simulated physical memory block for testing, in bytes. -pub(crate) const MEMORY_LEN: usize = 0x10000; // 64KB for testing +pub const MEMORY_LEN: usize = 0x10000; // 64KB for testing // Use #[repr(align(4096))] to ensure 4KB alignment #[repr(align(4096))] -pub(crate) struct AlignedMemory([u8; MEMORY_LEN]); +pub struct AlignedMemory([u8; MEMORY_LEN]); impl Default for AlignedMemory { fn default() -> Self { @@ -43,21 +43,21 @@ impl Default for AlignedMemory { lazy_static! { /// Simulates the actual physical memory block used for allocation. - pub(crate) static ref MEMORY: Mutex = Mutex::new(AlignedMemory::default()); + pub static ref MEMORY: Mutex = Mutex::new(AlignedMemory::default()); /// Global mutex to enforce serial execution for tests that modify shared state. /// This ensures test isolation and prevents race conditions between tests. - pub(crate) static ref TEST_MUTEX: Mutex<()> = Mutex::new(()); + pub static ref TEST_MUTEX: Mutex<()> = Mutex::new(()); } /// Counter to track the number of allocations. (Added from Chen Hong's code) -pub(crate) static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0); +pub static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0); /// Counter to track the number of deallocations. -pub(crate) static DEALLOC_COUNT: AtomicUsize = AtomicUsize::new(0); +pub static DEALLOC_COUNT: AtomicUsize = AtomicUsize::new(0); /// Flag to simulate memory allocation failures for testing error handling. -pub(crate) static ALLOC_SHOULD_FAIL: AtomicBool = AtomicBool::new(false); +pub static ALLOC_SHOULD_FAIL: AtomicBool = AtomicBool::new(false); #[derive(Debug)] /// A mock implementation of AxMmHal for testing purposes. @@ -65,7 +65,7 @@ pub(crate) static ALLOC_SHOULD_FAIL: AtomicBool = AtomicBool::new(false); /// /// The `Debug` trait is derived because `assert_matches!` on `Result, _>` /// requires `PhysFrame` (the `T` type) to implement `Debug` for diagnostic output on assertion failure. -pub(crate) struct MockHal {} +pub struct MockHal {} impl AxMmHal for MockHal { fn alloc_frame() -> Option { @@ -121,7 +121,7 @@ impl PagingHandler for MockHal { } /// A utility decorator for test functions that require the MockHal state to be reset before execution. -pub(crate) fn mock_hal_test(test_fn: F) -> R +pub fn mock_hal_test(test_fn: F) -> R where F: FnOnce() -> R, { @@ -131,7 +131,7 @@ where } /// A utility function to verify the number of deallocations performed by the MockHal. -pub(crate) fn test_dealloc_count(expected: usize) { +pub fn test_dealloc_count(expected: usize) { let actual_dealloc_count = DEALLOC_COUNT.load(Ordering::SeqCst); assert_eq!( actual_dealloc_count, expected, @@ -141,7 +141,7 @@ pub(crate) fn test_dealloc_count(expected: usize) { impl MockHal { /// Simulates the allocation of a single physical frame. - pub(crate) fn mock_alloc_frame() -> Option { + pub fn mock_alloc_frame() -> Option { // Use a static mutable variable to control alloc_should_fail state if ALLOC_SHOULD_FAIL.load(Ordering::SeqCst) { return None; @@ -156,14 +156,14 @@ impl MockHal { } /// Simulates the deallocation of a single physical frame. - pub(crate) fn mock_dealloc_frame(_paddr: PhysAddr) { + pub fn mock_dealloc_frame(_paddr: PhysAddr) { DEALLOC_COUNT.fetch_add(1, Ordering::SeqCst); } /// In this test mock, the "virtual address" is simply a direct pointer /// to the corresponding location within the `MEMORY` array. /// It simulates a physical-to-virtual memory mapping for test purposes. - pub(crate) fn mock_phys_to_virt(paddr: PhysAddr) -> VirtAddr { + pub fn mock_phys_to_virt(paddr: PhysAddr) -> VirtAddr { let paddr_usize = paddr.as_usize(); assert!( paddr_usize >= BASE_PADDR && paddr_usize < BASE_PADDR + MEMORY_LEN, @@ -175,7 +175,7 @@ impl MockHal { } /// Maps a virtual address (within the test process) back to a simulated physical address. - pub(crate) fn mock_virt_to_phys(vaddr: VirtAddr) -> PhysAddr { + pub fn mock_virt_to_phys(vaddr: VirtAddr) -> PhysAddr { let base_virt = MEMORY.lock().0.as_ptr() as usize; let vaddr_usize = vaddr.as_usize(); assert!( @@ -188,13 +188,13 @@ impl MockHal { } /// Helper function to control the simulated allocation failure. - pub(crate) fn set_alloc_fail(fail: bool) { + pub fn set_alloc_fail(fail: bool) { ALLOC_SHOULD_FAIL.store(fail, Ordering::SeqCst); } /// Resets all static state of the MockHal to its initial, clean state. /// This is crucial for ensuring test isolation between individual test functions. - pub(crate) fn reset_state() { + pub fn reset_state() { NEXT_PADDR.store(BASE_PADDR, Ordering::SeqCst); ALLOC_SHOULD_FAIL.store(false, Ordering::SeqCst); ALLOC_COUNT.store(0, Ordering::SeqCst); From dcf1e09504917d43c3849d9ae12a017740b7e9f5 Mon Sep 17 00:00:00 2001 From: YanLien <128586861+YanLien@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:04:00 +0800 Subject: [PATCH 119/132] ci: refactor workflows to use axci shared actions and migrate tests (#51) * ci: refactor workflows to use axci shared actions and migrate tests * chore: update comments in scripts for axdevice_base --- .github/config.json | 8 +- .github/workflows/check.yml | 67 ++------------ .github/workflows/deploy.yml | 107 ++--------------------- .github/workflows/push.yml | 145 ++----------------------------- .github/workflows/release.yml | 159 +++------------------------------- .github/workflows/test.yml | 43 ++------- .gitignore | 8 ++ rust-toolchain.toml | 10 +++ scripts/check.sh | 35 ++++++++ scripts/test.sh | 34 ++++++++ src/lib.rs | 3 - {src => tests}/test.rs | 5 +- 12 files changed, 137 insertions(+), 487 deletions(-) create mode 100644 rust-toolchain.toml create mode 100755 scripts/check.sh create mode 100755 scripts/test.sh rename {src => tests}/test.rs (96%) diff --git a/.github/config.json b/.github/config.json index f347e101e..ee774f698 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1,10 +1,16 @@ { + "component": { + "name": "axdevice_base", + "crate_name": "axdevice_base" + }, "targets": [ "aarch64-unknown-none-softfloat", - "x86_64-unknown-linux-gnu", "x86_64-unknown-none", "riscv64gc-unknown-none-elf" ], + "unit_test_targets": [ + "x86_64-unknown-linux-gnu" + ], "rust_components": [ "rust-src", "clippy", diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 330fa15e9..018bbbeb2 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,66 +1,15 @@ -name: Quality Checks +# Quality Check Workflow +# References shared workflow from axci + +name: Check on: push: - branches: - - '**' - tags-ignore: - - '**' + branches: ['**'] + tags-ignore: ['**'] pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - rust_components: ${{ steps.config.outputs.rust_components }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) - - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT - check: - name: Check - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: ${{ needs.load-config.outputs.rust_components }} - targets: ${{ matrix.target }} - - - name: Check rust version - run: rustc --version --verbose - - - name: Check code format - run: cargo fmt --all -- --check - - - name: Build - run: cargo build --target ${{ matrix.target }} --all-features - - - name: Run clippy - run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings - - - name: Build documentation - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 882ba9310..37b00c09c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,3 +1,6 @@ +# Deploy Workflow +# References shared workflow from axci + name: Deploy on: @@ -5,105 +8,9 @@ on: tags: - 'v[0-9]+.[0-9]+.[0-9]+' -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: 'pages' - cancel-in-progress: false - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_deploy: ${{ steps.check.outputs.should_deploy }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check if tag is on main or master branch - id: check - run: | - git fetch origin main master || true - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Tag is on main or master branch" - echo "should_deploy=true" >> $GITHUB_OUTPUT - else - echo "✗ Tag is not on main or master branch, skipping deployment" - echo "Tag is on: $BRANCHES" - echo "should_deploy=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_deploy == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - test: - uses: ./.github/workflows/test.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - - build: - name: Build documentation - runs-on: ubuntu-latest - needs: [verify-tag, check, test] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Build docs - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: target/doc - deploy: - name: Deploy to GitHub Pages - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: [verify-tag, build] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + uses: arceos-hypervisor/axci/.github/workflows/deploy.yml@main + with: + verify_branch: true + verify_version: true diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a18de4a04..3ff391a4a 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,147 +1,16 @@ -# ═══════════════════════════════════════════════════════════════════════════════ -# 组件仓库 GitHub Actions 配置模板 -# ═══════════════════════════════════════════════════════════════════════════════ -# -# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 -# -# 【使用步骤】 -# ───────────────────────────────────────────────────────────────────────────── -# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: -# cp scripts/push.yml <子仓库>/.github/workflows/push.yml -# -# 2. 在子仓库中配置 Secret: -# GitHub 仓库 → Settings → Secrets → Actions → New repository secret -# 名称: PARENT_REPO_TOKEN -# 值: 具有主仓库 repo 权限的 Personal Access Token -# -# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): -# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits -# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) -# -# 【Token 权限要求】 -# ───────────────────────────────────────────────────────────────────────────── -# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: -# - repo (Full control of private repositories) -# 或 -# - Fine-grained token: Contents (Read and Write) -# -# 【触发条件】 -# ───────────────────────────────────────────────────────────────────────────── -# - 自动触发:推送到 dev 或 main 分支时 -# - 手动触发:Actions → Notify Parent Repository → Run workflow -# -# 【工作流程】 -# ───────────────────────────────────────────────────────────────────────────── -# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull -# -# 【注意事项】 -# ───────────────────────────────────────────────────────────────────────────── -# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 -# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 -# -# ═══════════════════════════════════════════════════════════════════════════════ - name: Notify Parent Repository -# 当有新的推送时触发 on: push: branches: - main - - master + - zcs workflow_dispatch: jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Get repository info - id: repo - env: - GH_REPO_NAME: ${{ github.event.repository.name }} - GH_REF_NAME: ${{ github.ref_name }} - GH_SERVER_URL: ${{ github.server_url }} - GH_REPOSITORY: ${{ github.repository }} - run: | - # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 - COMPONENT="$GH_REPO_NAME" - BRANCH="$GH_REF_NAME" - # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list - REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" - - echo "component=${COMPONENT}" >> $GITHUB_OUTPUT - echo "branch=${BRANCH}" >> $GITHUB_OUTPUT - echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT - - echo "Component: ${COMPONENT}" - echo "Branch: ${BRANCH}" - echo "Repo URL: ${REPO_URL}" - - - name: Notify parent repository - env: - # ── 需要修改 ────────────────────────────────────────────────────────── - PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 - # ── 无需修改 ────────────────────────────────────────────────────────── - DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} - # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 - COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - GIT_ACTOR: ${{ github.actor }} - GIT_SHA: ${{ github.sha }} - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" - - # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 - PAYLOAD=$(jq -n \ - --arg component "$COMPONENT" \ - --arg branch "$BRANCH" \ - --arg repo_url "$REPO_URL" \ - --arg commit "$GIT_SHA" \ - --arg message "$COMMIT_MESSAGE" \ - --arg author "$GIT_ACTOR" \ - '{ - event_type: "subtree-update", - client_payload: { - component: $component, - branch: $branch, - repo_url: $repo_url, - commit: $commit, - message: $message, - author: $author - } - }') - - curl --fail --show-error -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${DISPATCH_TOKEN}" \ - https://api.github.com/repos/${PARENT_REPO}/dispatches \ - -d "$PAYLOAD" - - echo "Notification sent successfully" - - - name: Create summary - env: - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - GIT_SHA: ${{ github.sha }} - GIT_ACTOR: ${{ github.actor }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY - echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY + notify-parent: + name: Notify Parent Repository + # 调用 axci 仓库的可复用工作流 + uses: arceos-hypervisor/axci/.github/workflows/push.yml@main + secrets: + PARENT_REPO_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e857b48b..20f186395 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,7 @@ +# Release Workflow +# References shared workflow from axci +# check + test must pass before release + name: Release on: @@ -6,155 +10,18 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-pre.[0-9]+' -permissions: - contents: write - jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - is_prerelease: ${{ steps.check.outputs.is_prerelease }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check tag type and branch - id: check - run: | - git fetch origin main master dev || true - - TAG="${{ github.ref_name }}" - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - echo "Tag: $TAG" - echo "Branches containing this tag: $BRANCHES" - - # Check if it's a prerelease tag - if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-pre\.[0-9]+$ ]]; then - echo "📦 Detected prerelease tag" - echo "is_prerelease=true" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -q 'origin/dev'; then - echo "✓ Prerelease tag is on dev branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Prerelease tag must be on dev branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "📦 Detected stable release tag" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Stable release tag is on main or master branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Stable release tag must be on main or master branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - else - echo "✗ Unknown tag format, skipping release" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - echo "should_release=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_release == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/check.yml@main test: - uses: ./.github/workflows/test.yml - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main release: - name: Create GitHub Release - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate release notes - id: release_notes - run: | - CURRENT_TAG="${{ github.ref_name }}" - - # Get previous tag - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) - - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then - echo "No previous tag found, this is the first release" - CHANGELOG="Initial release" - else - echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" - - # Generate changelog with commit messages - CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") - - if [ -z "$CHANGELOG" ]; then - CHANGELOG="No changes" - fi - fi - - # Write changelog to output file (multi-line) - { - echo "changelog<> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} - body: | - ## Changes - ${{ steps.release_notes.outputs.changelog }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish: - name: Publish to crates.io - runs-on: ubuntu-latest - needs: [verify-tag, check] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Dry run publish - run: cargo publish --dry-run - - - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + needs: [check, test] + uses: arceos-hypervisor/axci/.github/workflows/release.yml@main + with: + verify_branch: true + verify_version: true + secrets: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc3b293d9..6a58ef8e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,3 +1,6 @@ +# Integration Test Workflow +# References shared workflow from axci + name: Test on: @@ -7,44 +10,8 @@ on: tags-ignore: - '**' pull_request: - workflow_call: + workflow_dispatch: jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - test: - name: Test - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - # - name: Run tests - # run: cargo test --target ${{ matrix.target }} --all-features -- --nocapture - - # - name: Run doc tests - # run: cargo test --target ${{ matrix.target }} --doc - - name: Run tests - run: echo "Tests are skipped!" + uses: arceos-hypervisor/axci/.github/workflows/test.yml@main diff --git a/.gitignore b/.gitignore index ff78c42af..22d7b02fd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,11 @@ /.vscode .DS_Store Cargo.lock + +# Test results (generated by shared test framework) +/test-results/ +/test_repos/ +*.log + +# Downloaded test framework +/scripts/.axci/ diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..8e03d91ab --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,10 @@ +[toolchain] +channel = "nightly-2025-05-20" +components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] +profile = "minimal" +targets = [ + "aarch64-unknown-none-softfloat", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-none", + "riscv64gc-unknown-none-elf" +] diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 000000000..d8bb79e11 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# axdevice_base 代码检查脚本 +# 下载并调用 axci 仓库中的检查脚本 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +COMPONENT_NAME="$(basename "$COMPONENT_DIR")" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行检查 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/check.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 000000000..8875c72f1 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# +# axdevice_base 测试脚本 +# 下载并调用 axci 仓库中的测试框架 +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +COMPONENT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +AXCI_DIR="${SCRIPT_DIR}/.axci" +AXCI_REPO="https://github.com/arceos-hypervisor/axci.git" + +# 下载或更新 axci 仓库 +download_axci() { + if [ -d "$AXCI_DIR" ]; then + echo "Updating axci repository..." + cd "$AXCI_DIR" && git pull --quiet + else + echo "Downloading axci repository..." + git clone --quiet "$AXCI_REPO" "$AXCI_DIR" + fi +} + +# 主函数 +main() { + download_axci + + # 在组件目录中运行测试,自动指定当前组件 + cd "$COMPONENT_DIR" + exec bash "$AXCI_DIR/tests.sh" --component-dir "$COMPONENT_DIR" "$@" +} + +main "$@" diff --git a/src/lib.rs b/src/lib.rs index 6b12a3348..c4bd6a87e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -333,6 +333,3 @@ pub trait BaseSysRegDeviceOps = BaseDeviceOps; /// /// Port I/O devices are only used on x86/x86_64 architectures. pub trait BasePortDeviceOps = BaseDeviceOps; - -#[cfg(test)] -mod test; diff --git a/src/test.rs b/tests/test.rs similarity index 96% rename from src/test.rs rename to tests/test.rs index 0c568d918..54e31f76d 100644 --- a/src/test.rs +++ b/tests/test.rs @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate alloc; + use alloc::vec; use alloc::{sync::Arc, vec::Vec}; use axaddrspace::{GuestPhysAddr, GuestPhysAddrRange, device::AccessWidth}; +use axdevice_base::{BaseDeviceOps, EmuDeviceType, map_device_of_type}; use axerrno::AxResult; -use crate::{BaseDeviceOps, EmuDeviceType, map_device_of_type}; - const DEVICE_A_TEST_METHOD_ANSWER: usize = 42; struct DeviceA; From 9f106ccd8f7ff7d52fe7cdb3eefe04dfc8901adb Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 20 Mar 2026 20:56:12 +0800 Subject: [PATCH 120/132] update axvisor components to main --- scripts/repo/repos.csv | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/repo/repos.csv b/scripts/repo/repos.csv index a93f32de9..f4079e3ba 100644 --- a/scripts/repo/repos.csv +++ b/scripts/repo/repos.csv @@ -4,21 +4,21 @@ https://github.com/arceos-hypervisor/axvisor,,os/axvisor,OS,axvisor - ArceOS Hyp https://github.com/Starry-OS/StarryOS,,os/StarryOS,OS,StarryOS - 教学操作系统 https://github.com/arceos-hypervisor/aarch64_sysreg,,components/aarch64_sysreg,Hypervisor, https://github.com/arceos-hypervisor/axaddrspace,,components/axaddrspace,Hypervisor, -https://github.com/arceos-hypervisor/axdevice,v0.2.1,components/axdevice,Hypervisor, -https://github.com/arceos-hypervisor/axdevice_base,v0.2.1,components/axdevice_base,Hypervisor, +https://github.com/arceos-hypervisor/axdevice,,components/axdevice,Hypervisor, +https://github.com/arceos-hypervisor/axdevice_base,,components/axdevice_base,Hypervisor, https://github.com/arceos-hypervisor/axhvc,,components/axhvc,Hypervisor, https://github.com/arceos-hypervisor/axklib,,components/axklib,Hypervisor, -https://github.com/arceos-hypervisor/axvcpu,v0.2.2,components/axvcpu,Hypervisor, -https://github.com/arceos-hypervisor/axvisor_api,v0.1.0,components/axvisor_api,Hypervisor, -https://github.com/arceos-hypervisor/axvm,v0.2.3,components/axvm,Hypervisor, +https://github.com/arceos-hypervisor/axvcpu,,components/axvcpu,Hypervisor, +https://github.com/arceos-hypervisor/axvisor_api,,components/axvisor_api,Hypervisor, +https://github.com/arceos-hypervisor/axvm,,components/axvm,Hypervisor, https://github.com/arceos-hypervisor/axvmconfig,,components/axvmconfig,Hypervisor, https://github.com/arceos-hypervisor/range-alloc,,components/range-alloc-arceos,Hypervisor, -https://github.com/arceos-hypervisor/x86_vcpu,v0.2.2,components/x86_vcpu,Hypervisor, -https://github.com/arceos-hypervisor/arm_vcpu,v0.2.2,components/arm_vcpu,Hypervisor, -https://github.com/arceos-hypervisor/arm_vgic,v0.2.1,components/arm_vgic,Hypervisor, -https://github.com/arceos-hypervisor/riscv_vcpu,v0.2.2,components/riscv_vcpu,Hypervisor, +https://github.com/arceos-hypervisor/x86_vcpu,,components/x86_vcpu,Hypervisor, +https://github.com/arceos-hypervisor/arm_vcpu,,components/arm_vcpu,Hypervisor, +https://github.com/arceos-hypervisor/arm_vgic,,components/arm_vgic,Hypervisor, +https://github.com/arceos-hypervisor/riscv_vcpu,,components/riscv_vcpu,Hypervisor, https://github.com/arceos-hypervisor/riscv-h,,components/riscv-h,Hypervisor, -https://github.com/arceos-hypervisor/riscv_vplic,v0.2.1,components/riscv_vplic,Hypervisor, +https://github.com/arceos-hypervisor/riscv_vplic,,components/riscv_vplic,Hypervisor, https://github.com/arceos-org/allocator,,components/axallocator,ArceOS, https://github.com/arceos-org/axconfig-gen,,components/axconfig-gen,ArceOS, https://github.com/arceos-org/axcpu,dev,components/axcpu,ArceOS, From b64bb4fdce65c193073295bf5883ea72cca176a4 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 20 Mar 2026 21:16:45 +0800 Subject: [PATCH 121/132] update toolchain version for axaddrspace --- components/axaddrspace/rust-toolchain.toml | 2 +- components/axaddrspace/tests/test_utils/mod.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/components/axaddrspace/rust-toolchain.toml b/components/axaddrspace/rust-toolchain.toml index 923b76f06..76d75c606 100644 --- a/components/axaddrspace/rust-toolchain.toml +++ b/components/axaddrspace/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-05-20" +channel = "nightly-2026-02-25" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] profile = "minimal" targets = [ diff --git a/components/axaddrspace/tests/test_utils/mod.rs b/components/axaddrspace/tests/test_utils/mod.rs index 68fe8aabd..bd926e969 100644 --- a/components/axaddrspace/tests/test_utils/mod.rs +++ b/components/axaddrspace/tests/test_utils/mod.rs @@ -20,8 +20,6 @@ use memory_addr::{PAGE_SIZE_4K as PAGE_SIZE, PhysAddr, VirtAddr}; use page_table_multiarch::PagingHandler; use spin::Mutex; -use crate::{AxMmHal, HostPhysAddr, HostVirtAddr}; - /// The starting physical address for the simulated memory region in tests. /// This offset is used to map simulated physical addresses to the `MEMORY` array's virtual address space. pub const BASE_PADDR: usize = 0x1000; From 645b82aa8408ca7715ae0d1d0b8aa67bf887c287 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 20 Mar 2026 21:43:46 +0800 Subject: [PATCH 122/132] update toolchain version for axvisor crates --- components/arm_vcpu/rust-toolchain.toml | 2 +- components/arm_vgic/rust-toolchain.toml | 2 +- components/axdevice/rust-toolchain.toml | 2 +- components/axdevice_base/rust-toolchain.toml | 2 +- components/axvisor_api/rust-toolchain.toml | 2 +- components/axvm/rust-toolchain.toml | 2 +- components/riscv_vcpu/rust-toolchain.toml | 2 +- components/x86_vcpu/rust-toolchain.toml | 2 +- os/axvisor/rust-toolchain.toml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/components/arm_vcpu/rust-toolchain.toml b/components/arm_vcpu/rust-toolchain.toml index 28393965a..3286bc46f 100644 --- a/components/arm_vcpu/rust-toolchain.toml +++ b/components/arm_vcpu/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] profile = "minimal" -channel = "nightly-2025-05-20" +channel = "nightly-2026-02-25" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] targets = [ "aarch64-unknown-none-softfloat", diff --git a/components/arm_vgic/rust-toolchain.toml b/components/arm_vgic/rust-toolchain.toml index d4da7a649..98a140ac9 100644 --- a/components/arm_vgic/rust-toolchain.toml +++ b/components/arm_vgic/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-05-20" +channel = "nightly-2026-02-25" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] profile = "minimal" targets = [ diff --git a/components/axdevice/rust-toolchain.toml b/components/axdevice/rust-toolchain.toml index 8e03d91ab..d71709d74 100644 --- a/components/axdevice/rust-toolchain.toml +++ b/components/axdevice/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-05-20" +channel = "nightly-2026-02-25" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] profile = "minimal" targets = [ diff --git a/components/axdevice_base/rust-toolchain.toml b/components/axdevice_base/rust-toolchain.toml index 8e03d91ab..d71709d74 100644 --- a/components/axdevice_base/rust-toolchain.toml +++ b/components/axdevice_base/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-05-20" +channel = "nightly-2026-02-25" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] profile = "minimal" targets = [ diff --git a/components/axvisor_api/rust-toolchain.toml b/components/axvisor_api/rust-toolchain.toml index d3d6b8265..4a8cf3803 100644 --- a/components/axvisor_api/rust-toolchain.toml +++ b/components/axvisor_api/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] profile = "minimal" -channel = "nightly-2025-05-20" +channel = "nightly-2026-02-25" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] targets = [ "x86_64-unknown-none", diff --git a/components/axvm/rust-toolchain.toml b/components/axvm/rust-toolchain.toml index d3d6b8265..4a8cf3803 100644 --- a/components/axvm/rust-toolchain.toml +++ b/components/axvm/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] profile = "minimal" -channel = "nightly-2025-05-20" +channel = "nightly-2026-02-25" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] targets = [ "x86_64-unknown-none", diff --git a/components/riscv_vcpu/rust-toolchain.toml b/components/riscv_vcpu/rust-toolchain.toml index 06e0e5b0f..f43861500 100644 --- a/components/riscv_vcpu/rust-toolchain.toml +++ b/components/riscv_vcpu/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-05-20" +channel = "nightly-2026-02-25" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] profile = "minimal" targets = [ diff --git a/components/x86_vcpu/rust-toolchain.toml b/components/x86_vcpu/rust-toolchain.toml index 899aca3bd..542c57474 100644 --- a/components/x86_vcpu/rust-toolchain.toml +++ b/components/x86_vcpu/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] profile = "minimal" -channel = "nightly-2025-05-20" +channel = "nightly-2026-02-25" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] targets = [ "x86_64-unknown-none", diff --git a/os/axvisor/rust-toolchain.toml b/os/axvisor/rust-toolchain.toml index 4d58874be..00f862502 100644 --- a/os/axvisor/rust-toolchain.toml +++ b/os/axvisor/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] profile = "minimal" -channel = "nightly-2025-12-12" +channel = "nightly-2026-02-25" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] targets = ["x86_64-unknown-none", "riscv64gc-unknown-none-elf", "aarch64-unknown-none-softfloat"] From 16badc9e321fd662f32a42e277f079cf607af35f Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 20 Mar 2026 21:52:29 +0800 Subject: [PATCH 123/132] add x86_vlapic --- scripts/repo/repos.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/repo/repos.csv b/scripts/repo/repos.csv index f4079e3ba..29f87e941 100644 --- a/scripts/repo/repos.csv +++ b/scripts/repo/repos.csv @@ -14,6 +14,7 @@ https://github.com/arceos-hypervisor/axvm,,components/axvm,Hypervisor, https://github.com/arceos-hypervisor/axvmconfig,,components/axvmconfig,Hypervisor, https://github.com/arceos-hypervisor/range-alloc,,components/range-alloc-arceos,Hypervisor, https://github.com/arceos-hypervisor/x86_vcpu,,components/x86_vcpu,Hypervisor, +https://github.com/arceos-hypervisor/x86_vlapic.git,,components/x86_vlapic,Hypervisor, https://github.com/arceos-hypervisor/arm_vcpu,,components/arm_vcpu,Hypervisor, https://github.com/arceos-hypervisor/arm_vgic,,components/arm_vgic,Hypervisor, https://github.com/arceos-hypervisor/riscv_vcpu,,components/riscv_vcpu,Hypervisor, From e8d2e4a49ea05b70a716744be415ad9f21093771 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 20 Mar 2026 21:56:48 +0800 Subject: [PATCH 124/132] update root Cargo.toml --- Cargo.toml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 464dded91..ac5fe926f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -229,6 +229,26 @@ scope-local = { path = "components/scope-local", version = "0.1.0" } axbuild = { path = "scripts/axbuild", version = "0.3.0-preview.3" } +# === axvisor === +axvm = { path = "components/axvm", version = "0.3" } +axvmconfig = { path = "components/axvmconfig", version = "0.3" } +axvcpu = { path = "components/axvcpu", version = "0.3" } +x86_vcpu = { path = "components/x86_vcpu", version = "0.3" } +x86_vlapic = { path = "components/x86_vlapic", version = "0.2.2" } +arm_vcpu = { path = "components/arm_vcpu", version = "0.3" } +arm_vgic = { path = "components/arm_vgic", version = "0.2.2" } +aarch64_sysreg = { path = "components/aarch64_sysreg", version = "0.1.1" } +riscv_vcpu = { path = "components/riscv_vcpu", version = "0.3" } +riscv_vplic = { path = "components/riscv_vplic", version = "0.2.2" } +riscv-h = { path = "components/riscv-h", version = "0.2" } +axaddrspace = { path = "components/axaddrspace", version = "0.3" } +axdevice = { path = "components/axdevice", version = "0.2.2" } +axdevice_base = { path = "components/axdevice_base", version = "0.2.2" } +axvisor_api = { path = "components/axvisor_api", version = "0.3" } +axhvc = { path = "components/axhvc", version = "0.2.0" } +axklib = { path = "components/axklib", version = "0.3.0" } +range-alloc-arceos = { path = "components/range-alloc-arceos", version = "0.1.4" } + [workspace.dependencies] # === os/arceos crates (0.3.0-preview.3) === # ulib @@ -307,7 +327,7 @@ bindgen = "0.72" buddy-slab-allocator = "0.2" cfg-if = "1.0" chrono = { version = "0.4", default-features = false } -crate_interface = "0.1" +crate_interface = "0.3" ctor_bare = "0.2" event-listener = { version = "5.4.0", default-features = false } kernel_guard = "0.1" From 4d921367943937da00aaa7e228726c81f5a42f43 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 20 Mar 2026 22:12:14 +0800 Subject: [PATCH 125/132] Remove subtree os/axvisor before force re-add --- os/axvisor/.cargo/config.toml | 16 - os/axvisor/.github/config.json | 10 - os/axvisor/.github/workflows/check.yml | 67 - os/axvisor/.github/workflows/deploy.yml | 116 - .../.github/workflows/qemu-aarch64.toml | 22 - .../.github/workflows/qemu-x86_64-kvm.toml | 24 - os/axvisor/.github/workflows/qemu-x86_64.toml | 21 - os/axvisor/.github/workflows/release.yml | 165 - os/axvisor/.github/workflows/test-board.yml | 64 - os/axvisor/.github/workflows/test-qemu.yml | 122 - os/axvisor/.github/workflows/uboot.toml | 20 - os/axvisor/.gitignore | 68 - os/axvisor/.gitmodules | 0 os/axvisor/.rustfmt.toml | 1 - os/axvisor/Cargo.lock | 6764 -------------- os/axvisor/Cargo.toml | 155 - os/axvisor/LICENSE | 201 - os/axvisor/README.md | 116 - os/axvisor/README_CN.md | 116 - os/axvisor/build.rs | 306 - os/axvisor/configs/board/orangepi-5-plus.dtb | Bin 280066 -> 0 bytes os/axvisor/configs/board/orangepi-5-plus.toml | 13 - os/axvisor/configs/board/phytiumpi.toml | 13 - os/axvisor/configs/board/qemu-aarch64.toml | 17 - os/axvisor/configs/board/qemu-x86_64.toml | 10 - os/axvisor/configs/board/roc-rk3568-pc.toml | 6 - os/axvisor/configs/defconfig.toml | 11 - .../configs/vms/arceos-aarch64-e2000-smp1.dts | 155 - .../vms/arceos-aarch64-e2000-smp1.toml | 61 - .../configs/vms/arceos-aarch64-e2000-smp2.dts | 155 - .../vms/arceos-aarch64-e2000-smp2.toml | 60 - .../configs/vms/arceos-aarch64-qemu-smp1.toml | 75 - .../vms/arceos-aarch64-rk3568-smp1.dts | 87 - .../vms/arceos-aarch64-rk3568-smp1.toml | 63 - .../vms/arceos-aarch64-rk3568-smp2.dts | 101 - .../vms/arceos-aarch64-rk3568-smp2.toml | 63 - .../vms/arceos-aarch64-tac_e400-smp1.dts | 155 - .../vms/arceos-aarch64-tac_e400-smp1.toml | 61 - .../vms/linux-aarch64-a1000-smp8-fada.dts | 3385 ------- .../vms/linux-aarch64-a1000-smp8-fadb.dts | 3378 ------- .../configs/vms/linux-aarch64-a1000-smp8.toml | 64 - .../configs/vms/linux-aarch64-e2000-smp1.dts | 1302 --- .../configs/vms/linux-aarch64-e2000-smp1.toml | 62 - .../configs/vms/linux-aarch64-e2000-smp2.dts | 1302 --- .../configs/vms/linux-aarch64-e2000-smp2.toml | 63 - .../configs/vms/linux-aarch64-qemu-smp1.dts | 397 - .../configs/vms/linux-aarch64-qemu-smp1.toml | 70 - .../configs/vms/linux-aarch64-rk3568-smp1.dts | 6108 ------------- .../vms/linux-aarch64-rk3568-smp1.toml | 62 - .../configs/vms/linux-aarch64-rk3568-smp2.dts | 6108 ------------- .../vms/linux-aarch64-rk3568-smp2.toml | 62 - .../configs/vms/linux-aarch64-rk3588-smp8.dts | 8099 ----------------- .../vms/linux-aarch64-rk3588-smp8.toml | 122 - .../vms/linux-aarch64-tac_e400-smp1.dts | 1302 --- .../vms/linux-aarch64-tac_e400-smp1.toml | 62 - .../configs/vms/nimbos-aarch64-qemu-smp1.toml | 81 - .../configs/vms/nimbos-riscv64-qemu-smp1.toml | 59 - .../configs/vms/nimbos-x86_64-qemu-smp1.toml | 80 - .../vms/rtthread-aarch64-e2000-smp1.toml | 63 - os/axvisor/doc/FDT_Configuration_Guide.md | 274 - os/axvisor/doc/qemu-quickstart.md | 151 - os/axvisor/doc/qemu-quickstart_cn.md | 151 - os/axvisor/doc/shell.md | 1104 --- os/axvisor/doc/task.py-usage.md | 271 - os/axvisor/rust-toolchain.toml | 5 - os/axvisor/scripts/ci_run_qemu_nimbos.py | 94 - os/axvisor/scripts/lds/linker.lds.S | 100 - os/axvisor/scripts/ostool/qemu-aarch64.toml | 21 - os/axvisor/scripts/ostool/qemu-x86_64.toml | 21 - os/axvisor/scripts/quick-start.sh | 904 -- os/axvisor/scripts/setup_qemu.sh | 205 - os/axvisor/src/driver/blk/mod.rs | 104 - os/axvisor/src/driver/blk/phytium.rs | 314 - os/axvisor/src/driver/blk/rockchip.rs | 269 - os/axvisor/src/driver/mod.rs | 32 - os/axvisor/src/driver/serial/mod.rs | 68 - os/axvisor/src/driver/soc/mod.rs | 15 - .../src/driver/soc/rockchip/clk/rk3568-clk.rs | 137 - .../src/driver/soc/rockchip/clk/rk3588-clk.rs | 97 - os/axvisor/src/driver/soc/rockchip/mod.rs | 21 - os/axvisor/src/driver/soc/rockchip/pm.rs | 52 - os/axvisor/src/hal/arch/aarch64/api.rs | 89 - os/axvisor/src/hal/arch/aarch64/cache.rs | 31 - os/axvisor/src/hal/arch/aarch64/mod.rs | 159 - os/axvisor/src/hal/arch/x86_64/cache.rs | 19 - os/axvisor/src/hal/arch/x86_64/mod.rs | 18 - os/axvisor/src/hal/mod.rs | 291 - os/axvisor/src/logo.rs | 51 - os/axvisor/src/main.rs | 56 - os/axvisor/src/shell/command/base.rs | 796 -- os/axvisor/src/shell/command/history.rs | 82 - os/axvisor/src/shell/command/mod.rs | 580 -- os/axvisor/src/shell/command/vm.rs | 1409 --- os/axvisor/src/shell/mod.rs | 239 - os/axvisor/src/task.rs | 57 - os/axvisor/src/vmm/config.rs | 271 - os/axvisor/src/vmm/fdt/create.rs | 483 - os/axvisor/src/vmm/fdt/device.rs | 522 -- os/axvisor/src/vmm/fdt/mod.rs | 137 - os/axvisor/src/vmm/fdt/parser.rs | 411 - os/axvisor/src/vmm/fdt/print.rs | 149 - os/axvisor/src/vmm/fdt/vm_fdt/mod.rs | 14 - os/axvisor/src/vmm/fdt/vm_fdt/writer.rs | 553 -- os/axvisor/src/vmm/hvc.rs | 162 - os/axvisor/src/vmm/images/linux.rs | 162 - os/axvisor/src/vmm/images/mod.rs | 323 - os/axvisor/src/vmm/ivc.rs | 299 - os/axvisor/src/vmm/mod.rs | 155 - os/axvisor/src/vmm/timer.rs | 127 - os/axvisor/src/vmm/vcpus.rs | 599 -- os/axvisor/src/vmm/vm_list.rs | 127 - os/axvisor/xtask/src/cargo.rs | 80 - os/axvisor/xtask/src/clippy.rs | 424 - os/axvisor/xtask/src/ctx.rs | 49 - os/axvisor/xtask/src/devspace.rs | 501 - os/axvisor/xtask/src/image.rs | 318 - os/axvisor/xtask/src/image/config.rs | 159 - os/axvisor/xtask/src/image/download.rs | 134 - os/axvisor/xtask/src/image/registry.rs | 243 - os/axvisor/xtask/src/image/spec.rs | 168 - os/axvisor/xtask/src/image/storage.rs | 426 - os/axvisor/xtask/src/main.rs | 252 - os/axvisor/xtask/src/menuconfig.rs | 55 - os/axvisor/xtask/src/tbuld.rs | 151 - os/axvisor/xtask/src/vmconfig.rs | 29 - 125 files changed, 57481 deletions(-) delete mode 100644 os/axvisor/.cargo/config.toml delete mode 100644 os/axvisor/.github/config.json delete mode 100644 os/axvisor/.github/workflows/check.yml delete mode 100644 os/axvisor/.github/workflows/deploy.yml delete mode 100644 os/axvisor/.github/workflows/qemu-aarch64.toml delete mode 100644 os/axvisor/.github/workflows/qemu-x86_64-kvm.toml delete mode 100644 os/axvisor/.github/workflows/qemu-x86_64.toml delete mode 100644 os/axvisor/.github/workflows/release.yml delete mode 100644 os/axvisor/.github/workflows/test-board.yml delete mode 100644 os/axvisor/.github/workflows/test-qemu.yml delete mode 100644 os/axvisor/.github/workflows/uboot.toml delete mode 100644 os/axvisor/.gitignore delete mode 100644 os/axvisor/.gitmodules delete mode 100644 os/axvisor/.rustfmt.toml delete mode 100644 os/axvisor/Cargo.lock delete mode 100644 os/axvisor/Cargo.toml delete mode 100644 os/axvisor/LICENSE delete mode 100644 os/axvisor/README.md delete mode 100644 os/axvisor/README_CN.md delete mode 100644 os/axvisor/build.rs delete mode 100644 os/axvisor/configs/board/orangepi-5-plus.dtb delete mode 100644 os/axvisor/configs/board/orangepi-5-plus.toml delete mode 100644 os/axvisor/configs/board/phytiumpi.toml delete mode 100644 os/axvisor/configs/board/qemu-aarch64.toml delete mode 100644 os/axvisor/configs/board/qemu-x86_64.toml delete mode 100644 os/axvisor/configs/board/roc-rk3568-pc.toml delete mode 100644 os/axvisor/configs/defconfig.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-e2000-smp1.dts delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-e2000-smp1.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.dts delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.dts delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.dts delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.dts delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fada.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fadb.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-a1000-smp8.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-e2000-smp1.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-e2000-smp1.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-e2000-smp2.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-e2000-smp2.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-qemu-smp1.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.toml delete mode 100644 os/axvisor/configs/vms/nimbos-aarch64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/nimbos-riscv64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/nimbos-x86_64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/rtthread-aarch64-e2000-smp1.toml delete mode 100644 os/axvisor/doc/FDT_Configuration_Guide.md delete mode 100644 os/axvisor/doc/qemu-quickstart.md delete mode 100644 os/axvisor/doc/qemu-quickstart_cn.md delete mode 100644 os/axvisor/doc/shell.md delete mode 100644 os/axvisor/doc/task.py-usage.md delete mode 100644 os/axvisor/rust-toolchain.toml delete mode 100755 os/axvisor/scripts/ci_run_qemu_nimbos.py delete mode 100644 os/axvisor/scripts/lds/linker.lds.S delete mode 100644 os/axvisor/scripts/ostool/qemu-aarch64.toml delete mode 100644 os/axvisor/scripts/ostool/qemu-x86_64.toml delete mode 100755 os/axvisor/scripts/quick-start.sh delete mode 100755 os/axvisor/scripts/setup_qemu.sh delete mode 100644 os/axvisor/src/driver/blk/mod.rs delete mode 100644 os/axvisor/src/driver/blk/phytium.rs delete mode 100644 os/axvisor/src/driver/blk/rockchip.rs delete mode 100644 os/axvisor/src/driver/mod.rs delete mode 100644 os/axvisor/src/driver/serial/mod.rs delete mode 100644 os/axvisor/src/driver/soc/mod.rs delete mode 100644 os/axvisor/src/driver/soc/rockchip/clk/rk3568-clk.rs delete mode 100644 os/axvisor/src/driver/soc/rockchip/clk/rk3588-clk.rs delete mode 100644 os/axvisor/src/driver/soc/rockchip/mod.rs delete mode 100644 os/axvisor/src/driver/soc/rockchip/pm.rs delete mode 100644 os/axvisor/src/hal/arch/aarch64/api.rs delete mode 100644 os/axvisor/src/hal/arch/aarch64/cache.rs delete mode 100644 os/axvisor/src/hal/arch/aarch64/mod.rs delete mode 100644 os/axvisor/src/hal/arch/x86_64/cache.rs delete mode 100644 os/axvisor/src/hal/arch/x86_64/mod.rs delete mode 100644 os/axvisor/src/hal/mod.rs delete mode 100644 os/axvisor/src/logo.rs delete mode 100644 os/axvisor/src/main.rs delete mode 100644 os/axvisor/src/shell/command/base.rs delete mode 100644 os/axvisor/src/shell/command/history.rs delete mode 100644 os/axvisor/src/shell/command/mod.rs delete mode 100644 os/axvisor/src/shell/command/vm.rs delete mode 100644 os/axvisor/src/shell/mod.rs delete mode 100644 os/axvisor/src/task.rs delete mode 100644 os/axvisor/src/vmm/config.rs delete mode 100644 os/axvisor/src/vmm/fdt/create.rs delete mode 100644 os/axvisor/src/vmm/fdt/device.rs delete mode 100644 os/axvisor/src/vmm/fdt/mod.rs delete mode 100644 os/axvisor/src/vmm/fdt/parser.rs delete mode 100644 os/axvisor/src/vmm/fdt/print.rs delete mode 100644 os/axvisor/src/vmm/fdt/vm_fdt/mod.rs delete mode 100644 os/axvisor/src/vmm/fdt/vm_fdt/writer.rs delete mode 100644 os/axvisor/src/vmm/hvc.rs delete mode 100644 os/axvisor/src/vmm/images/linux.rs delete mode 100644 os/axvisor/src/vmm/images/mod.rs delete mode 100644 os/axvisor/src/vmm/ivc.rs delete mode 100644 os/axvisor/src/vmm/mod.rs delete mode 100644 os/axvisor/src/vmm/timer.rs delete mode 100644 os/axvisor/src/vmm/vcpus.rs delete mode 100644 os/axvisor/src/vmm/vm_list.rs delete mode 100644 os/axvisor/xtask/src/cargo.rs delete mode 100644 os/axvisor/xtask/src/clippy.rs delete mode 100644 os/axvisor/xtask/src/ctx.rs delete mode 100644 os/axvisor/xtask/src/devspace.rs delete mode 100644 os/axvisor/xtask/src/image.rs delete mode 100644 os/axvisor/xtask/src/image/config.rs delete mode 100644 os/axvisor/xtask/src/image/download.rs delete mode 100644 os/axvisor/xtask/src/image/registry.rs delete mode 100644 os/axvisor/xtask/src/image/spec.rs delete mode 100644 os/axvisor/xtask/src/image/storage.rs delete mode 100644 os/axvisor/xtask/src/main.rs delete mode 100644 os/axvisor/xtask/src/menuconfig.rs delete mode 100644 os/axvisor/xtask/src/tbuld.rs delete mode 100644 os/axvisor/xtask/src/vmconfig.rs diff --git a/os/axvisor/.cargo/config.toml b/os/axvisor/.cargo/config.toml deleted file mode 100644 index 0da14d5b2..000000000 --- a/os/axvisor/.cargo/config.toml +++ /dev/null @@ -1,16 +0,0 @@ -[net] -# 使用系统 git 拉取依赖,可利用已配置的 git 凭证(如 gh auth、credential helper) -git-fetch-with-cli = true - -[target.'cfg(target_os = "none")'] -runner = "cargo osrun" - -[target.x86_64-unknown-none] -rustflags = [ - "-Clink-args=-no-pie", - "-Clink-args=-znostart-stop-gc", - "-Clink-args=-Tlink.x", -] - -[alias] -xtask = "run --bin xtask --" diff --git a/os/axvisor/.github/config.json b/os/axvisor/.github/config.json deleted file mode 100644 index b4de9a62c..000000000 --- a/os/axvisor/.github/config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "targets": [ - "aarch64-unknown-none-softfloat" - ], - "rust_components": [ - "rust-src", - "clippy", - "rustfmt" - ] -} \ No newline at end of file diff --git a/os/axvisor/.github/workflows/check.yml b/os/axvisor/.github/workflows/check.yml deleted file mode 100644 index 3debf4892..000000000 --- a/os/axvisor/.github/workflows/check.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Quality Checks - -on: - push: - branches: - - '**' - tags-ignore: - - '**' - pull_request: - workflow_call: - -jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - rust_components: ${{ steps.config.outputs.rust_components }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) - - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT - - check: - name: Check - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: ${{ needs.load-config.outputs.rust_components }} - targets: ${{ matrix.target }} - - - name: Check rust version - run: rustc --version --verbose - - - name: Check code format - run: cargo fmt --all -- --check - - # - name: Build - # run: cargo build --target ${{ matrix.target }} --all-features - - - name: Run clippy - # run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings - run: cargo clippy --target ${{ matrix.target }} -- -D warnings - - - name: Build documentation - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features diff --git a/os/axvisor/.github/workflows/deploy.yml b/os/axvisor/.github/workflows/deploy.yml deleted file mode 100644 index c55889c1b..000000000 --- a/os/axvisor/.github/workflows/deploy.yml +++ /dev/null @@ -1,116 +0,0 @@ -name: Deploy - -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+' - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: 'pages' - cancel-in-progress: false - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - -jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_deploy: ${{ steps.check.outputs.should_deploy }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check if tag is on main or master branch - id: check - run: | - git fetch origin main master || true - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Tag is on main or master branch" - echo "should_deploy=true" >> $GITHUB_OUTPUT - else - echo "✗ Tag is not on main or master branch, skipping deployment" - echo "Tag is on: $BRANCHES" - echo "should_deploy=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_deploy == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - - build: - name: Build documentation - runs-on: ubuntu-latest - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Build docs - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: | - # Build documentation - cargo doc --no-deps --all-features - - # Auto-detect documentation directory - # Check if doc exists in target/doc or target/*/doc - if [ -d "target/doc" ]; then - DOC_DIR="target/doc" - else - # Find doc directory under target/*/doc pattern - DOC_DIR=$(find target -type d -name doc -path "target/*/doc" | head -n 1) - if [ -z "$DOC_DIR" ]; then - echo "Error: Could not find documentation directory" - exit 1 - fi - fi - - echo "Documentation found in: $DOC_DIR" - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > "${DOC_DIR}/index.html" - echo "DOC_DIR=${DOC_DIR}" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ${{ env.DOC_DIR }} - - deploy: - name: Deploy to GitHub Pages - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: [verify-tag, build] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/os/axvisor/.github/workflows/qemu-aarch64.toml b/os/axvisor/.github/workflows/qemu-aarch64.toml deleted file mode 100644 index 4ba97898e..000000000 --- a/os/axvisor/.github/workflows/qemu-aarch64.toml +++ /dev/null @@ -1,22 +0,0 @@ -args = [ - "-nographic", - "-cpu", - "cortex-a72", - "-machine", - "virt,virtualization=on,gic-version=3", - "-smp", - "4", - "-device", - "virtio-blk-device,drive=disk0", - "-append", - "root=/dev/vda rw init=/init", - "-m", - "8g", -] -fail_regex = ["panicked at"] -success_regex = [ - "Hello, world!", - "test pass!", -] -to_bin = true -uefi = false diff --git a/os/axvisor/.github/workflows/qemu-x86_64-kvm.toml b/os/axvisor/.github/workflows/qemu-x86_64-kvm.toml deleted file mode 100644 index 7171f958b..000000000 --- a/os/axvisor/.github/workflows/qemu-x86_64-kvm.toml +++ /dev/null @@ -1,24 +0,0 @@ -# QEMU x86_64 配置 - KVM 加速(需 VT-x/VMX 支持) -# 用于物理 Linux 机或支持嵌套虚拟化的 VM -# 若使用 TCG(无 KVM),请用 qemu-x86_64.toml -args = [ - "-m", - "128M", - "-smp", - "1", - "-machine", - "q35", - "-device", - "virtio-blk-pci,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-nographic", - "-accel", - "kvm", - "-cpu", - "host", -] -fail_regex = ["System will reboot, press any key to continue ..."] -success_regex = ["usertests passed!"] -to_bin = false -uefi = false diff --git a/os/axvisor/.github/workflows/qemu-x86_64.toml b/os/axvisor/.github/workflows/qemu-x86_64.toml deleted file mode 100644 index bf0753753..000000000 --- a/os/axvisor/.github/workflows/qemu-x86_64.toml +++ /dev/null @@ -1,21 +0,0 @@ -args = [ - "-m", - "128M", - "-smp", - "1", - "-machine", - "q35", - "-device", - "virtio-blk-pci,drive=disk0", - # "-drive", - # "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-nographic", - "-accel", - "kvm", - "-cpu", - "host", -] -fail_regex = ["System will reboot, press any key to continue ..."] -success_regex = ["usertests passed!"] -to_bin = false -uefi = false \ No newline at end of file diff --git a/os/axvisor/.github/workflows/release.yml b/os/axvisor/.github/workflows/release.yml deleted file mode 100644 index 961ebf795..000000000 --- a/os/axvisor/.github/workflows/release.yml +++ /dev/null @@ -1,165 +0,0 @@ -name: Release - -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+' - - 'v[0-9]+.[0-9]+.[0-9]+-(pre|preview).[0-9]+' - -permissions: - contents: write - -jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - is_prerelease: ${{ steps.check.outputs.is_prerelease }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check tag type and branch - id: check - run: | - git fetch origin main master dev || true - - TAG="${{ github.ref_name }}" - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - echo "Tag: $TAG" - echo "Branches containing this tag: $BRANCHES" - - # Check if it's a prerelease tag - if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-(pre|preview)\.[0-9]+$ ]]; then - echo "📦 Detected prerelease tag" - echo "is_prerelease=true" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -q 'origin/dev'; then - echo "✓ Prerelease tag is on dev branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Prerelease tag must be on dev branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "📦 Detected stable release tag" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Stable release tag is on main or master branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Stable release tag must be on main or master branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - else - echo "✗ Unknown tag format, skipping release" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - echo "should_release=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_release == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' - - test-board: - uses: ./.github/workflows/test-board.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' - - test-qemu: - uses: ./.github/workflows/test-qemu.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' - - release: - name: Create GitHub Release - runs-on: ubuntu-latest - needs: [verify-tag, check, test-board, test-qemu] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate release notes - id: release_notes - run: | - CURRENT_TAG="${{ github.ref_name }}" - - # Get previous tag - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) - - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then - echo "No previous tag found, this is the first release" - CHANGELOG="Initial release" - else - echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" - - # Generate changelog with commit messages - CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") - - if [ -z "$CHANGELOG" ]; then - CHANGELOG="No changes" - fi - fi - - # Write changelog to output file (multi-line) - { - echo "changelog<> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} - body: | - ## Changes - ${{ steps.release_notes.outputs.changelog }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish: - name: Publish to crates.io - runs-on: ubuntu-latest - needs: [verify-tag, check, test-board, test-qemu] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Dry run publish - run: cargo publish --dry-run - - - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/os/axvisor/.github/workflows/test-board.yml b/os/axvisor/.github/workflows/test-board.yml deleted file mode 100644 index 8f3ab7679..000000000 --- a/os/axvisor/.github/workflows/test-board.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Test for BOARD - -on: - push: - branches: - - '**' - tags-ignore: - - '**' - pull_request: - workflow_call: - -jobs: - test-board: - name: "Test board: ${{ matrix.board }} - ${{ matrix.vmconfigs_name }}" - strategy: - matrix: - include: - - board: phytiumpi - vmconfigs: configs/vms/arceos-aarch64-e2000-smp1.toml - vmconfigs_name: ArceOS - - board: phytiumpi - vmconfigs: configs/vms/linux-aarch64-e2000-smp1.toml - vmconfigs_name: Linux - - board: roc-rk3568-pc - vmconfigs: configs/vms/arceos-aarch64-rk3568-smp1.toml - vmconfigs_name: ArceOS - - board: roc-rk3568-pc - vmconfigs: configs/vms/linux-aarch64-rk3568-smp1.toml - vmconfigs_name: Linux - fail-fast: false - runs-on: - - self-hosted - - linux - - ${{ matrix.board }} - - steps: - - name: Clean up workspace - run: | - [ -d .git ] && git submodule deinit -f . || true - [ -d .git/modules ] && rm -rf .git/modules || true - [ -d .git ] && git clean -ffdx || true - - - name: Checkout - uses: actions/checkout@v4 - with: - clean: true - submodules: recursive - - - name: Install dependencies - run: cargo +stable install ostool --version ^0.8 - - - name: Run tests - run: | - echo $BOARD_POWER_RESET - export RUST_LOG=debug - # Use script to provide a PTY so ostool's crossterm can initialize (avoids "reader source not set" panic in CI) - script -q -c "cargo xtask uboot \ - --build-config configs/board/${{ matrix.board }}.toml \ - --uboot-config .github/workflows/uboot.toml \ - --vmconfigs ${{ matrix.vmconfigs }} \ - --bin-dir /home/runner/tftp" /dev/null - # cargo xtask uboot \ - # --build-config configs/board/${{ matrix.board }}.toml \ - # --uboot-config .github/workflows/uboot.toml diff --git a/os/axvisor/.github/workflows/test-qemu.yml b/os/axvisor/.github/workflows/test-qemu.yml deleted file mode 100644 index b445a3303..000000000 --- a/os/axvisor/.github/workflows/test-qemu.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: Test for QEMU - -on: - push: - branches: - - '**' - tags-ignore: - - '**' - pull_request: - workflow_call: - -jobs: - test-qemu: - name: "Test qemu: ${{ matrix.arch }} - ${{ matrix.vmconfigs_name }}" - strategy: - matrix: - include: - - arch: aarch64 - vmconfigs: configs/vms/arceos-aarch64-qemu-smp1.toml - vmconfigs_name: ArceOS - vmimage_name: qemu_aarch64_arceos - - arch: aarch64 - vmconfigs: configs/vms/linux-aarch64-qemu-smp1.toml - vmconfigs_name: Linux - vmimage_name: qemu_aarch64_linux - # - arch: riscv64 - # vmconfigs: configs/vms/arceos-riscv64-qemu-smp1.toml - # vmconfigs_name: ArceOS - # vmimage_name: qemu_arceos_riscv64 - - arch: x86_64 - vmconfigs: configs/vms/nimbos-x86_64-qemu-smp1.toml - vmconfigs_name: NimbOS - vmimage_name: qemu_x86_64_nimbos - fail-fast: false - runs-on: - - self-hosted - - linux - - intel - - steps: - - name: Clean up workspace - run: | - [ -d .git ] && git submodule deinit -f . || true - [ -d .git ] && git clean -ffdx || true - - - name: Checkout - uses: actions/checkout@v4 - with: - clean: true - submodules: recursive - - - name: Install dependencies - run: cargo +stable install ostool --version ^0.8 - - - name: Download images and patch vm configs - run: | - ls -lha . - - IMAGE_DIR="/tmp/.axvisor-images" - IFS=',' read -ra CONFIGS <<< "${{ matrix.vmconfigs }}" - IFS=',' read -ra IMAGES <<< "${{ matrix.vmimage_name }}" - for i in "${!CONFIGS[@]}"; do - img="${IMAGES[$i]}" - img=$(echo "$img" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') - config="${CONFIGS[$i]}" - config=$(echo "$config" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') - cargo xtask image download $img - img_name="qemu-${{ matrix.arch }}" - image_location=$(sed -n 's/^image_location[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' "$config") - - case "$image_location" in - "fs") - echo "Filesystem storage mode - no config update needed" - ;; - "memory") - sed -i 's|^kernel_path[[:space:]]*=.*|kernel_path = "'"${IMAGE_DIR}"'/'"$img"'/'"$img_name"'"|' "$config" - echo "Memory storage mode - updating kernel_path" - # NimbOS x86_64 requires axvm-bios for bootstrapping; ensure it exists in the extracted image - if [[ "$img" == "qemu_x86_64_nimbos" ]]; then - if [ ! -f "${IMAGE_DIR}/${img}/axvm-bios.bin" ]; then - echo "ERROR: axvm-bios.bin not found for NimbOS image at ${IMAGE_DIR}/${img}/axvm-bios.bin" - exit 1 - fi - fi - ;; - *) - echo "Unknown image_location: $image_location" - exit 1 - ;; - esac - - ROOTFS_IMG_PATH="${IMAGE_DIR}/$img/rootfs.img" - - # Check if rootfs.img exists after extraction - if [ -f "${ROOTFS_IMG_PATH}" ]; then - echo "Found rootfs.img, patching qemu-aarch64.toml file..." - sed -i 's|file=${workspaceFolder}/tmp/rootfs.img|file='"${ROOTFS_IMG_PATH}"'|' ".github/workflows/qemu-${{ matrix.arch }}.toml" - echo "Rootfs setup completed" - else - echo "No rootfs.img found, removing rootfs device configuration from qemu-${{ matrix.arch }}.toml..." - sed -i '/-device/,/virtio-blk-device,drive=disk0/d' ".github/workflows/qemu-${{ matrix.arch }}.toml" - sed -i '/-drive/,/id=disk0,if=none,format=raw,file=${workspaceFolder}\/tmp\/rootfs.img/d' ".github/workflows/qemu-${{ matrix.arch }}.toml" - sed -i 's/root=\/dev\/vda rw //' ".github/workflows/qemu-${{ matrix.arch }}.toml" - echo "Rootfs device configuration removed" - fi - done - - - name: Run tests - run: | - export RUST_LOG=debug - if [ "${{ matrix.vmconfigs_name }}" = "NimbOS" ]; then - python3 scripts/ci_run_qemu_nimbos.py -- \ - cargo xtask qemu \ - --build-config configs/board/qemu-${{ matrix.arch }}.toml \ - --qemu-config .github/workflows/qemu-${{ matrix.arch }}.toml \ - --vmconfigs ${{ matrix.vmconfigs }} - else - cargo xtask qemu \ - --build-config configs/board/qemu-${{ matrix.arch }}.toml \ - --qemu-config .github/workflows/qemu-${{ matrix.arch }}.toml \ - --vmconfigs ${{ matrix.vmconfigs }} - fi diff --git a/os/axvisor/.github/workflows/uboot.toml b/os/axvisor/.github/workflows/uboot.toml deleted file mode 100644 index ef36d350f..000000000 --- a/os/axvisor/.github/workflows/uboot.toml +++ /dev/null @@ -1,20 +0,0 @@ -baud_rate = "${env:BOARD_COMM_UART_BAUD}" -board_power_off_cmd = "${env:BOARD_POWER_OFF}" -board_reset_cmd = "${env:BOARD_POWER_RESET}" -dtb_file = "${env:BOARD_DTB}" -fail_regex = [ - "panicked at", -] -serial = "${env:BOARD_COMM_UART_DEV}" -success_regex = [ - "Welcome to AxVisor Shell!", - "All tests passed!", - "Hello World!", - "root@firefly:~#", - "^Phytium Pi", - "Last login: *", -] - -[net] -interface = "${env:BOARD_COMM_NET_IFACE}" -tftp_dir = "${env:TFTP_DIR}" \ No newline at end of file diff --git a/os/axvisor/.gitignore b/os/axvisor/.gitignore deleted file mode 100644 index ed539b02b..000000000 --- a/os/axvisor/.gitignore +++ /dev/null @@ -1,68 +0,0 @@ -# Build output and other log/bin files from arceos -**/target -/.vscode -/tmp -/.arceos -/.images -/.image.toml - -.img -*.bin -*.elf -*.tmp_its -*guest.S -actual.out -qemu.log - -/**/.axconfig.* -/**/.hvconfig.* -/**/.project.* -/**/.board.* - -# dev env -/crates/* -!/crates/nop/ -!/crates/nop/** -.devspace/ - -/Cargo.toml.bk - -# tools should be downloaded from github -tools/* - -# initramfs -*.cpio.gz - -# Python -__pycache__/ -*.py[cod] -*$py.class -*.so -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -*-schema.json -.qemu* -.build* -.uboot* - -# AI tools -.spec-workflow/ -.claude/ - -.idea/ \ No newline at end of file diff --git a/os/axvisor/.gitmodules b/os/axvisor/.gitmodules deleted file mode 100644 index e69de29bb..000000000 diff --git a/os/axvisor/.rustfmt.toml b/os/axvisor/.rustfmt.toml deleted file mode 100644 index 89ab2b7fc..000000000 --- a/os/axvisor/.rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -force_explicit_abi = false \ No newline at end of file diff --git a/os/axvisor/Cargo.lock b/os/axvisor/Cargo.lock deleted file mode 100644 index 6d5015bc5..000000000 --- a/os/axvisor/Cargo.lock +++ /dev/null @@ -1,6764 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aarch64-cpu" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a21cd0131c25c438e19cd6a774adf7e3f64f7f4d723022882facc2dee0f8bc9" -dependencies = [ - "tock-registers 0.9.0", -] - -[[package]] -name = "aarch64-cpu" -version = "11.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44171e22925ec72b63d86747bc3655c7849a5b8d865c980222128839f45ac034" -dependencies = [ - "tock-registers 0.10.1", -] - -[[package]] -name = "aarch64-cpu-ext" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dad5cf7342926ce1c375ec680834e56dd3cdbe8b7adf8a6f99b2854cc52c17" -dependencies = [ - "aarch64-cpu 10.0.0", - "tock-registers 0.10.1", -] - -[[package]] -name = "aarch64_sysreg" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2c929f5025d9b8a0f549b187c4d3a39671f44015ff6ccddd0b134c874b3c1a" - -[[package]] -name = "acpi" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b763acdc1d85c36d61acf97a59938f23202d0e8efe45e8759de10c02db242744" -dependencies = [ - "bit_field", - "bitflags 2.11.0", - "byteorder", - "log", - "pci_types", - "spinning_top 0.3.0", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "getrandom 0.3.4", - "once_cell", - "version_check", - "zerocopy 0.8.42", -] - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "aml" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f8cba7d4260ea05671dda81029f6f718b54402a4ec926a0d9a41bdbb96b415" -dependencies = [ - "bit_field", - "bitvec", - "byteorder", - "log", - "spinning_top 0.2.5", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_rgb" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a730095eb14ee842a0f1e68504b85c8d4a19b1ef2ac2a9b4debf0ed982f9b08a" -dependencies = [ - "rgb", -] - -[[package]] -name = "anstream" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "arceos_api" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0ec20839aa76d3a2eedd79b11e444a83db4c160135f1016ef22b6399a627be" -dependencies = [ - "axalloc", - "axconfig", - "axdriver", - "axerrno 0.2.2", - "axfeat", - "axfs", - "axhal", - "axio", - "axlog", - "axruntime", - "axsync", - "axtask", -] - -[[package]] -name = "arm-gic-driver" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e30c6a0ffd23095c69f48afd996eb51156b2511b52a01bdbb0b418fdfd1d458c" -dependencies = [ - "aarch64-cpu 11.2.0", - "bitflags 2.11.0", - "enum_dispatch", - "log", - "paste", - "tock-registers 0.10.1", -] - -[[package]] -name = "arm-gic-driver" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dda00d35b3c85f4e994746587c4579e63c0ba350b843fca96d6531b609292ae" -dependencies = [ - "aarch64-cpu 11.2.0", - "bitflags 2.11.0", - "enum_dispatch", - "log", - "paste", - "rdif-intc", - "tock-registers 0.10.1", -] - -[[package]] -name = "arm_pl011" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efcf6afca4502993a737ba1e00952d1321078689da92bf7aab27d4e5756c0bec" -dependencies = [ - "tock-registers 0.8.1", -] - -[[package]] -name = "arm_pl031" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13696b1c2b59992f4223e0ae5bb173c81c63039367ca90eee845346ad2a13421" -dependencies = [ - "chrono", -] - -[[package]] -name = "arm_vcpu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8581cf4d84a33f95aa43d39c0a25cabeeaddd65c97a790a0830e37da6e5d871" -dependencies = [ - "aarch64-cpu 10.0.0", - "axaddrspace", - "axdevice_base", - "axerrno 0.1.2", - "axvcpu", - "axvisor_api", - "log", - "numeric-enum-macro", - "percpu", - "spin 0.10.0", -] - -[[package]] -name = "arm_vgic" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e2c4d90852cad20bbe1e5ee6e6d1b05468b98787a8344ffea58537eb54f375" -dependencies = [ - "aarch64-cpu 10.0.0", - "aarch64_sysreg", - "axaddrspace", - "axdevice_base", - "axerrno 0.1.2", - "axvisor_api", - "bitmaps", - "log", - "memory_addr", - "spin 0.9.8", - "tock-registers 0.10.1", -] - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "as-any" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "aws-lc-rs" -version = "1.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - -[[package]] -name = "axaddrspace" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91962ea80ef137c2986b6802d664900e73aa7a014272c13dd9172cc948d5c94e" -dependencies = [ - "axerrno 0.1.2", - "bit_field", - "bitflags 2.11.0", - "cfg-if", - "lazyinit", - "log", - "memory_addr", - "memory_set", - "numeric-enum-macro", - "page_table_entry", - "page_table_multiarch", - "x86", -] - -[[package]] -name = "axalloc" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5bb0695cfda0a651f3b44026899987c51c76063a9a807666f8f6b5e93a67966" -dependencies = [ - "axallocator", - "axerrno 0.2.2", - "cfg-if", - "kspin", - "log", - "memory_addr", - "strum 0.27.2", -] - -[[package]] -name = "axallocator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3894f6027940d4b013f1d1f9e2e61b47a9e4a7dbf1a0ba10dd33e7bb265ea733" -dependencies = [ - "axerrno 0.1.2", - "bitmap-allocator", - "cfg-if", - "rlsf", -] - -[[package]] -name = "axbacktrace" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed127f24c722b06d19c9c1ff73907905bbc2f9463dfd7440652f3f0d6617a93" -dependencies = [ - "cfg-if", - "log", - "spin 0.10.0", -] - -[[package]] -name = "axconfig" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f74d126fc479e4a5acd4389e7ef2c9176c41e7ea3c54e985a7468489b494fb" -dependencies = [ - "axconfig-macros", - "const-str", -] - -[[package]] -name = "axconfig-gen" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8021a26bbd0b7e0760e28ded5dba2082fda8224c7cfd457ab370ff851626452" -dependencies = [ - "clap", - "toml_edit 0.22.27", -] - -[[package]] -name = "axconfig-macros" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2decc5437a10ddb659f0fbd819b9308362ea1d11455ddb5a1c47ea3973920d" -dependencies = [ - "axconfig-gen", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "axcpu" -version = "0.3.0-preview.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361edfc761188b19fb3d906b0b155942a6290068ee88d42f3b1f0ce31dcd099e" -dependencies = [ - "aarch64-cpu 11.2.0", - "axbacktrace", - "cfg-if", - "lazyinit", - "linkme", - "log", - "loongArch64", - "memory_addr", - "page_table_entry", - "page_table_multiarch", - "percpu", - "riscv 0.16.0", - "static_assertions", - "tock-registers 0.10.1", - "x86", - "x86_64", -] - -[[package]] -name = "axdevice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473e2bedf3a04bede7ab6e05909d56f8aed538c56507ce172aaaf0d14dc3be36" -dependencies = [ - "arm_vgic", - "axaddrspace", - "axdevice_base", - "axerrno 0.1.2", - "axvmconfig", - "cfg-if", - "log", - "memory_addr", - "range-alloc-arceos", - "riscv_vplic", - "spin 0.9.8", -] - -[[package]] -name = "axdevice_base" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2707beb1e7bb6557406d77f3968a9ed7ec3d0476691022eafa550c266ec5c9" -dependencies = [ - "axaddrspace", - "axerrno 0.1.2", - "axvmconfig", - "memory_addr", - "serde", -] - -[[package]] -name = "axdriver" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aea5c6b6a48e7283ede40784ded0eedd371e98d59faf97e7d9f4a73dcec4fe00" -dependencies = [ - "axalloc", - "axconfig", - "axdriver_base", - "axdriver_block", - "axdriver_pci", - "axdriver_virtio", - "axerrno 0.2.2", - "axhal", - "axplat-dyn", - "cfg-if", - "crate_interface 0.1.4", - "log", - "smallvec", -] - -[[package]] -name = "axdriver_base" -version = "0.1.4-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4614d9e505dae224e4e4cb934c8fa916d77ecfb7b770a8bf8eac78bdeec3dba" - -[[package]] -name = "axdriver_block" -version = "0.1.4-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cccf23999a9dff620ef87c08c571509d2e90cc9dc80f932381b0fd949f020f9" -dependencies = [ - "axdriver_base", - "log", -] - -[[package]] -name = "axdriver_pci" -version = "0.1.4-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129bd3a5ad486d989e9bdc211c86fd8d50720462ef46e2b0013d333338d50e98" -dependencies = [ - "virtio-drivers", -] - -[[package]] -name = "axdriver_virtio" -version = "0.1.4-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6c36cc900745f3bab9de0dd8d5a2d5ac720a253937b3c5f3ab81bbb9c9e139" -dependencies = [ - "axdriver_base", - "axdriver_block", - "log", - "virtio-drivers", -] - -[[package]] -name = "axerrno" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a88b1fa2ce97a6ff4ce31ba9fda3065730ca4d77a1ba50dec000fc04f1fb686" -dependencies = [ - "axerrno 0.2.2", - "log", -] - -[[package]] -name = "axerrno" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f961d2868582a092fb1e71b90c16cc6f2cbbe7bb5fa7e4bd6fe61d882ce6bb34" -dependencies = [ - "log", - "strum 0.27.2", -] - -[[package]] -name = "axfatfs" -version = "0.1.0-pre.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf1c27753a96a0f835cca49e6fb354912107d018c905d13c7eff39be757eb5a" -dependencies = [ - "bitflags 2.11.0", - "log", -] - -[[package]] -name = "axfeat" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc376f478b85111d8b9ba475040d1016708c35f15e634efbdf8f8495272bb2" -dependencies = [ - "axalloc", - "axbacktrace", - "axconfig", - "axdriver", - "axfs", - "axhal", - "axlog", - "axruntime", - "axsync", - "axtask", - "kspin", -] - -[[package]] -name = "axfs" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b7b9d07fd327e010fd3b9c09ea9ccdfaccf329afd859ff802d2448dfb685ac9" -dependencies = [ - "axdriver", - "axerrno 0.2.2", - "axfatfs", - "axfs_devfs", - "axfs_ramfs", - "axfs_vfs", - "axio", - "cap_access", - "lazyinit", - "log", - "rsext4", - "spin 0.10.0", -] - -[[package]] -name = "axfs_devfs" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b87ae981272ca8d5d8f106a4452c63f4b5ac36e17ee8f848ee1b250538b9f8" -dependencies = [ - "axfs_vfs", - "log", - "spin 0.9.8", -] - -[[package]] -name = "axfs_ramfs" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50c26614485d837a3fc09a92f24a226caddc25a30df7e6aaf4bd19b304c399" -dependencies = [ - "axfs_vfs", - "log", - "spin 0.9.8", -] - -[[package]] -name = "axfs_vfs" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcba2006898d7879d456a9c34b9c9460cb536f5bf69d1d5d7d0e0f19f073368d" -dependencies = [ - "axerrno 0.1.2", - "bitflags 2.11.0", - "log", -] - -[[package]] -name = "axhal" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281f69e09ddb6eb04b7695fc8bdf5a58c760c630443aca8e69abc65ded5642da" -dependencies = [ - "axalloc", - "axconfig", - "axcpu", - "axplat", - "axplat-aarch64-qemu-virt", - "axplat-dyn", - "axplat-loongarch64-qemu-virt", - "axplat-riscv64-qemu-virt", - "axplat-x86-pc", - "cfg-if", - "fdt-parser", - "heapless", - "kernel_guard", - "linkme", - "log", - "memory_addr", - "page_table_multiarch", - "percpu", - "spin 0.10.0", -] - -[[package]] -name = "axhvc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6262e1b84fe6c912c07cc7d9dfa45d8f4511870d79a2886ce6217278bfb8c0dc" -dependencies = [ - "axerrno 0.2.2", -] - -[[package]] -name = "axio" -version = "0.3.0-pre.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ce41624ae4e7ef942ebe3ac3aa3ce5d64340e8f23fb29bbd0007e9765544b4" -dependencies = [ - "autocfg", - "axerrno 0.2.2", - "heapless", - "memchr", -] - -[[package]] -name = "axklib" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03bf328ee0dd583179ce7108584ec69da10e06bd4beb4d6acac7f6cb33754dab" -dependencies = [ - "axerrno 0.2.2", - "memory_addr", - "trait-ffi", -] - -[[package]] -name = "axlog" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b27e79e15984afd375b94328701719b8ab0d6ef4092a7999c8277f9b2f410d" -dependencies = [ - "cfg-if", - "crate_interface 0.1.4", - "kspin", - "log", -] - -[[package]] -name = "axmm" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e9513a2db8f0ad60733581f0ecd77c8c7f7db7b5fcaa1784752504ad924e00" -dependencies = [ - "axalloc", - "axerrno 0.2.2", - "axhal", - "kspin", - "lazyinit", - "log", - "memory_addr", - "memory_set", -] - -[[package]] -name = "axplat" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29d8d929fe41fcb361cf784819041f942aba4bcd2d26b9a26305df84e26b206b" -dependencies = [ - "axplat-macros", - "bitflags 2.11.0", - "const-str", - "crate_interface 0.3.0", - "handler_table", - "kspin", - "memory_addr", - "percpu", -] - -[[package]] -name = "axplat-aarch64-peripherals" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a744097da129e66068e4fff6758726c98bf329a7182dcc65712ac80daed581ed" -dependencies = [ - "aarch64-cpu 11.2.0", - "arm-gic-driver 0.16.4", - "arm_pl011", - "arm_pl031", - "axcpu", - "axplat", - "int_ratio", - "kspin", - "lazyinit", - "log", - "spin 0.10.0", -] - -[[package]] -name = "axplat-aarch64-qemu-virt" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff51219e3ff388368eec683f83b5a8184b3125bac709006f6ac22e435dc07e4" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "axplat-aarch64-peripherals", - "log", - "page_table_entry", -] - -[[package]] -name = "axplat-dyn" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e6762cfd943160d0baa8c2db303adc2b10d1dc3fb754bb8b6b364ac37c7ad2a" -dependencies = [ - "anyhow", - "axalloc", - "axconfig-macros", - "axcpu", - "axdriver_base", - "axdriver_block", - "axdriver_virtio", - "axerrno 0.2.2", - "axklib", - "axplat", - "dma-api 0.7.1", - "fdt-edit", - "heapless", - "log", - "memory_addr", - "percpu", - "rd-block", - "rdrive", - "somehal", - "spin 0.10.0", -] - -[[package]] -name = "axplat-loongarch64-qemu-virt" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb9c67d904ccf30561a6239b67411058b215cb6d6a8d3ac737e7a51230b40bf" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "kspin", - "lazyinit", - "log", - "loongArch64", - "page_table_entry", - "uart_16550", -] - -[[package]] -name = "axplat-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90dfaee06a112fe4f810c60af1a86bc080af2172185b491cacc307b84dff748" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "axplat-riscv64-qemu-virt" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f91aff22afadd24807e34fb94fe4d0d2c8c5b86fb89dd6ff87a8093f812518" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "kspin", - "lazyinit", - "log", - "riscv 0.16.0", - "riscv_plic", - "sbi-rt", - "uart_16550", -] - -[[package]] -name = "axplat-x86-pc" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df26719c444ca8302e9366b8dc5abe8735933ea756ff094e3ac5ce3b64c41a1" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "bitflags 2.11.0", - "heapless", - "int_ratio", - "kspin", - "lazyinit", - "log", - "multiboot", - "percpu", - "raw-cpuid 11.6.0", - "uart_16550", - "x2apic", - "x86", - "x86_64", -] - -[[package]] -name = "axplat-x86-qemu-q35" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "828cbdb755936681e60fefd3d4b16842d67a18e6ccc7661b3b9ef9e6322670cd" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "bitflags 2.11.0", - "heapless", - "int_ratio", - "kspin", - "lazyinit", - "log", - "multiboot", - "percpu", - "raw-cpuid 11.6.0", - "uart_16550", - "x2apic", - "x86", - "x86_64", -] - -[[package]] -name = "axpoll" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b92f85c6903350f5146216ccb7d7a7e7b4dbd6f5927a1279db03ba52a53ae7" -dependencies = [ - "bitflags 2.11.0", - "linux-raw-sys 0.12.1", - "spin 0.10.0", -] - -[[package]] -name = "axruntime" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79659a480ddcacf786119394000822567e47093ab5f01ef1952565cc5bcf256b" -dependencies = [ - "axalloc", - "axbacktrace", - "axconfig", - "axdriver", - "axfs", - "axhal", - "axklib", - "axlog", - "axmm", - "axplat", - "axtask", - "cfg-if", - "chrono", - "crate_interface 0.1.4", - "ctor_bare", - "indoc", - "percpu", -] - -[[package]] -name = "axsched" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad6b7b0b8d9ad1d52a834d8b7721114413da8cf3430af928b1c8651f911287a" -dependencies = [ - "linked_list_r4l", -] - -[[package]] -name = "axstd" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488fb97366c35a4591bd6ac8536e0d17fb32fd31f43f8e15a7fef98ca589293e" -dependencies = [ - "arceos_api", - "axerrno 0.2.2", - "axfeat", - "axio", - "kspin", - "lazyinit", - "lock_api", - "spin 0.10.0", -] - -[[package]] -name = "axsync" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdadc56f8fc6d471c977997b4865d423ebf8b69256f183475af511bb4749d251" -dependencies = [ - "axtask", - "event-listener", - "kspin", - "lock_api", -] - -[[package]] -name = "axtask" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678fe49dac66ef02c5ed0a8657e5fbc74d40b66e29b9864f5e4325741c0ad878" -dependencies = [ - "axconfig", - "axerrno 0.2.2", - "axhal", - "axpoll", - "axsched", - "cfg-if", - "cpumask", - "crate_interface 0.1.4", - "event-listener", - "extern-trait", - "futures-util", - "kernel_guard", - "kspin", - "lazyinit", - "log", - "memory_addr", - "percpu", - "spin 0.10.0", -] - -[[package]] -name = "axum" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" -dependencies = [ - "axum-core", - "bytes", - "form_urlencoded", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "serde_core", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axvcpu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70880a87fffe8087719ad2e7aa09da482db88a65d123dc9170297b7e2a162fb5" -dependencies = [ - "axaddrspace", - "axerrno 0.1.2", - "axvisor_api", - "memory_addr", - "percpu", -] - -[[package]] -name = "axvisor" -version = "0.3.0-preview.2" -dependencies = [ - "aarch64-cpu-ext", - "anyhow", - "arm-gic-driver 0.17.0", - "axaddrspace", - "axconfig", - "axdevice", - "axdevice_base", - "axerrno 0.2.2", - "axhvc", - "axklib", - "axplat-x86-qemu-q35", - "axstd", - "axvcpu", - "axvisor_api", - "axvm", - "axvmconfig", - "bitflags 2.11.0", - "byte-unit", - "cargo_metadata", - "cfg-if", - "chrono", - "clap", - "colored", - "cpumask", - "crate_interface 0.1.4", - "extern-trait", - "fdt-parser", - "flate2", - "hashbrown 0.14.5", - "jkconfig", - "kernel_guard", - "kspin", - "lazy_static", - "lazyinit", - "log", - "memory_addr", - "ostool", - "page_table_entry", - "page_table_multiarch", - "pcie 0.5.0", - "percpu", - "phytium-mci", - "prettyplease", - "quote", - "rd-block", - "rdif-block", - "rdif-clk", - "rdif-intc", - "rdrive", - "regex", - "reqwest 0.13.2", - "rk3568_clk", - "rk3588-clk", - "rockchip-pm", - "schemars", - "sdmmc", - "serde", - "serde_json", - "sha2", - "spin 0.9.8", - "syn 2.0.117", - "tar", - "timer_list", - "tokio", - "toml", -] - -[[package]] -name = "axvisor_api" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7233b2a1338dc06a80e2779b572b4df02007ea128ef7b235b66fc3eeac0ca6" -dependencies = [ - "axaddrspace", - "axvisor_api_proc", - "crate_interface 0.1.4", - "memory_addr", -] - -[[package]] -name = "axvisor_api_proc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a64eb4410ae8357ac8c01c2fb201e57d7aeeb5436ed4d0f5774bfa11cc5902" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "axvm" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c13dc01a73107817fa04f8f2cad019eaee992541713d7064fd2da855502c6d" -dependencies = [ - "arm_vcpu", - "arm_vgic", - "axaddrspace", - "axdevice", - "axdevice_base", - "axerrno 0.2.2", - "axvcpu", - "axvmconfig", - "cfg-if", - "cpumask", - "log", - "memory_addr", - "page_table_entry", - "page_table_multiarch", - "percpu", - "riscv_vcpu", - "spin 0.9.8", - "x86_vcpu", -] - -[[package]] -name = "axvmconfig" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35f2cf8bfcabc84e636f5cf3cd7b1c1ddcf1f584d6fa91791a572056204db35a" -dependencies = [ - "axerrno 0.2.2", - "clap", - "enumerable", - "env_logger", - "log", - "schemars", - "serde", - "serde_repr", - "toml", -] - -[[package]] -name = "bare-metal" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" - -[[package]] -name = "bare-test-macros" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e585a01076fee271c5aabcf36212acb349fb3e638561d842fffa8ca013f4fdd8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bit" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b645c5c09a7d4035949cfce1a915785aaad6f17800c35fda8a8c311c491f284" - -[[package]] -name = "bit_field" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" - -[[package]] -name = "bitmap-allocator" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11e01b72b2a40aab84454fb6b71090c2a6873e1b5ba22a8e3d74e83086a2304" -dependencies = [ - "bit_field", -] - -[[package]] -name = "bitmaps" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "borsh" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" -dependencies = [ - "borsh-derive", - "cfg_aliases", -] - -[[package]] -name = "borsh-derive" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" -dependencies = [ - "once_cell", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "buddy_system_allocator" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b672b945a3e4f4f40bfd4cd5ee07df9e796a42254ce7cd6d2599ad969244c44a" -dependencies = [ - "spin 0.10.0", -] - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "byte-unit" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6d47a4e2961fb8721bcfc54feae6455f2f64e7054f9bc67e875f0e77f4c58d" -dependencies = [ - "rust_decimal", - "schemars", - "serde", - "utf8-width", -] - -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta 0.1.4", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "bytemuck" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "camino" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" -dependencies = [ - "serde_core", -] - -[[package]] -name = "cap_access" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b24894fa5f73bbf9c72196e7f495a1f81d6218a548280a09ada4a937157692" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "cargo-platform" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "cargo_metadata" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror 2.0.18", -] - -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - -[[package]] -name = "cc" -version = "1.2.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "clap" -version = "4.5.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "clap_lex" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" - -[[package]] -name = "cmake" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" -dependencies = [ - "cc", -] - -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "colored" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "compact_str" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" -dependencies = [ - "encode_unicode", - "libc", - "once_cell", - "unicode-width 0.2.0", - "windows-sys 0.61.2", -] - -[[package]] -name = "const-default" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" - -[[package]] -name = "const-str" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18f12cc9948ed9604230cdddc7c86e270f9401ccbe3c2e98a4378c5e7632212f" - -[[package]] -name = "const_fn" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413d67b29ef1021b4d60f4aa1e925ca031751e213832b4b1d588fae623c05c60" - -[[package]] -name = "convert_case" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "cpumask" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4266f1bd910c087ff8c7848882217b2b079742877b21d29614c11c16087d70" -dependencies = [ - "bitmaps", -] - -[[package]] -name = "crate_interface" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70272a03a2cef15589bac05d3d15c023752f5f8f2da8be977d983a9d9e6250fb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "crate_interface" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e5c7109dea31fc91ab584e99752baa997f76d46e49bab0a17b5e9679248df7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "crc" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags 2.11.0", - "crossterm_winapi", - "mio", - "parking_lot", - "rustix 0.38.44", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" -dependencies = [ - "bitflags 2.11.0", - "crossterm_winapi", - "derive_more", - "document-features", - "futures-core", - "mio", - "parking_lot", - "rustix 1.1.4", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ctor_bare" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e5ae3c454dc1efb0e5821dc17344539849391b2de18c89596ea563f1909f93" -dependencies = [ - "ctor_bare_macros", -] - -[[package]] -name = "ctor_bare_macros" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49d5cd78b1c748184d41407b14a58af8403c13328ff2b9f49b0a418c24e3ff" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "cursive" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "386d5a36020bb856e9a34ecb8a4e6c9bd6b0262d1857bae4db7bc7e2fdaa532e" -dependencies = [ - "ahash 0.8.12", - "cfg-if", - "crossbeam-channel", - "crossterm 0.28.1", - "cursive_core", - "lazy_static", - "libc", - "log", - "signal-hook", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "cursive-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac7ac0eb0cede3dfdfebf4d5f22354e05a730b79c25fd03481fc69fcfba0a73e" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "cursive_core" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321ec774d27fafc66e812034d0025f8858bd7d9095304ff8fc200e0b9f9cc257" -dependencies = [ - "ahash 0.8.12", - "compact_str", - "crossbeam-channel", - "cursive-macros", - "enum-map", - "enumset", - "lazy_static", - "log", - "num", - "parking_lot", - "serde_json", - "time", - "unicode-segmentation", - "unicode-width 0.1.14", - "xi-unicode", -] - -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core 0.20.11", - "darling_macro 0.20.11", -] - -[[package]] -name = "darling" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", -] - -[[package]] -name = "darling" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" -dependencies = [ - "darling_core 0.23.0", - "darling_macro 0.23.0", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] - -[[package]] -name = "darling_core" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] - -[[package]] -name = "darling_core" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core 0.20.11", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" -dependencies = [ - "darling_core 0.21.3", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" -dependencies = [ - "darling_core 0.23.0", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case 0.10.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.117", - "unicode-xid", -] - -[[package]] -name = "device_tree" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18f717c5c7c2e3483feb64cccebd077245ad6d19007c2db0fd341d38595353c" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "dma-api" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624f4a84cc1031cfceb56780b82570a785f6cfdcee4f34c06c4e8f1fba25c970" - -[[package]] -name = "dma-api" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d51274fe946b4c27cdada87cc6de09a50c0d9192c0f30ba455b29dae09caa0b" -dependencies = [ - "aarch64-cpu-ext", -] - -[[package]] -name = "dma-api" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3816a852c6c71653e1941cfe22f215eca41c14fd45ec9b32c0fa82fe57fe0989" -dependencies = [ - "aarch64-cpu-ext", - "cfg-if", - "spin 0.10.0", - "thiserror 2.0.18", -] - -[[package]] -name = "dma-api" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e9ce2657334f873c12845789d5a4f1b34bef91fe531437284b8387d77bd925" -dependencies = [ - "aarch64-cpu-ext", - "cfg-if", - "derive_more", - "log", - "mbarrier", - "spin 0.10.0", - "thiserror 2.0.18", -] - -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "embedded-hal" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" - -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enum-map" -version = "2.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" -dependencies = [ - "enum-map-derive", -] - -[[package]] -name = "enum-map-derive" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "enum_dispatch" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "enumerable" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05481ae0c653c7de79f2f62d515b09049fed59cbfc5755a4afe55c95181492b" -dependencies = [ - "enumerable_derive", -] - -[[package]] -name = "enumerable_derive" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef47b28174f7ef4ccb124c8766565006263114c1cc129cd119a66f1e15635125" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "enumn" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "enumset" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" -dependencies = [ - "darling 0.21.3", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "env_filter" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "pin-project-lite", -] - -[[package]] -name = "extern-trait" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3bc9641ace0d852a93f2699f342c935b825146acb63049b92d06e57aa9aebd" -dependencies = [ - "extern-trait-impl", - "typeid", -] - -[[package]] -name = "extern-trait-impl" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89ddc8cd109db4196b4751cce7fc34380d55f095fcd5d634c708485fe4c820f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "fdt-edit" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda8ff88f0dd8770b247b17d9e0faf293c52cb5733e2500294ffa93ac1f0b384" -dependencies = [ - "enum_dispatch", - "fdt-raw", - "log", -] - -[[package]] -name = "fdt-parser" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f95f0bda5ff920492f6573294d8e3a99b75ee2e5ef93ab313fc6d517fa46785" - -[[package]] -name = "fdt-raw" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e6747052012035c3473585be1d706207893d8db73101825d8a3f75effa9843" -dependencies = [ - "heapless", - "log", - "thiserror 2.0.18", -] - -[[package]] -name = "filetime" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" -dependencies = [ - "cfg-if", - "libc", - "libredox", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "fitimage" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e99ae4a85d0135904ef62da3dfb2b4cc90846622a7b8dd5c7a0cd5fea79b10cf" -dependencies = [ - "anyhow", - "byteorder", - "crc", - "device_tree", - "flate2", - "hex", - "md5", - "serde", - "sha1", - "thiserror 2.0.18", - "vm-fdt", -] - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "libz-sys", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "handler_table" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702cb690200d6303c1e1992bc648f3f3bf9c1d6a27fcf50551c513d61f339c99" - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash 0.8.12", - "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "heapless" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", -] - -[[package]] -name = "indicatif" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" -dependencies = [ - "console", - "portable-atomic", - "unicode-width 0.2.0", - "unit-prefix", - "web-time", -] - -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - -[[package]] -name = "instability" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" -dependencies = [ - "darling 0.23.0", - "indoc", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "int_ratio" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6045ea39e8d2862506c0dff6c65d068da362335df698bb1634033492740d2170" - -[[package]] -name = "io-kit-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" -dependencies = [ - "core-foundation-sys", - "mach2", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "iri-string" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" - -[[package]] -name = "jiff" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" -dependencies = [ - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", -] - -[[package]] -name = "jiff-static" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "jkconfig" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8af2ca0c0ea6ee42a80e380d44e2a18a06185484236a8af0cf766682b17176a" -dependencies = [ - "anyhow", - "axum", - "cargo_metadata", - "chrono", - "clap", - "cursive", - "log", - "schemars", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "toml", - "tower", - "tower-http", -] - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "kasm-aarch64" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791dc7a2b079d81b8e3615521fccbd75c0c9f068b53f7d891a2e300222c7cada" -dependencies = [ - "darling 0.20.11", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "kernel_guard" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d10c55bedf6789bc3748e0d8756ee639df1ae25144fd3525ed311044bd9a739f" -dependencies = [ - "cfg-if", - "crate_interface 0.1.4", -] - -[[package]] -name = "kernutil" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baabe1075575d14a32c232dbe55699a2908326f0b2022243ae4a069ff02ccb9a" -dependencies = [ - "heapless", - "num-align", - "ranges-ext", -] - -[[package]] -name = "kspin" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d841fd3aeaa9a55871068f5c3ce48497a6dbcf14e20ca7784a9f68bfdb4c825" -dependencies = [ - "cfg-if", - "kernel_guard", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin 0.9.8", -] - -[[package]] -name = "lazyinit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f03abfebdaaf0fad16790237a0348baf84886d3ade460db13bae59e614a180" - -[[package]] -name = "lenient_semver" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de8de3f4f3754c280ce1c8c42ed8dd26a9c8385c2e5ad4ec5a77e774cea9c1ec" -dependencies = [ - "lenient_semver_parser", - "semver", -] - -[[package]] -name = "lenient_semver_parser" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f650c1d024ddc26b4bb79c3076b30030f2cf2b18292af698c81f7337a64d7d6" -dependencies = [ - "lenient_semver_version_builder", - "semver", -] - -[[package]] -name = "lenient_semver_version_builder" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9049f8ff49f75b946f95557148e70230499c8a642bf2d6528246afc7d0282d17" -dependencies = [ - "semver", -] - -[[package]] -name = "libc" -version = "0.2.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" - -[[package]] -name = "libredox" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" -dependencies = [ - "bitflags 2.11.0", - "libc", - "plain", - "redox_syscall 0.7.3", -] - -[[package]] -name = "libudev" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" -dependencies = [ - "libc", - "libudev-sys", -] - -[[package]] -name = "libudev-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "libz-sys" -version = "1.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52f4c29e2a68ac30c9087e1b772dc9f44a2b66ed44edf2266cf2be9b03dafc1" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked_list_r4l" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1730c4ce817dc3edb092739ca5c109fe551018e5ea5a8361a8ddaa13d79ac8ed" - -[[package]] -name = "linkme" -version = "0.3.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e3283ed2d0e50c06dd8602e0ab319bb048b6325d0bba739db64ed8205179898" -dependencies = [ - "linkme-impl", -] - -[[package]] -name = "linkme-impl" -version = "0.3.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5cec0ec4228b4853bb129c84dbf093a27e6c7a20526da046defc334a1b017f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "loongArch64" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9f0d275c70310e2a9d2fc23250c5ac826a73fa828a5f256401f85c5c554283" -dependencies = [ - "bit_field", - "bitflags 2.11.0", -] - -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "lzma-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" -dependencies = [ - "byteorder", - "crc", -] - -[[package]] -name = "mach2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" -dependencies = [ - "libc", -] - -[[package]] -name = "matchit" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" - -[[package]] -name = "mbarrier" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9257ea6fe9726d1f1a67fddbda4c06cc97b4fb18716b78ec03ba05e29d625e28" - -[[package]] -name = "md5" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memory_addr" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f0625c50adb5f6aaf47f05cae3c4dbc13a74c659241b06c4576f3d7e1da940" - -[[package]] -name = "memory_set" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50a49ecd4114cf87f7e442ec5dd03bd590e7094541f987057310dbb32a6341ad" -dependencies = [ - "axerrno 0.1.2", - "memory_addr", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "mmio-api" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c7c68327571e1807adf7b5bdf7809a281402f879fc641f0e051fb0b960a41e" -dependencies = [ - "derive_more", - "thiserror 2.0.18", -] - -[[package]] -name = "multiboot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87ad3b7b7bcf5da525c22221e3eb3a020cd68b2d55ae62f629c15e8bc3bd56e" -dependencies = [ - "paste", -] - -[[package]] -name = "nb" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" - -[[package]] -name = "network-interface" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddcb8865ad3d9950f22f42ffa0ef0aecbfbf191867b3122413602b0a360b2a6" -dependencies = [ - "cc", - "libc", - "thiserror 2.0.18", - "winapi", -] - -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", -] - -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-align" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4b86e8ef968de2261141fc760ee57cae8fabb3a0e756b3390a4c4871b16c3d1" - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - -[[package]] -name = "numeric-enum-macro" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e4bdb6b46b592948e700ea1ef24a4296491f6a0ee722b258040abd15a3714" - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "flate2", - "memchr", - "ruzstd", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "ostool" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "906b3258004531f252829818fd568c1e2d7345463c2c7a550d0953d071988a5b" -dependencies = [ - "anyhow", - "byte-unit", - "cargo_metadata", - "clap", - "colored", - "crossterm 0.29.0", - "cursive", - "env_logger", - "fitimage", - "futures", - "indicatif", - "jkconfig", - "log", - "lzma-rs", - "network-interface", - "object", - "ratatui", - "regex", - "reqwest 0.12.28", - "schemars", - "serde", - "serde_json", - "serialport", - "sha2", - "tar", - "tftpd", - "tokio", - "toml", - "uboot-shell", - "ureq", -] - -[[package]] -name = "page-table-generic" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062719187d8cadefaabc0c07c12bd486f8e88760fc02b756da3053f42dff0b4d" -dependencies = [ - "bitflags 2.11.0", - "heapless", - "log", - "num-align", - "thiserror 2.0.18", -] - -[[package]] -name = "page_table_entry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9a63b9b86d32f64c3874a90936939281d045ef1751d0aca3d82d5e4e06b2ef" -dependencies = [ - "aarch64-cpu 11.2.0", - "bitflags 2.11.0", - "memory_addr", - "x86_64", -] - -[[package]] -name = "page_table_multiarch" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42c5b75d5d9bdbee44c827b0dd2766fa3d478a76b9c6735419228089d1b24536" -dependencies = [ - "arrayvec", - "axerrno 0.1.2", - "log", - "memory_addr", - "page_table_entry", - "riscv 0.16.0", - "x86", -] - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.18", - "smallvec", - "windows-link", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pci_types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c2a105c657261a938ff68ee231c199a3d80eef33976004829de761ef5b1a9b" -dependencies = [ - "bit_field", - "bitflags 2.11.0", -] - -[[package]] -name = "pcie" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98b1339d53c73a04833791fa907b60ff074b95cae8db9f94f3173ed2b30b5b6c" -dependencies = [ - "bare-test-macros", - "bit_field", - "bitflags 2.11.0", - "log", - "pci_types", - "rdif-pcie", - "thiserror 2.0.18", -] - -[[package]] -name = "pcie" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b092a434cb4a9dff1321bc486f0d324ce2425f217eb1fe43d34b1d21b03294" -dependencies = [ - "bit_field", - "bitflags 2.11.0", - "log", - "mmio-api", - "pci_types", - "rdif-pcie", - "thiserror 2.0.18", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "percpu" -version = "0.2.3-preview.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c03ecfaf97c11a791d8b65e34a6353d012a735a5cbfebba34ee6668da16ce38" -dependencies = [ - "cfg-if", - "percpu_macros", - "spin 0.10.0", - "x86", -] - -[[package]] -name = "percpu_macros" -version = "0.2.3-preview.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6660d83b91174e6d39fae0cdf893889dcdbffda6e99664f8ee8a45fde6a6936c" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "phytium-mci" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca870f654bd7302211a92507bd8301f66f83d1e5c97236de79dab7a6d41fcbf1" -dependencies = [ - "bare-test-macros", - "bitflags 2.11.0", - "bytemuck", - "dma-api 0.2.2", - "lazy_static", - "log", - "nb", - "rlsf", - "spin 0.10.0", - "tock-registers 0.9.0", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - -[[package]] -name = "portable-atomic" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - -[[package]] -name = "portable-atomic-util" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy 0.8.42", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit 0.25.4+spec-1.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive 0.1.4", -] - -[[package]] -name = "ptr_meta" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79" -dependencies = [ - "ptr_meta_derive 0.3.1", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "range-alloc-arceos" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187083342c080f6150a1e8e6e6a687b49239df195aaf866ff4ed535961ab860e" - -[[package]] -name = "ranges-ext" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dbbaa030b3eec58088755e56d3acb775fc74f4726565eda8633a415bfc44e0" -dependencies = [ - "heapless", - "thiserror 2.0.18", -] - -[[package]] -name = "ratatui" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" -dependencies = [ - "bitflags 2.11.0", - "cassowary", - "compact_str", - "crossterm 0.28.1", - "indoc", - "instability", - "itertools", - "lru", - "paste", - "strum 0.26.3", - "unicode-segmentation", - "unicode-truncate", - "unicode-width 0.2.0", -] - -[[package]] -name = "raw-cpuid" -version = "10.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "raw-cpuid" -version = "11.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "rd-block" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c20952f27dc5aaf853e8c00026ba7f2633e149ce47f9a58115704b342756307" -dependencies = [ - "dma-api 0.7.1", - "futures", - "rdif-block", - "spin_on", -] - -[[package]] -name = "rdif-base" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f8c32d8cbc18633a412130719b07d31135215d1715ac48fc3888ca835a811ba" -dependencies = [ - "as-any", - "async-trait", - "paste", - "rdif-def", - "thiserror 2.0.18", -] - -[[package]] -name = "rdif-base" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda8c6ace8e0124ec777028cf86a56049f039c5d6f7a19b307a3f564b01ed858" -dependencies = [ - "as-any", - "async-trait", - "paste", - "rdif-def", - "thiserror 2.0.18", -] - -[[package]] -name = "rdif-block" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2a2573c1fb0af5daf596a32b9e10284f1fc018adfd0413e2213f35151ebff1" -dependencies = [ - "dma-api 0.7.1", - "rdif-base 0.8.0", - "thiserror 2.0.18", -] - -[[package]] -name = "rdif-clk" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b043140cf3c809e5904b5205f7b7205283867da44a44a29820916df08abc362" -dependencies = [ - "rdif-base 0.8.0", -] - -[[package]] -name = "rdif-def" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e44a12e44c2f439be06af72a3e03d4e57f6c79211a68058158a02670e63853c" -dependencies = [ - "thiserror 2.0.18", -] - -[[package]] -name = "rdif-intc" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f3b9bf119dea83dcc7e4f0e2487d7635d373f55500bde982defa934dd17e9c" -dependencies = [ - "cfg-if", - "rdif-base 0.8.0", -] - -[[package]] -name = "rdif-pcie" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ab8ecf37d86123500a1d99519424b7dcf2489c0df6b62b56af9f436224259f" -dependencies = [ - "pci_types", - "rdif-base 0.8.0", - "thiserror 2.0.18", -] - -[[package]] -name = "rdif-serial" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6668610226177053d66d9d3e55436cfa44f6428a5b755b9d7593288b738e3f12" -dependencies = [ - "bitflags 2.11.0", - "futures", - "heapless", - "rdif-base 0.7.0", - "spin 0.10.0", - "thiserror 2.0.18", -] - -[[package]] -name = "rdrive" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9bdca8365115779b803244844c4db3c8e2ae3355edbdb5ce6547de540b29dd2" -dependencies = [ - "fdt-edit", - "fdt-raw", - "log", - "mmio-api", - "paste", - "pcie 0.6.0", - "rdif-base 0.8.0", - "rdif-pcie", - "rdrive-macros", - "spin 0.10.0", - "thiserror 2.0.18", -] - -[[package]] -name = "rdrive-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab3105c9af32e901a2adc7d920b39ff8b6ee0f6f0b7dfdeaf18f306ec12606f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "redox_syscall" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", -] - -[[package]] -name = "reqwest" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-core", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "mime", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "rgb" -version = "0.8.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "riscv" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1671c79a01a149fe000af2429ce9ccc8e58cdecda72672355d50e5536b363c" -dependencies = [ - "critical-section", - "embedded-hal", - "paste", - "riscv-macros 0.2.0", - "riscv-pac", -] - -[[package]] -name = "riscv" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9251433e48c39d2133cbaff3ae7809ce6a1ecbc8225ca7da33d96d10cf360582" -dependencies = [ - "critical-section", - "embedded-hal", - "paste", - "riscv-macros 0.4.0", - "riscv-types", -] - -[[package]] -name = "riscv-decode" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b59d645e392e041ad18f5e529ed13242d8405c66bb192f59703ea2137017d0" - -[[package]] -name = "riscv-h" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cffa652689d01c5f7033abe105e69f4d57ac85bf7e17da688bab10e4b9d3a2d8" -dependencies = [ - "bare-metal", - "bit_field", - "bitflags 2.11.0", - "log", - "riscv 0.14.0", -] - -[[package]] -name = "riscv-h" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bf93901d1af87f984c7be7a348cd27571e1f6f29293b8f3b3193f4c40d4d40" -dependencies = [ - "bare-metal", - "bit_field", - "bitflags 2.11.0", - "log", - "riscv 0.14.0", -] - -[[package]] -name = "riscv-macros" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c4aa1ea1af6dcc83a61be12e8189f9b293c3ba5a487778a4cd89fb060fdbbc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "riscv-macros" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d47d1fb716349455b8e5e3ebbf1eff95344dbdf9f782a4e1359d2f16f51e3dce" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "riscv-pac" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" - -[[package]] -name = "riscv-types" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3f2ad9f15a07f4a0e1677124f9120ce7e83ab7e1ca7186af0ca9da529b62e80" - -[[package]] -name = "riscv_plic" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e701d1c6ea06c35a19cb80d213fab87d264798f9bac0aed2730c0e86d297394a" -dependencies = [ - "tock-registers 0.10.1", -] - -[[package]] -name = "riscv_vcpu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6122b26f3d5920206f22b871fd13d79f5b23e570c7860f345d8736528dacc4c" -dependencies = [ - "axaddrspace", - "axerrno 0.1.2", - "axvcpu", - "axvisor_api", - "bit_field", - "bitflags 2.11.0", - "cfg-if", - "crate_interface 0.1.4", - "log", - "memoffset", - "memory_addr", - "page_table_entry", - "riscv 0.14.0", - "riscv-decode", - "riscv-h 0.2.0", - "rustsbi", - "sbi-rt", - "sbi-spec", - "tock-registers 0.9.0", -] - -[[package]] -name = "riscv_vplic" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193b9d354fe0680a7e151b069929ab7e5722519b5623b24f2bf0211236742a5f" -dependencies = [ - "axaddrspace", - "axdevice_base", - "axerrno 0.1.2", - "axvisor_api", - "bitmaps", - "log", - "riscv-h 0.1.0", - "spin 0.9.8", -] - -[[package]] -name = "rk3568_clk" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9786b01d79acb045652d4cb451b049b26a509171d4afe5c19767d06a366585" -dependencies = [ - "aarch64-cpu 10.0.0", - "bare-test-macros", - "fdt-parser", - "kspin", - "log", -] - -[[package]] -name = "rk3588-clk" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df59d8451a4f331a53fbd7c0c349cf4e844ebc051b7081342341a85910d7ac08" -dependencies = [ - "bare-test-macros", - "log", - "tock-registers 0.10.1", -] - -[[package]] -name = "rkyv" -version = "0.7.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta 0.1.4", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "rlsf" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1646a59a9734b8b7a0ac51689388a60fe1625d4b956348e9de07591a1478457a" -dependencies = [ - "cfg-if", - "const-default", - "libc", - "rustversion", - "svgbobdoc", -] - -[[package]] -name = "rockchip-pm" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9e2f942ea3a4bf3443b3abb70cf3c179274fb1ef4894c1426289fef959ed4c" -dependencies = [ - "bare-test-macros", - "dma-api 0.5.2", - "log", - "mbarrier", - "rdif-base 0.7.0", - "tock-registers 0.10.1", -] - -[[package]] -name = "rsext4" -version = "0.1.0-pre.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c9e89b7c4314992d8b2f6147e8637cc7a578a8d0df083e516bde4b23a0b25d" -dependencies = [ - "bitflags 2.11.0", - "lazy_static", - "log", -] - -[[package]] -name = "rust_decimal" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f703d19852dbf87cbc513643fa81428361eb6940f1ac14fd58155d295a3eb0" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand 0.8.5", - "rkyv", - "serde", - "serde_json", -] - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.11.0", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.11.0", - "errno", - "libc", - "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" -dependencies = [ - "aws-lc-rs", - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustsbi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c13763120794ed11d64bac885fb31d384ae385c3287b0697711b97affbf8ab" -dependencies = [ - "rustsbi-macros", - "sbi-rt", - "sbi-spec", -] - -[[package]] -name = "rustsbi-macros" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71347da9582cc6b6f3652c7d2c06516c9555690b3738ecdff7e84297f4e17fc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ruzstd" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ff0cc5e135c8870a775d3320910cd9b564ec036b4dc0b8741629020be63f01" -dependencies = [ - "twox-hash", -] - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "sbi-rt" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbaa69be1eedc61c426e6d489b2260482e928b465360576900d52d496a58bd0" -dependencies = [ - "sbi-spec", -] - -[[package]] -name = "sbi-spec" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e36312fb5ddc10d08ecdc65187402baba4ac34585cb9d1b78522ae2358d890" - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "schemars" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" -dependencies = [ - "dyn-clone", - "ref-cast", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.117", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sdmmc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0dbda75ab3bc25f210eb7ad16f2bd1f877a05444e1aeea083c60221bf76f15" -dependencies = [ - "aarch64-cpu 10.0.0", - "arm_pl011", - "bare-test-macros", - "bitflags 2.11.0", - "cfg-if", - "dma-api 0.3.1", - "fdt-parser", - "kspin", - "log", - "paste", - "smccc", - "spin 0.10.0", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_repr" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_spanned" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serialport" -version = "4.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acaf3f973e8616d7ceac415f53fc60e190b2a686fbcf8d27d0256c741c5007b" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "core-foundation 0.10.1", - "core-foundation-sys", - "io-kit-sys", - "libudev", - "mach2", - "nix", - "scopeguard", - "unescaper", - "winapi", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "simd-adler32" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" - -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "smccc" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c73e0ca8c566478040487791c9f488f86c5aec846ca1ab18484be8a1d8c55cd" -dependencies = [ - "thiserror 2.0.18", -] - -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "some-serial" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ad586f110c679176859fe111f2c3dd176b95bbe731e9b4c3d1c6382cb301c9" -dependencies = [ - "bare-test-macros", - "bitflags 2.11.0", - "dma-api 0.5.2", - "enum_dispatch", - "heapless", - "log", - "mbarrier", - "rdif-serial", - "thiserror 2.0.18", - "tock-registers 0.10.1", - "x86", -] - -[[package]] -name = "someboot" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afbf93ac5c9501d93d1496f58ff089ac3aa02648adedad5777a767bcfd448ad" -dependencies = [ - "aarch64-cpu 11.2.0", - "aarch64-cpu-ext", - "acpi", - "aml", - "ansi_rgb", - "anyhow", - "arrayvec", - "bit_field", - "bitflags 2.11.0", - "buddy_system_allocator", - "byte-unit", - "byteorder", - "derive_more", - "fdt-edit", - "fdt-raw", - "heapless", - "kasm-aarch64", - "kernutil", - "log", - "loongArch64", - "num-align", - "numeric-enum-macro", - "page-table-generic", - "prettyplease", - "quote", - "ranges-ext", - "rgb", - "smccc", - "some-serial", - "somehal-macros", - "spin 0.10.0", - "syn 2.0.117", - "thiserror 2.0.18", - "tock-registers 0.10.1", - "uefi", - "uguid", -] - -[[package]] -name = "somehal" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2e441db2f693b803ee5fdf85b89bcc1830c83ae483406d4f0bb74cf90473a6" -dependencies = [ - "aarch64-cpu 11.2.0", - "anyhow", - "arm-gic-driver 0.17.0", - "kernutil", - "log", - "mmio-api", - "page-table-generic", - "rdif-intc", - "rdrive", - "someboot", - "somehal-macros", - "spin 0.10.0", - "thiserror 2.0.18", - "tock-registers 0.10.1", -] - -[[package]] -name = "somehal-macros" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e26764471ab7fe1c7cafcc1505b61ed59a3e9d55d4aa4a3d42648f0d4638d78" -dependencies = [ - "darling 0.21.3", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spin_on" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076e103ed41b9864aa838287efe5f4e3a7a0362dd00671ae62a212e5e4612da2" -dependencies = [ - "pin-utils", -] - -[[package]] -name = "spinning_top" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spinning_top" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros 0.27.2", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.117", -] - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "svgbobdoc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" -dependencies = [ - "base64 0.13.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-width 0.1.14", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tar" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" -dependencies = [ - "filetime", - "libc", - "xattr", -] - -[[package]] -name = "tftpd" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee6f92408c23beea910b60784f6dc323b488c477bf6fa88aff8083675eda26c" - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "timer_list" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158b52ace9609dd94f4af338f4828ff23b600d9160def8c001f2c73885521936" - -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tock-registers" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" - -[[package]] -name = "tock-registers" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b9e2fdb3a1e862c0661768b7ed25390811df1947a8acbfbefe09b47078d93c4" - -[[package]] -name = "tock-registers" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2d250f87fb3fb6f225c907cf54381509f47b40b74b1d1f12d2dccbc915bdfe" - -[[package]] -name = "tokio" -version = "1.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.9.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" -dependencies = [ - "indexmap", - "serde_core", - "serde_spanned", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" - -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_datetime" -version = "1.0.0+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "toml_datetime 0.6.11", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.25.4+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" -dependencies = [ - "indexmap", - "toml_datetime 1.0.0+spec-1.1.0", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.9+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" -dependencies = [ - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "toml_writer" -version = "1.0.6+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags 2.11.0", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "http-range-header", - "httpdate", - "iri-string", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "tokio", - "tokio-util", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "log", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "trait-ffi" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87d49469ee333631b3130bec28965c47dcf0d4f3a792f8ed425dd036cf84be7" -dependencies = [ - "convert_case 0.8.0", - "lenient_semver", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "twox-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" - -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "uart_16550" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d293f51425981fdb1b766beae254dbb711a17e8c4b549dc69b9b7ee0d478d5" -dependencies = [ - "bitflags 2.11.0", - "rustversion", - "x86", -] - -[[package]] -name = "uboot-shell" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5fe43e54f4e6cf96cd577556bd0cd1726b135a2563e19ca3ed8f98fa163b5" -dependencies = [ - "colored", - "log", -] - -[[package]] -name = "ucs2" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79298e11f316400c57ec268f3c2c29ac3c4d4777687955cd3d4f3a35ce7eba" -dependencies = [ - "bit_field", -] - -[[package]] -name = "uefi" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe9058b73ee2b6559524af9e33199c13b2485ddbf3ad1181b68051cdc50c17" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "log", - "ptr_meta 0.3.1", - "ucs2", - "uefi-macros", - "uefi-raw", - "uguid", -] - -[[package]] -name = "uefi-macros" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4687412b5ac74d245d5bfb1733ede50c31be19bf8a4b6a967a29b451bab49e67" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "uefi-raw" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f64fe59e11af447d12fd60a403c74106eb104309f34b4c6dbce6e927d97da9d" -dependencies = [ - "bitflags 2.11.0", - "uguid", -] - -[[package]] -name = "uguid" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8352f8c05e47892e7eaf13b34abd76a7f4aeaf817b716e88789381927f199c" - -[[package]] -name = "unescaper" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4064ed685c487dbc25bd3f0e9548f2e34bab9d18cefc700f9ec2dba74ba1138e" -dependencies = [ - "thiserror 2.0.18", -] - -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "unit-prefix" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "ureq" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" -dependencies = [ - "base64 0.22.1", - "flate2", - "log", - "percent-encoding", - "rustls", - "rustls-pki-types", - "ureq-proto", - "utf-8", - "webpki-roots", -] - -[[package]] -name = "ureq-proto" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" -dependencies = [ - "base64 0.22.1", - "http", - "httparse", - "log", -] - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "uuid" -version = "1.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "virtio-drivers" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a39747311dabb3d37807037ed1c3c38d39f99198d091b5b79ecd5c8d82f799" -dependencies = [ - "bitflags 2.11.0", - "enumn", - "log", - "zerocopy 0.7.35", -] - -[[package]] -name = "vm-fdt" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e21282841a059bb62627ce8441c491f09603622cd5a21c43bfedc85a2952f23" - -[[package]] -name = "volatile" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" -dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" - -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x2apic" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db5cbcb7faedfa15f90376004ffc0cb42e427623ab56629f0073d275ee8e7043" -dependencies = [ - "bit", - "bitflags 1.3.2", - "paste", - "raw-cpuid 10.7.0", - "x86_64", -] - -[[package]] -name = "x86" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" -dependencies = [ - "bit_field", - "bitflags 1.3.2", - "raw-cpuid 10.7.0", -] - -[[package]] -name = "x86_64" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7841fa0098ceb15c567d93d3fae292c49e10a7662b4936d5f6a9728594555ba" -dependencies = [ - "bit_field", - "bitflags 2.11.0", - "const_fn", - "rustversion", - "volatile", -] - -[[package]] -name = "x86_vcpu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b71adc527803929792ea1af4a2cd7c264c9cd3dd0096b03f9bc959505f73a" -dependencies = [ - "axaddrspace", - "axdevice_base", - "axerrno 0.1.2", - "axvcpu", - "axvisor_api", - "bit_field", - "bitflags 2.11.0", - "cfg-if", - "crate_interface 0.1.4", - "log", - "memory_addr", - "numeric-enum-macro", - "page_table_entry", - "paste", - "raw-cpuid 11.6.0", - "spin 0.9.8", - "x86", - "x86_64", - "x86_vlapic", -] - -[[package]] -name = "x86_vlapic" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809257bd2252fc337f3b494ae9230e4600bfa4e7d3f2478a0dd794e16849040d" -dependencies = [ - "axaddrspace", - "axdevice_base", - "axerrno 0.1.2", - "axvisor_api", - "bit", - "log", - "memory_addr", - "paste", - "tock-registers 0.10.1", -] - -[[package]] -name = "xattr" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" -dependencies = [ - "libc", - "rustix 1.1.4", -] - -[[package]] -name = "xi-unicode" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" - -[[package]] -name = "yoke" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" -dependencies = [ - "zerocopy-derive 0.8.42", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/os/axvisor/Cargo.toml b/os/axvisor/Cargo.toml deleted file mode 100644 index 57e5b1c7b..000000000 --- a/os/axvisor/Cargo.toml +++ /dev/null @@ -1,155 +0,0 @@ -[package] -name = "axvisor" -authors = ["Keyang Hu ", "周睿 "] -edition = "2024" -license = "Apache-2.0" -version = "0.3.0-preview.2" -description = "A lightweight type-1 hypervisor based on ArceOS" -repository = "https://github.com/arceos-hypervisor/axvisor" -homepage = "https://github.com/arceos-hypervisor/axvisor" -documentation = "https://docs.rs/axvisor" -readme = "README.md" -keywords = ["hypervisor", "virtualization", "arceos", "type-1"] -categories = ["os", "no-std", "embedded"] -include = [ - ".cargo/config.toml", - ".github/*/**", - "doc/**", - "src/**", - "build.rs", - "configs/**", - "scripts/**", - "xtask/src/**", - "rust-toolchain.toml", - "README.md", - "LICENSE*", -] - -[profile.release] -lto = true - -[[bin]] -name = "xtask" -path = "xtask/src/main.rs" - -[features] -default = [] -ept-level-4 = ["axaddrspace/4-level-ept"] -fs = ["axstd/fs"] -dyn-plat = ["axstd/plat-dyn"] -# Driver features (from former driver crate) -rk3568-clk = ["dep:rk3568_clk"] -rk3588-clk = ["dep:rk3588-clk"] -sdmmc = ["dep:sdmmc"] -rockchip-pm = ["dep:rockchip-pm"] -phytium-blk = ["dep:phytium-mci"] - -[target.'cfg(not(any(windows, unix)))'.dependencies] -bitflags = "2.9.1" -cfg-if = "1.0" -cpumask = "0.1.0" -kernel_guard = "0.1" -kspin = "0.1" -lazy_static = { version = "1.5", default-features = false, features = [ - "spin_no_std", -] } -lazyinit = "0.2" -log = "0.4" -spin = "0.9" -timer_list = "0.1.0" -hashbrown = "0.14" - -# System dependent modules provided by ArceOS. -axstd = { version = "=0.3.0-preview.3", features = [ - "alloc-level-1", - "paging", - "irq", - "multitask", - "task-ext", - "smp", - "hv", -] } - -# System dependent modules provided by ArceOS-Hypervisor (bare-metal only) -axaddrspace = "0.1.5" -axhvc = "0.2.0" -# axruntime = { version = "=0.3.0-preview.1", features = ["alloc", "irq", "paging", "smp", "multitask"] } -axvcpu = "0.2.2" -axvm = "0.2.1" -axdevice = "0.2.1" -axdevice_base = "0.2.1" -axvisor_api = "0.1.0" - -# System independent crates provided by ArceOS -axerrno = "0.2.2" -axklib = "0.3.0" -# axcpu = { version = "0.3.0-preview.8", features = ["arm-el2"] } -byte-unit = { version = "5", default-features = false, features = ["byte"] } -crate_interface = "0.1.4" -extern-trait = "0.4" -fdt-parser = "0.4" -memory_addr = "0.4.1" -page_table_entry = { version = "0.6", features = ["arm-el2"] } -page_table_multiarch = "0.6" -percpu = { version = "0.2.3-preview.1", features = ["arm-el2"] } - -rdif-intc = "0.14" -rdif-block = "0.7" -rdif-clk = "0.5" -rdrive = "0.20" -rd-block = "0.1" -pcie = "0.5.0" - -# Optional driver dependencies -rk3568_clk = { version = "0.1", optional = true } -sdmmc = { version = "0.1", default-features = false, features = [ - "pio", -], optional = true } -rk3588-clk = { version = "0.1", optional = true } -rockchip-pm = { version = "0.4", optional = true } -phytium-mci = { version = "0.1", default-features = false, features = [ - "pio", -], optional = true } - -[target.'cfg(target_arch = "aarch64")'.dependencies] -aarch64-cpu-ext = "0.1" -arm-gic-driver = { version = "0.17", features = ["rdif"] } - -[target.'cfg(target_arch = "x86_64")'.dependencies] -axplat-x86-qemu-q35 = { version = "0.2.0", default-features = false, features = [ - "reboot-on-system-off", - "irq", - "smp", -] } -axconfig = { version = "0.3.0-preview.3", features = ["plat-dyn"] } - -# xtask dependencies (only used on host platforms) -[target.'cfg(any(windows, unix))'.dependencies] -axbuild = { version = "0.3.0-preview.3" } -anyhow = { version = "1.0" } -cargo_metadata = { version = "0.23" } -chrono = { version = "0.4", features = ["serde"] } -clap = { version = "4.4", features = ["derive"] } -colored = { version = "3" } -flate2 = { version = "1.0" } -jkconfig = { version = "0.1" } -ostool = { version = "0.8.4" } -regex = { version = "1.12" } -reqwest = { version = "0.13" } -schemars = { version = "1", features = ["derive"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1" } -sha2 = { version = "0.10" } -tar = { version = "0.4" } -tokio = { version = "1", features = ["full"] } -toml = { version = "0.9" } - -axvmconfig = { version = "0.2", features = ["std"] } - -[build-dependencies] -axconfig = "0.3.0-preview.3" -prettyplease = "0.2" -quote = "1.0" -syn = "2.0" -toml = "0.9" -anyhow = "1.0" diff --git a/os/axvisor/LICENSE b/os/axvisor/LICENSE deleted file mode 100644 index b1a313f58..000000000 --- a/os/axvisor/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to the Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/os/axvisor/README.md b/os/axvisor/README.md deleted file mode 100644 index 0002161d9..000000000 --- a/os/axvisor/README.md +++ /dev/null @@ -1,116 +0,0 @@ - - -

AxVisor

- -

A unified modular Type I hypervisor

- -
- -[![GitHub stars](https://img.shields.io/github/stars/arceos-hypervisor/axvisor?logo=github)](https://github.com/arceos-hypervisor/axvisor/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/arceos-hypervisor/axvisor?logo=github)](https://github.com/arceos-hypervisor/axvisor/network) -[![license](https://img.shields.io/github/license/arceos-hypervisor/axvisor)](https://github.com/arceos-hypervisor/axvisor/blob/master/LICENSE) - -
- -English | [中文](README_CN.md) - -# Introduction - -AxVisor is a Hypervisor implemented based on the ArceOS kernel. Its goal is to leverage the basic operating system functionalities provided by ArceOS as a foundation to implement a lightweight unified modular Hypervisor. - -- **Unified** means using the same codebase to support three architectures—x86_64, Arm (aarch64), and RISC-V—maximizing the reuse of architecture-agnostic code and simplifying development and maintenance costs. - -- **Modular** means that the Hypervisor's functionalities are decomposed into multiple independently usable components. Each component implements a specific function, and components communicate through standardized interfaces to achieve decoupling and reusability. - -## Architecture - -The software architecture of AxVisor is divided into five layers as shown in the diagram below. Each box represents an independent component, and components communicate with each other through standard interfaces. The complete architecture description can be found in the [documentation](https://arceos-hypervisor.github.io/axvisorbook/docs/overview). - -![Architecture](https://arceos-hypervisor.github.io/doc/assets/arceos-hypervisor-architecture.png) - -## Hardware Platforms - -AxVisor has been verified on multiple hardware platforms, covering extensive support from virtualization environments to actual physical devices. To facilitate rapid user deployment, we provide one-click build scripts for each platform in the [axvisor-guest](https://github.com/arceos-hypervisor/axvisor-guest) repository, which can automatically generate corresponding image files. - -| Platform Name | Architecture Support | Key Features | -|---------------|---------------------|--------------| -| QEMU | ARM64, x86_64 | Virtualization platform, supports multiple architectures, for development and testing | -| Orange Pi 5 Plus | ARM64 | Development board based on Rockchip RK3588, high-performance ARM platform | -| Phytium Pi | ARM64 | Development board based on Phytium E2000Q processor, domestic ARM platform | -| ROC-RK3568-PC | ARM64 | Development board based on Rockchip RK3568, suitable for industrial applications | -| EVM3588 | ARM64 | Evaluation board based on Rockchip RK3588, enterprise-level applications | - -## Guest Systems - -AxVisor supports multiple operating systems as guests, with good compatibility from lightweight microkernels to mature macrokernel systems. To simplify the user deployment process, we provide one-click build scripts for different guest systems in the [axvisor-guest](https://github.com/arceos-hypervisor/axvisor-guest) repository, which can quickly generate adapted guest images. - -| Guest System | System Type | Architecture Support | Feature Description | -|--------------|-------------|---------------------|-------------------| -| [ArceOS](https://github.com/arceos-org/arceos) | Unikernel | ARM64, x86_64, RISC-V | Rust-based componentized operating system, lightweight and high-performance | -| [Starry-OS](https://github.com/Starry-OS) | Macrokernel OS | ARM64, x86_64 | Real-time operating system for embedded scenarios | -| [NimbOS](https://github.com/equation314/nimbos) | RTOS System | ARM64, x86_64, RISC-V | Concise Unix-like system, supports POSIX interface | -| Linux | Macrokernel OS | ARM64, x86_64, RISC-V | Mature and stable general-purpose operating system, rich software ecosystem | - -# Build - -AxVisor is built based on the Rust ecosystem, providing complete project build, configuration management, and debugging support through the extended xtask toolchain, offering developers a unified and efficient development experience. - -## Build Environment - -> **Quick Start**: To quickly run AxVisor on QEMU, see the [QEMU Quickstart Guide](doc/qemu-quickstart.md) for a complete walkthrough from environment setup to running guest OSes. - -First, in a Linux environment, you need to install basic development tool packages such as `libssl-dev gcc libudev-dev pkg-config`. - -Second, AxVisor is written in the Rust programming language, so you need to install the Rust development environment according to the official Rust website instructions, and use the `cargo install cargo-binutils` command to install [cargo-binutils](https://github.com/rust-embedded/cargo-binutils) to use tools like `rust-objcopy` and `rust-objdump`. - -> If necessary, you may also need to install [musl-gcc](http://musl.cc/x86_64-linux-musl-cross.tgz) to build guest applications. - -## Configuration Files - -AxVisor uses a layered configuration system, including hardware platform configuration and guest configuration, both in TOML format. - -### Hardware Platform Configuration - -Hardware platform configuration files are located in the `configs/board/` directory, with each configuration file corresponding to a development board (or QEMU platform architecture) that we have verified. They specify the target architecture, feature sets, driver support, log levels, and build options. - -> The guest configuration item `vm_configs` is not specified by default and needs to be specified in actual use! - -### Guest Configuration - -Guest configuration files are located in the `configs/vms/` directory, defining the runtime parameters of guests, including basic information, kernel configuration, memory regions, and device configuration details. - -The configuration file naming format is `--board_or_cpu-smpx`, where `` is the guest system name (such as `arceos`, `linux`, `nimbos`), `` is the architecture (such as `aarch64`, `x86_64`, `riscv64`), `board_or_cpu` is the hardware development board or CPU name, and `smpx` is the number of CPUs allocated to the guest. - -## Compilation - -AxVisor uses the xtask tool for build management, supporting multiple hardware platforms and configuration options. For a quick build and run of AxVisor, please refer to the [Quick Start](https://arceos-hypervisor.github.io/axvisorbook/docs/category/quickstart) chapter in the configuration documentation. - -1. **Generate Configuration**: Use `cargo xtask defconfig ` to select the target hardware platform configuration from the `configs/board/` directory. This command copies the corresponding board-level configuration to `.build.toml` as the build configuration. - -2. **Modify Configuration**: Use `cargo xtask menuconfig` to launch the interactive configuration interface, where you can adjust the target architecture, feature sets, log levels, and other parameters. - -3. **Execute Build**: Use `cargo xtask build` to compile the project according to the `.build.toml` configuration file, generating the target platform binary file. - -## QEMU Quick Run - -To quickly run AxVisor on QEMU with a guest OS (ArceOS / Linux / NimbOS), see the [QEMU Quickstart Guide](doc/qemu-quickstart.md). - -# Contributing - -Welcome to fork this repository and submit pull requests. The existence and development of this project is thanks to the support of all contributors. - -
- - - -You are also welcome to scan the QR code below to join the discussion group (please send a note with: AxVisor). We look forward to consulting on issues, exchanging experiences, and receiving feedback suggestions. - -![group](https://arceos-hypervisor.github.io/axvisorbook/assets/images/group-c0e9fb6c8a7720a1f7eb55d3f4f40b4c.png) - -# License - -Axvisor is licensed under the Apache License, Version 2.0. See the [LICENSE](./LICENSE) file for details. \ No newline at end of file diff --git a/os/axvisor/README_CN.md b/os/axvisor/README_CN.md deleted file mode 100644 index 43a5d2a1f..000000000 --- a/os/axvisor/README_CN.md +++ /dev/null @@ -1,116 +0,0 @@ - - -

AxVisor

- -

一个统一组件化的 Type I 类型的虚拟机管理程序

- -
- -[![GitHub stars](https://img.shields.io/github/stars/arceos-hypervisor/axvisor?logo=github)](https://github.com/arceos-hypervisor/axvisor/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/arceos-hypervisor/axvisor?logo=github)](https://github.com/arceos-hypervisor/axvisor/network) -[![license](https://img.shields.io/github/license/arceos-hypervisor/axvisor)](https://github.com/arceos-hypervisor/axvisor/blob/master/LICENSE) - -
- -[English](README.md) | 中文 - -# 简介 - -AxVisor 是一个基于 ArceOS 内核实现的 Hypervisor。其目标是利用 ArceOS 提供的基础操作系统功能作为基础,实现一个轻量级统一组件化 Hypervisor。 - -- **统一**是指使用同一套代码同时支持 x86_64、Arm(aarch64) 和 RISC-V 三种架构,以最大化复用架构无关代码,简化代码开发和维护成本。 - -- **组件化**是指 Hypervisor 的功能被分解为多个可独立使用的组件,每个组件实现一个特定的功能,组件之间通过标准接口进行通信,以实现功能的解耦和复用。 - -## 架构 - -AxVisor 的软件架构分为如下图所示的五层,其中,每一个框都是一个独立的组件,组件之间通过标准接口进行通信。完整的架构描述可以在[文档](https://arceos-hypervisor.github.io/axvisorbook/docs/overview)中找到。 - -![Architecture](https://arceos-hypervisor.github.io/doc/assets/arceos-hypervisor-architecture.png) - -## 硬件平台 - -AxVisor 已在多种硬件平台上完成验证,涵盖从虚拟化环境到实际物理设备的广泛支持。为方便用户快速部署,我们在 [axvisor-guest](https://github.com/arceos-hypervisor/axvisor-guest) 仓库中提供了各平台的一键构建脚本,可自动生成对应的镜像文件。 - -| 平台名称 | 架构支持 | 主要特点 | -|---------|---------|---------| -| QEMU | ARM64, x86_64 | 虚拟化平台,支持多架构,用于开发和测试 | -| Orange Pi 5 Plus | ARM64 | 基于 Rockchip RK3588 的开发板,高性能 ARM 平台 | -| 飞腾派 | ARM64 | 基于飞腾 E2000Q 处理器的开发板,国产 ARM 平台 | -| ROC-RK3568-PC | ARM64 | 基于 Rockchip RK3568 的开发板,适合工业应用 | -| EVM3588 | ARM64 | 基于 Rockchip RK3588 的评估板,企业级应用 | - -## 客户机系统 - -AxVisor 支持多种操作系统作为客户机运行,从轻量级微内核到成熟的宏内核系统均有良好兼容性。为简化用户部署流程,我们在 [axvisor-guest](https://github.com/arceos-hypervisor/axvisor-guest) 仓库中提供了针对不同客户机系统的一键构建脚本,可快速生成适配的客户机镜像。 - -| 客户机系统 | 系统类型 | 架构支持 | 特点描述 | -|-----------|---------|---------|---------| -| [ArceOS](https://github.com/arceos-org/arceos) | Unikernel | ARM64, x86_64, RISC-V | 基于Rust的组件化操作系统,轻量级、高性能 | -| [Starry-OS](https://github.com/Starry-OS) | 宏内核操作系统 | ARM64, x86_64 | 面向嵌入式场景的实时操作系统 | -| [NimbOS](https://github.com/equation314/nimbos) | RTOS 系统 | ARM64, x86_64, RISC-V | 简洁的类Unix系统,支持POSIX接口 | -| Linux | 宏内核操作系统 | ARM64, x86_64, RISC-V | 成熟稳定的通用操作系统,丰富的软件生态 | - -# 构建 - -AxVisor 基于 Rust 生态系统构建,通过扩展的 xtask 工具链提供了完整的项目构建、配置管理和调试支持,为开发者提供统一且高效的开发体验。 - -## 构建环境 - -> **快速开始**:如果你只想在 QEMU 上快速跑起来,请直接参见 [QEMU 快速上手指南](doc/qemu-quickstart_cn.md),其中包含了从环境搭建到运行客户机的完整步骤。 - -首先,在 Linux 环境下,需要安装 `libssl-dev gcc libudev-dev pkg-config` 等基本开发工具包。 - -其次,AxVisor 是使用 Rust 编程语言编写的,因此,需要根据 Rust 官方网站的说明安装 Rust 开发环境,并使用 `cargo install cargo-binutils` 命令安装 [cargo-binutils](https://github.com/rust-embedded/cargo-binutils) 以便使用 `rust-objcopy` 和 `rust-objdump` 等工具 - -> 根据需要,可能还要安装 [musl-gcc](http://musl.cc/x86_64-linux-musl-cross.tgz) 来构建客户机应用程序 - -## 配置文件 - -AxVisor 使用分层配置系统,包含硬件平台配置和客户机配置两部分,均采用 TOML 格式。 - -### 硬件平台配置 - -硬件平台配置文件位于 `configs/board/` 目录下,每个配置文件都对应了一个经过我们验证的开发板(或 QEMU 平台架构)。其中指定了目标架构、功能特性、驱动支持、日志级别以及构建选项。 - -> 客户机配置项 `vm_configs` 默认并没有指定,在实际使用时需要进行指定! - -### 客户机配置 - -客户机配置文件位于 `configs/vms/` 目录下,定义了客户机的运行参数,包括基本信息、内核配置、内存区域以及设备配置等详细信息。 - -配置文件命名格式为 `--board_or_cpu-smpx`,其中 `` 是客户机系统名字(如 `arceos`、`linux`、`nimbos`),`` 是架构(如 `aarch64`、`x86_64`、`riscv64`),`board_or_cpu` 是硬件开发板或 CPU 名称,`smpx` 是分配给客户机的 CPU 数量。 - -## 编译 - -AxVisor 使用 xtask 工具进行构建管理,支持多种硬件平台和配置选项。快速构建及运行 AxVisor,请参见配置套文档中的[快速上手](https://arceos-hypervisor.github.io/axvisorbook/docs/category/quickstart)章节。 - -1. **生成配置**:使用 `cargo xtask defconfig ` 选择 `configs/board/` 目录下目标硬件平台配置。此命令会将对应板级配置复制为 `.build.toml` 作为构建配置。 - -2. **修改配置**:使用 `cargo xtask menuconfig` 启动交互式配置界面,可调整目标架构、功能特性、日志级别等参数。 - -3. **执行构建**:使用 `cargo xtask build` 根据 `.build.toml` 配置文件编译项目,生成目标平台的二进制文件。 - -## QEMU 快速运行 - -如需在 QEMU 上快速运行 AxVisor 并启动客户机系统(ArceOS / Linux / NimbOS),请参见 [QEMU 快速上手指南](doc/qemu-quickstart_cn.md)。 - -# 贡献 - -欢迎 fork 本仓库并提交 pull request。这个项目的存在与发展得益于所有贡献者的支持。 - - - - - -您也可以扫描下方二维码加入讨论群(请务必发送备注信息:AxVisor),进行问题咨询、经验交流与反馈建议。 - -![group](https://arceos-hypervisor.github.io/axvisorbook/assets/images/group-c0e9fb6c8a7720a1f7eb55d3f4f40b4c.png) - -# 许可协议 - -Axvisor 采用 Apache License 2.0 开源协议。详见 [LICENSE](./LICENSE) 文件。 \ No newline at end of file diff --git a/os/axvisor/build.rs b/os/axvisor/build.rs deleted file mode 100644 index bd5125d0a..000000000 --- a/os/axvisor/build.rs +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! This build script reads config file paths from the `AXVISOR_VM_CONFIGS` environment variable, -//! reads them, and then outputs them to `$(OUT_DIR)/vm_configs.rs` to be used by -//! `src/vmm/config.rs`. -//! -//! The `AXVISOR_VM_CONFIGS` environment variable should follow the format convention for the `PATH` -//! environment variable on the building platform, i.e., paths are separated by colons (`:`) on -//! Unix-like systems and semicolons (`;`) on Windows. -//! -//! In the generated `vm_configs.rs` file, a function `static_vm_configs` is defined that returns a -//! `Vec<&'static str>` containing the contents of the configuration files. -//! -//! If the `AXVISOR_VM_CONFIGS` environment variable is not set, `static_vm_configs` will call the -//! `default_static_vm_configs` function from `src/vmm/config.rs` to return the default -//! configurations. -//! -//! If the `AXVISOR_VM_CONFIGS` environment variable is set but the configuration files cannot be -//! read, the build script will output a `compile_error!` macro that will cause the build to fail. -//! -//! A function `get_memory_images` is also provided to get every vm image from the configuration -//! files. -//! -//! This build script reruns if the `AXVISOR_VM_CONFIGS` environment variable changes, or if the -//! `build.rs` file changes, or if any of the files in the paths specified by `AXVISOR_VM_CONFIGS` -//! change. -use std::{ - env, - ffi::OsString, - fs, - io::Write, - path::{Path, PathBuf}, -}; - -use anyhow::Context; -use quote::quote; -use toml::Table; - -/// A configuration file that has been read from disk. -struct ConfigFile { - /// The path to the configuration file. - pub path: OsString, - /// The contents of the configuration file. - pub content: String, -} - -/// Gets the paths (colon-separated) from the `AXVISOR_VM_CONFIGS` environment variable. -/// -/// Returns `None` if the environment variable is not set. -fn get_config_paths() -> Option> { - env::var("AXVISOR_VM_CONFIGS") - .ok() - .map(|paths| env::split_paths(&paths).map(OsString::from).collect()) -} - -/// Gets the paths and contents of the configuration files specified by the `AXVISOR_VM_CONFIGS` environment variable. -/// -/// Returns a tuple of the paths and contents of the configuration files if successful, or an error message if not. -fn get_configs() -> Result, String> { - get_config_paths() - .map(|paths| { - paths - .into_iter() - .map(|path| { - let path_buf = PathBuf::from(&path); - let content = fs::read_to_string(&path_buf).map_err(|e| { - format!("Failed to read file {}: {}", path_buf.display(), e) - })?; - Ok(ConfigFile { path, content }) - }) - .collect() - }) - .unwrap_or_else(|| Ok(vec![])) -} - -/// Opens the output file for writing. -/// -/// Returns the file handle. -fn open_output_file() -> fs::File { - let output_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let output_file = output_dir.join("vm_configs.rs"); - - fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(output_file) - .unwrap() -} - -// Convert relative path to absolute path -fn convert_to_absolute(configs_path: impl AsRef, path: &str) -> PathBuf { - let path = Path::new(path); - let configs_path = configs_path.as_ref().parent().unwrap().join(path); - if path.is_relative() { - fs::canonicalize(configs_path).unwrap_or_else(|_| path.to_path_buf()) - } else { - path.to_path_buf() - } -} - -struct MemoryImage { - pub id: usize, - pub kernel: PathBuf, - pub dtb: Option, - pub bios: Option, - pub ramdisk: Option, -} - -fn parse_config_file(config_file: &ConfigFile) -> Option { - let config = config_file - .content - .parse::() - .expect("failed to parse config file"); - - let id = config.get("base")?.as_table()?.get("id")?.as_integer()? as usize; - - let image_location_val = config.get("kernel")?.as_table()?.get("image_location")?; - - let image_location = image_location_val.as_str()?; - - if image_location != "memory" { - return None; - } - - let kernel_path = config.get("kernel")?.as_table()?.get("kernel_path")?; - - let kernel = convert_to_absolute(&config_file.path, kernel_path.as_str().unwrap()); - - let dtb = config - .get("kernel")? - .as_table()? - .get("dtb_path") - .and_then(|v| v.as_str()) - .map(|v| convert_to_absolute(&config_file.path, v)); - - let bios = config - .get("kernel")? - .as_table()? - .get("bios_path") - .and_then(|v| v.as_str()) - .map(|v| convert_to_absolute(&config_file.path, v)); - - let ramdisk = config - .get("kernel")? - .as_table()? - .get("ramdisk_path") - .and_then(|v| v.as_str()) - .map(|v| convert_to_absolute(&config_file.path, v)); - - Some(MemoryImage { - id, - kernel, - dtb, - bios, - ramdisk, - }) -} - -/// Generate function to load guest images from config -/// Toml file must be provided to load from memory. -fn generate_guest_img_loading_functions( - out_file: &mut fs::File, - config_files: Vec, -) -> anyhow::Result<()> { - let mut memory_images = vec![]; - - for config_file in config_files { - if let Some(files) = parse_config_file(&config_file) { - let id = files.id; - let kernel = files - .kernel - .canonicalize() - .with_context(|| format!("Path {} not found", files.kernel.display()))? - .display() - .to_string(); - let dtb = match files.dtb { - Some(v) => { - let s = v.canonicalize().unwrap().display().to_string(); - quote! { Some(include_bytes!(#s)) } - } - None => quote! { None }, - }; - - let bios = match files.bios { - Some(v) => { - let s = v.canonicalize().unwrap().display().to_string(); - quote! { Some(include_bytes!(#s)) } - } - None => quote! { None }, - }; - - let ramdisk = match files.ramdisk { - Some(v) => { - let s = v.canonicalize().unwrap().display().to_string(); - quote! { Some(include_bytes!(#s)) } - } - None => quote! { None }, - }; - - memory_images.push(quote! { - MemoryImage { - id: #id, - kernel: include_bytes!(#kernel), - dtb: #dtb, - bios: #bios, - ramdisk: #ramdisk, - } - }); - } - } - - let output = quote! { - /// One guest image data from memory. - pub struct MemoryImage{ - /// vm id in config file - pub id: usize, - /// kernel image - pub kernel: &'static [u8], - /// dtb image - pub dtb: Option<&'static [u8]>, - /// bios image - pub bios: Option<&'static [u8]>, - /// ramdisk image - pub ramdisk: Option<&'static [u8]>, - } - - /// Get memory images from config file. - pub fn get_memory_images() -> &'static [MemoryImage] { - &[ - #(#memory_images),* - ] - } - }; - let syntax_tree = syn::parse2(output).unwrap(); - let formatted = prettyplease::unparse(&syntax_tree); - out_file.write_all(formatted.as_bytes())?; - - Ok(()) -} - -fn main() -> anyhow::Result<()> { - let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); - - // let platform = env::var("AX_PLATFORM").unwrap_or("".to_string()); - - let platform = if arch == "aarch64" { - "aarch64-generic".to_string() - } else if arch == "x86_64" { - "x86-qemu-q35".to_string() - } else { - "dummy".to_string() - }; - - println!("cargo:rustc-cfg=platform=\"{platform}\""); - - let config_files = get_configs(); - let mut output_file = open_output_file(); - - println!("cargo:rerun-if-env-changed=AXVISOR_VM_CONFIGS"); - println!("cargo:rerun-if-changed=build.rs"); - - writeln!( - output_file, - "pub fn static_vm_configs() -> Vec<&'static str> {{" - )?; - - match config_files { - Ok(config_files) => { - if config_files.is_empty() { - writeln!(output_file, " default_static_vm_configs()")?; - } else { - writeln!(output_file, " vec![")?; - for config_file in &config_files { - writeln!(output_file, " r###\"{}\"###,", config_file.content)?; - println!( - "cargo:rerun-if-changed={}", - PathBuf::from(config_file.path.clone()).display() - ); - } - writeln!(output_file, " ]")?; - } - writeln!(output_file, "}}\n")?; - - // generate "load kernel and dtb images function" - generate_guest_img_loading_functions(&mut output_file, config_files)?; - } - Err(error) => { - writeln!(output_file, " compile_error!(\"{error}\")")?; - writeln!(output_file, "}}\n")?; - } - } - Ok(()) -} diff --git a/os/axvisor/configs/board/orangepi-5-plus.dtb b/os/axvisor/configs/board/orangepi-5-plus.dtb deleted file mode 100644 index 88d5fdcc1e0535d2c4d294bb98b556cf4ea00170..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 280066 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±kyc6|Zt_{~Ud)^)N_yqNq^DDy0zek@m&}rF+ z7Urj06I)viw>M=wyFP_~dj1`A&b8gO-;LJ_dVZ`qf2cXLND%7$lyPayk1unRMsV`T+%&NG z;4A3o+>=-xSIJTu81lCJ?LK?JaQw4aJZl_HEQR?0G8#E9zMq44G2mqbnBfgS!&5oekTQ%%!OypzxUrhH z0-f?^Ng7zADhk#Cx&x3{Rvr5((D zY}^BM(5nx)(g(hKaev0PyJc-Fm`|ub{H1t)J>VXW<3qL;lQ^;vMsDwVZSXDNkSBki zjpnRt!gTb}IRxD5d}A6YGDc>*A8uzGO4N2AM~B+Z>Q6^QhU9h}DKqDT*lYrZ%*-Zz z#x}{n96s7&wh`al44g9GfUaOawz>7R|K*N9?7eIU?&68Ut__D39e2KyHXnF4=)~qW zj+>d`X@ff+$3NZM$;~-;++U}$1P}_m8pU0=6o%-wXQGn^od!fz8GjqYj zT(`LH(|r)^=WV5XCiNBLT4e}*1)PFC*`{&Ld_}p+;+kt7`%zY0f`58m7VqFwwl_|` z2YQKhmtQtzVJ>`k(l6V=jwR}skD)_tV*PR-w9_wo+z6OD?W|u;a&*=&&|P?0zksi_ zVT=`zhpSmQUN65~?eN4ecrp>@oA2c=KDGBTbd)8 zBPgr*B)V@w0NM7jVYhQh1BHrnZ*2^`6&B--hN8jef#0cn=v1C49o?$mtXzRbz8TYT{RRL4rvXmbXfs55|L?1P$%bGZl9 zW~<0RW2Q0w3u(i@ah{OY?$37sR{LTPr7@oT6796_SKZO=Is35J6nyk0H{qQ84g%gy zypF3aY^z}1s-#y6vJ>6XsEbgT|11fnx8aOw#sz$nWNtU44DRW-^=;CgTvt71-e8Ywq1zx z8meBepl$gDjpnA>!j#-XQp3nEu z_V3CqPFI;1FXXs8k!$`3_@wWZYkmYA&b5qpe9o%fnQO*Tot+9cg>NWtg*DH&cb@s6 z)SOUL%>;m^?q`hMDtw7AAywga^87OJ#ie2M!H zIUL~adG}Nm>5t7t(8yfUxzoe811|#ae^-2GF*l4+Q@v?p$=4YyYtV-s+(Z5f=Ci~_ z#=B=cC;z)d-ajkw?j6(2YxHL?xw$&Q|EcV|xOO7HdLEt^^CB)6@C;~vZgy&_Imd-m zE`5QW%C?>-_PUDuO{JTk15MVOZzf^NBb!IROB*Ydv$YTN=pHLmLab7oJKEkYUdwX- zk!DU}4bgQuVZLMRuy9@AIqkkT=ExiC_|y}}uQaB)PO-3W9n7Wkm}?ho*2nzDBiAM# z4|GS&d4cJ1o!?_WXxHXy>3o-Ul1p)CB2_(GbiqQ#AH*Ej`%vFQ z8^+V$R_S+pz|xFsQ`*j>o-02d_hT&M*=(gfU9U`cHdm&an``y+E6uq%^!l+^GMlQ8FfUg=Kzg%( zl!5%tM&DzyW;p|0@~NPWAnnF>^0ftgq@8}Y1@cQf$6*D$a?&9v%tXMvq3}fk{BppI zFYSMmJ10!N-i;$la+Msib(>>xFF_kRT3I(g?O?|H-5FZ)Og|+i9>(%}*~1-f8>(NC ziczWDoZ|W;hrCeRaL#hGJF<@ovD`c;2Y;+wU#VQ3HbE9CdaR76Fy$FUm9_A8?kZSP z>n|G@0RPex;;v$j6`ij28BLpj~k-1Y`W(9hJq=9B z(aOBnKFT!=fYxwh>3dIAAKkYli~)lud5Jnff2lq<`8zNv&%@DwXXGaM!GB%_I{4gl zMk-Ij`3TvPhq>bu+;PGkFUFBN9|yGEottPA+6yJ2&GeiN?!59ZbAG!!r#%0z+;la@ z3JdMU_H=e+y^u@Vsh2I#McOGhD`3e@CQerFhTKF~GdJc?VkqD*e=-IJSc;Rq;t z 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 yU!}PVlDqy; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x00 0x00 0x80000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial1 = "/soc/uart@2800d000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - cluster0 { - - core0 { - cpu = <0x05>; - }; - }; - - // cluster1 { - - // core0 { - // cpu = <0x06>; - // }; - // }; - - // cluster2 { - - // core0 { - // cpu = <0x07>; - // }; - - // core1 { - // cpu = <0x08>; - // }; - // }; - }; - - // cpu@0 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x200>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x07>; - // }; - - // cpu@1 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x201>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x08>; - // }; - - cpu@100 { - device_type = "cpu"; - compatible = "phytium,ftc664\0arm,armv8"; - reg = <0x00 0x00>; - enable-method = "psci"; - clocks = <0x09 0x00>; - capacity-dmips-mhz = <0x161c>; - phandle = <0x05>; - }; - - // cpu@101 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x100>; - // enable-method = "psci"; - // clocks = <0x09 0x01>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x06>; - // }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 root=/dev/mmcblk0p1 rootfstype=ext4 rootwait rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory@00 { - device_type = "memory"; - reg = <0x20 0x20000000 0x00 0x20000000>; - }; -}; diff --git a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp1.toml b/os/axvisor/configs/vms/arceos-aarch64-e2000-smp1.toml deleted file mode 100644 index d8f730225..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp1.toml +++ /dev/null @@ -1,61 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 2 -# Guest vm name. -name = "arceos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x00] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_2008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x20_2008_0000 -## The file path of the kernel image. -kernel_path = "/guest/arceos/phytiumpi" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_2000_0000 -#dtb_path = "/path/to/axvisor/configs/vms/arceos-aarch64-e2000_smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_2000_0000, 0x2000_0000, 0x7, 1], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.dts b/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.dts deleted file mode 100644 index b80c5dad0..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.dts +++ /dev/null @@ -1,155 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000080000000 0x0000000000010000; -/ { - compatible = "phytium,pe2204"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x00 0x00 0x80000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial1 = "/soc/uart@2800d000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - // cluster0 { - - // core0 { - // cpu = <0x05>; - // }; - // }; - - cluster1 { - - core0 { - cpu = <0x06>; - }; - }; - - cluster2 { - - // core0 { - // cpu = <0x07>; - // }; - - core1 { - cpu = <0x08>; - }; - }; - }; - - // cpu@0 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x200>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x07>; - // }; - - cpu@1 { - device_type = "cpu"; - compatible = "phytium,ftc310\0arm,armv8"; - reg = <0x00 0x201>; - enable-method = "psci"; - clocks = <0x09 0x02>; - capacity-dmips-mhz = <0xb22>; - phandle = <0x08>; - }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x00>; - // enable-method = "psci"; - // clocks = <0x09 0x00>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x05>; - // }; - - cpu@101 { - device_type = "cpu"; - compatible = "phytium,ftc664\0arm,armv8"; - reg = <0x00 0x100>; - enable-method = "psci"; - clocks = <0x09 0x01>; - capacity-dmips-mhz = <0x161c>; - phandle = <0x06>; - }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 root=/dev/mmcblk0p1 rootfstype=ext4 rootwait rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory@00 { - device_type = "memory"; - reg = <0x20 0x20000000 0x00 0x20000000>; - }; -}; diff --git a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.toml b/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.toml deleted file mode 100644 index 4f04ffa7a..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.toml +++ /dev/null @@ -1,60 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 2 -# Guest vm name. -name = "arceos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 2 -# The physical CPU ids. -phys_cpu_ids = [0x201, 0x100] -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_2008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x20_2008_0000 -## The file path of the kernel image. -kernel_path = "/guest/arceos/phytiumpi" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_2000_0000 -#dtb_path = "/path/to/axvisor/configs/vms/arceos-aarch64-e2000_smp2.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_2000_0000, 0x2000_0000, 0x7, 1], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/arceos-aarch64-qemu-smp1.toml b/os/axvisor/configs/vms/arceos-aarch64-qemu-smp1.toml deleted file mode 100644 index fdd3beccd..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-qemu-smp1.toml +++ /dev/null @@ -1,75 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "arceos-qemu" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu ids. -phys_cpu_ids = [0] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8020_0000 -# The location of image: "memory" | "fs". -# load from memory. -image_location = "memory" -# The file path of the kernel image. -kernel_path = "path/arceos-aarch64-dyn-smp1.bin" -# The load address of the kernel image. -kernel_load_addr = 0x8020_0000 -# The file path of the device tree blob (DTB). -#dtb_path = "path/aarch64-qemu-gicv3.dtb" -# The load address of the device tree blob (DTB). -dtb_load_addr = 0x8000_0000 - -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "disk.img" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x4000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Pass-through devices. -passthrough_devices = [ - ["/",], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - ["/pcie@10000000"], -] - -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [ - # ["gppt-gicd", 0x0800_0000, 0x1_0000, 0, 0x21, []], - # ["gppt-gicr", 0x080a_0000, 0x2_0000, 0, 0x20, [1, 0x2_0000, 0]], # 1 vcpu, stride 0x20000, starts with pcpu 0 - # ["gppt-gits", 0x0808_0000, 0x2_0000, 0, 0x22, [0x0808_0000]], # host_gits_base -] - -interrupt_mode = "passthrough" - diff --git a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.dts b/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.dts deleted file mode 100644 index d1ad3a39f..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.dts +++ /dev/null @@ -1,87 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000008300000 0x000000000001c000; -/memreserve/ 0x000000000a200000 0x00000000008cf15d; -/ { - serial-number = "425ca8fc29ade692"; - compatible = "rockchip,rk3568-firefly-roc-pc\0rockchip,rk3568"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Firefly RK3568-ROC-PC"; - - memory { - reg = <0x00 0x70000000 0x00 0x10000000>; - device_type = "memory"; - }; - - chosen { - bootargs = "earlycon=uart8250,mmio32,0xfe660000"; - }; - - aliases { - serial2 = "/serial@fe660000"; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu@200 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x200>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - phandle = <0x0d>; - }; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; - arm,no-tick-in-suspend; - }; - - interrupt-controller@fd400000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0xfd400000 0x00 0x10000 0x00 0xfd460000 0x00 0xc0000>; - interrupts = <0x01 0x09 0x04>; - phandle = <0x01>; - - interrupt-controller@fd440000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <0x01>; - reg = <0x00 0xfd440000 0x00 0x20000>; - status = "okay"; - phandle = <0xbe>; - }; - }; - - serial@fe660000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe660000 0x00 0x100>; - interrupts = <0x00 0x76 0x04>; - clocks = <0x23 0x123 0x23 0x120>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x04 0x4e 0x05>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "okay"; - }; -}; \ No newline at end of file diff --git a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.toml b/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.toml deleted file mode 100644 index 432f53771..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.toml +++ /dev/null @@ -1,63 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "arceos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x200] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x7008_0000 -# The location of image: "memory" | "fs". -# Load from memory. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x7008_0000 -## The file path of the kernel image. -kernel_path = "/userdata/rootfs_overlay/guest/arceos/roc-rk3568-pc" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x7000_0000 -#dtb_path = "/path/arceos-rk3568.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x7000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# The interrupt mode. -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] diff --git a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.dts b/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.dts deleted file mode 100644 index 6da5bf8b4..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.dts +++ /dev/null @@ -1,101 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000008300000 0x000000000001c000; -/memreserve/ 0x000000000a200000 0x00000000008cf15d; -/ { - serial-number = "425ca8fc29ade692"; - compatible = "rockchip,rk3568-firefly-roc-pc\0rockchip,rk3568"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Firefly RK3568-ROC-PC"; - - memory { - reg = <0x00 0x70000000 0x00 0x10000000>; - device_type = "memory"; - }; - - chosen { - bootargs = "earlycon=uart8250,mmio32,0xfe660000"; - }; - - aliases { - serial2 = "/serial@fe660000"; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x00>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - #cooling-cells = <0x02>; - dynamic-power-coefficient = <0xbb>; - cpu-supply = <0x05>; - phandle = <0x0c>; - }; - - cpu@100 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x100>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - phandle = <0x0d>; - }; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; - arm,no-tick-in-suspend; - }; - - interrupt-controller@fd400000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0xfd400000 0x00 0x10000 0x00 0xfd460000 0x00 0xc0000>; - interrupts = <0x01 0x09 0x04>; - phandle = <0x01>; - - interrupt-controller@fd440000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <0x01>; - reg = <0x00 0xfd440000 0x00 0x20000>; - status = "okay"; - phandle = <0xbe>; - }; - }; - - serial@fe660000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe660000 0x00 0x100>; - interrupts = <0x00 0x76 0x04>; - clocks = <0x23 0x123 0x23 0x120>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x04 0x4e 0x05>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "okay"; - }; -}; \ No newline at end of file diff --git a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.toml b/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.toml deleted file mode 100644 index 5f2b90020..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.toml +++ /dev/null @@ -1,63 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 2 -# Guest vm name. -name = "arceos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 2 -# The physical CPU ids. -phys_cpu_ids = [0x00, 0x100] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x7008_0000 -# The location of image: "memory" | "fs". -# Load from memory. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x7008_0000 -## The file path of the kernel image. -kernel_path = "/userdata/rootfs_overlay/guest/arceos/roc-rk3568-pc" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x7000_0000 -#dtb_path = "/path/arceos-aarch64-rk3568_smp2.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x7000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# The interrupt mode. -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] diff --git a/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.dts b/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.dts deleted file mode 100644 index 53d9a15f2..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.dts +++ /dev/null @@ -1,155 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000080000000 0x0000000000010000; -/ { - compatible = "phytium,pe2204"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x00 0x00 0x80000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial1 = "/soc/uart@2800d000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - // cluster0 { - - // core0 { - // cpu = <0x05>; - // }; - // }; - - // cluster1 { - - // core0 { - // cpu = <0x06>; - // }; - // }; - - cluster2 { - - core0 { - cpu = <0x07>; - }; - - // core1 { - // cpu = <0x08>; - // }; - }; - }; - - cpu@0 { - device_type = "cpu"; - compatible = "phytium,ftc310\0arm,armv8"; - reg = <0x00 0x200>; - enable-method = "psci"; - clocks = <0x09 0x02>; - capacity-dmips-mhz = <0xb22>; - phandle = <0x07>; - }; - - // cpu@1 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x201>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x08>; - // }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x00>; - // enable-method = "psci"; - // clocks = <0x09 0x00>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x05>; - // }; - - // cpu@101 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x100>; - // enable-method = "psci"; - // clocks = <0x09 0x01>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x06>; - // }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 root=/dev/sda2 rootfstype=ext4 rootwait rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory@00 { - device_type = "memory"; - reg = <0x20 0x20000000 0x00 0x20000000>; - }; -}; diff --git a/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.toml b/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.toml deleted file mode 100644 index 159ea607f..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.toml +++ /dev/null @@ -1,61 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 2 -# Guest vm name. -name = "arceos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x200] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_2008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "memory" -# The load address of the kernel image. -kernel_load_addr = 0x20_2008_0000 -## The file path of the kernel image. -kernel_path = "/path/to/arceos_aarch64-dyn_smp1.bin" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_2000_0000 -#dtb_path = "/path/to/arceos-aarch64-tac_e400-smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_2000_0000, 0x2000_0000, 0x7, 1], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fada.dts b/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fada.dts deleted file mode 100644 index 49cb3d063..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fada.dts +++ /dev/null @@ -1,3385 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000018000000 0x0000000000100000; -/ { - compatible = "bst,a1000b"; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "BST A1000B FAD-A"; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - - cpu@0 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x00>; - cpu-idle-states = <0x04>; - phandle = <0x3a>; - }; - - cpu@1 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x100>; - cpu-idle-states = <0x04>; - phandle = <0x3b>; - }; - - cpu@2 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x200>; - cpu-idle-states = <0x04>; - phandle = <0x3c>; - }; - - cpu@3 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x300>; - cpu-idle-states = <0x04>; - phandle = <0x3d>; - }; - - cpu@4 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x400>; - cpu-idle-states = <0x04>; - phandle = <0x3e>; - }; - - cpu@5 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x500>; - cpu-idle-states = <0x04>; - phandle = <0x3f>; - }; - - cpu@6 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x600>; - cpu-idle-states = <0x04>; - phandle = <0x40>; - }; - - cpu@7 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x700>; - cpu-idle-states = <0x04>; - phandle = <0x41>; - }; - - l2-cache0 { - compatible = "cache"; - phandle = <0x03>; - }; - - }; - - psci { - compatible = "arm,psci"; - method = "hvc"; - cpu_on = <0xc4000003>; - cpu_off = <0x84000002>; - cpu_suspend = <0xc4000001>; - }; - - misc_clk { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x3d0900>; - phandle = <0x02>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupt-parent = <0x01>; - interrupts = <0x01 0x0d 0xff08 0x01 0x0e 0xff08 0x01 0x0b 0xff08 0x01 0x0a 0xff08>; - }; - - amba_apu@0 { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x00 0x00 0xffffffff>; - - a1000bclkc@33002000 { - compatible = "bst,a1000b-clkc"; - osc-clk-frequency = <0x17d7840>; - rgmii0-rxclk-frequency = <0x7735940>; - rgmii1-rxclk-frequency = <0x7735940>; - ptp_clk-frequency = <0x7735940>; - #clock-cells = <0x01>; - reg = <0x00 0x33002000 0x1000 0x00 0x70035000 0x1000 0x00 0x20020000 0x1000 0x00 0x20021000 0x1000>; - phandle = <0x05>; - }; - - a1000rstc@0x3300217c { - compatible = "bst,a1000b-rstc"; - #reset-cells = <0x01>; - reg = <0x00 0x3300217c 0x04 0x00 0x33002180 0x04 0x00 0x70035008 0x04 0x00 0x20020000 0x04 0x00 0x20021000 0x04>; - phandle = <0x06>; - }; - - interrupt-controller@32000000 { - compatible = "arm,gic-400"; - #interrupt-cells = <0x03>; - interrupt-controller; - reg = <0x00 0x32001000 0x1000 0x00 0x32002000 0x2000 0x00 0x32004000 0x2000 0x00 0x32006000 0x2000>; - interrupt-parent = <0x01>; - interrupts = <0x01 0x09 0xff04>; - phandle = <0x01>; - }; - - a1000noc { - compatible = "bst,a1000-noc"; - echo-args = <0x01>; - noc-arg-num = <0x2a>; - noc-args = <0x3342000c 0x00 0x33420008 0x505 0x3342008c 0x00 0x33420088 0x505 0x3342010c 0x00 0x33420108 0x505 0x3342018c 0x00 0x33420188 0x505 0x3342020c 0x00 0x33420208 0x505 0x3342028c 0x00 0x33420288 0x505 0x3342030c 0x00 0x33420308 0x505 0x3342038c 0x00 0x33420388 0x303 0x3342040c 0x00 0x33420408 0x303 0x3342048c 0x00 0x33420488 0x303 0x3342050c 0x00 0x33420508 0x303 0x3342058c 0x00 0x33420588 0x303 0x3342060c 0x00 0x33420608 0x303 0x3342068c 0x00 0x33420688 0x303 0x3342070c 0x00 0x33420708 0x00 0x3342078c 0x00 0x33420788 0x707 0x3342080c 0x00 0x33420808 0x707 0x3342088c 0x00 0x33420888 0x707 0x3342090c 0x00 0x33420908 0x707 0x3340010c 0x00 0x33400108 0x505 0x3340018c 0x00 0x33400188 0x505>; - }; - - serial@20008000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x20008000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd5 0x04>; - clocks = <0x05 0x54 0x05 0x66>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x1d>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x07>; - }; - - serial@2000a000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x2000a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd6 0x04>; - clocks = <0x05 0x55 0x05 0x67>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x17>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x08>; - }; - - serial@2000b000 { - status = "disable"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x2000b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x05 0x70 0x05 0x7d>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x29>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x09>; - }; - - serial@20009000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x20009000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x05 0x6f 0x05 0x7e>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x25>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x0a>; - }; - - dma-controller@33200000 { - status = "okay"; - compatible = "bst,dw-axi-gdma"; - reg = <0x00 0x33200000 0x1000>; - clocks = <0x05 0x2f 0x05 0x30>; - clock-names = "core-clk\0cfgr-clk"; - resets = <0x06 0x0c>; - reset-names = "gdma_reset"; - dma-channels = <0x08>; - snps,dma-masters = <0x01>; - snps,data-width = <0x04>; - snps,block-size = <0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000>; - snps,priority = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07>; - snps,axi-max-burst-len = <0x10>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x90 0xff04 0x00 0x91 0xff04 0x00 0x92 0xff04 0x00 0x93 0xff04 0x00 0x94 0xff04 0x00 0x95 0xff04 0x00 0x96 0xff04 0x00 0x97 0xff04 0x00 0x98 0xff04>; - support-slave; - }; - - pcie-phy@30E02000 { - reg = <0x00 0x30e02000 0x1000>; - reg-names = "phy-base"; - dmc-lane = <0x01>; - dmc-mode = <0x02>; - pcie-ctl0 = <0x01>; - pcie-ctl1 = <0x00>; - phandle = <0x0b>; - }; - - pcie@30600000 { - status = "disable"; - compatible = "bst,dw-pcie-rc"; - device_type = "pci"; - controller-id = <0x00>; - bus-range = <0x00 0xff>; - linux,pci-domain = <0x00>; - reg = <0x00 0x30600000 0x10000 0x00 0x30700000 0x10000 0x00 0x30900000 0x40000 0x00 0x30980000 0x40000 0x00 0x40000000 0x40000>; - reg-names = "dbi\0dbi2\0atu\0dma\0config"; - num-iatu = <0x04>; - num-viewport = <0x04>; - #address-cells = <0x03>; - #size-cells = <0x02>; - ranges = <0x81000000 0x00 0x41000000 0x00 0x41000000 0x00 0x1000000 0x83000000 0x00 0x42000000 0x00 0x42000000 0x00 0x4000000>; - dma-ranges = <0x3000000 0x00 0x80000000 0x00 0x80000000 0x02 0x00>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc5 0x04 0x00 0xbf 0x04 0x00 0xc0 0x04 0x00 0xc1 0x04 0x00 0xc6 0x04>; - interrupt-names = "sys\0dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - pcie-phy = <0x0b>; - max-link-speed = <0x03>; - num-lanes = <0x02>; - picp-ctl = "mem"; - ob-memaddr-def = <0x0c>; - }; - - pcie0_ep@30600000 { - status = "disable"; - compatible = "bst,dw-pcie-ep"; - device_type = "pci"; - controller-id = <0x00>; - bus-range = <0x00 0x04>; - reg = <0x00 0x30600000 0x40000 0x00 0x30700000 0x40000 0x00 0x30900000 0x40000 0x00 0x30980000 0x40000 0x00 0x40000000 0x100000>; - reg-names = "dbi\0dbi2\0atu\0dma\0addr_space"; - num-ib-windows = <0x06>; - num-ob-windows = <0x06>; - max-functions = [04]; - pcie-phy = <0x0b>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc5 0x04 0x00 0xbf 0x04 0x00 0xc0 0x04 0x00 0xc1 0x04 0x00 0xc6 0x04>; - interrupt-names = "sys\0dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - }; - - pcie@30a00000 { - status = "disable"; - compatible = "bst,dw-pcie-ep"; - device_type = "pci"; - controller-id = <0x01>; - bus-range = <0x00 0x04>; - reg = <0x00 0x30a00000 0x40000 0x00 0x30b00000 0x40000 0x00 0x30d00000 0x40000 0x00 0x30d80000 0x40000 0x00 0x48000000 0x1000000>; - reg-names = "dbi\0dbi2\0atu\0dma\0addr_space"; - num-ib-windows = <0x06>; - num-ob-windows = <0x06>; - max-functions = [04]; - pcie-phy = <0x0b>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc2 0x04 0x00 0xc3 0x04 0x00 0xc4 0x04 0x00 0xc7 0x04>; - interrupt-names = "dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - }; - - pcie_vnet@0 { - status = "disable"; - compatible = "bst,pcie-vnet"; - vnet-id = <0x00>; - dma-chan-num = <0x01>; - rx_queues = <0x01>; - tx_queues = <0x01>; - tx-fifo-depth = <0x100>; - rx-fifo-depth = <0x100>; - extend-op = <0x10>; - memory-region = <0x0c>; - }; - - pcie_vnet@1 { - status = "disable"; - compatible = "bst,pcie-vnet"; - vnet-id = <0x01>; - dma-chan-num = <0x01>; - rx_queues = <0x01>; - tx_queues = <0x01>; - tx-fifo-depth = <0x100>; - rx-fifo-depth = <0x100>; - }; - - - - - gpio@20010000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,dw-apb-gpio"; - reg = <0x00 0x20010000 0x1000>; - clocks = <0x05 0x62>; - clock-names = "bus"; - resets = <0x06 0x1c>; - resets-names = "gpio0_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x15 0x16 0x17 0x18 0x19 0x1a 0x1b>; - - gpio-controller@0 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x00>; - chipnum-base = <0x00>; - interrupt-controller; - #interrupt-cells = <0x03>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdf 0x04 0x00 0xe3 0x04 0x00 0xe4 0x04 0x00 0xe5 0x04 0x00 0xe6 0x04 0x00 0xe7 0x04 0x00 0xe8 0x04 0x00 0xe9 0x04 0x00 0xea 0x04>; - phandle = <0x1f>; - }; - - gpio-controller@1 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x01>; - chipnum-base = <0x20>; - phandle = <0x4a>; - }; - - gpio-controller@2 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x02>; - chipnum-base = <0x40>; - phandle = <0x47>; - }; - - gpio-controller@3 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x03>; - chipnum-base = <0x60>; - phandle = <0x54>; - }; - }; - - gpio@20011000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,dw-apb-gpio"; - reg = <0x00 0x20011000 0x1000>; - clocks = <0x05 0x82>; - clock-names = "bus"; - resets = <0x06 0x27>; - resets-names = "gpio1_reset"; - - gpio-controller@0 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x00>; - chipnum-base = <0x80>; - interrupt-controller; - #interrupt-cells = <0x03>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe0 0x04>; - }; - - gpio-controller@1 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x01>; - chipnum-base = <0xa0>; - }; - - gpio-controller@2 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x02>; - chipnum-base = <0xc0>; - }; - - gpio-controller@3 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x03>; - chipnum-base = <0xe0>; - phandle = <0x0d>; - }; - }; - - watchdog@2001a000 { - status = "disable"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5d 0x04>; - clocks = <0x05 0x5a 0x05 0x64>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x1f>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt0"; - }; - - watchdog@2001b000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5e 0x04>; - clocks = <0x05 0x5b 0x05 0x65>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x20>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt1"; - }; - - watchdog@2001c000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001c000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5f 0x04>; - clocks = <0x05 0x74 0x05 0x7b>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x2e>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt2"; - }; - - watchdog@2001d000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001d000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x60 0x04>; - clocks = <0x05 0x75 0x05 0x7c>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x2f>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt3"; - }; - - watchdog@32009000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x32009000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x43 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt0"; - }; - - watchdog@3200a000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x44 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt1"; - }; - - watchdog@3200b000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x45 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt2"; - }; - - watchdog@3200c000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200c000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x46 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt3"; - }; - - watchdog@3200d000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200d000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x47 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt4"; - }; - - watchdog@3200e000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200e000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x48 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt5"; - }; - - watchdog@3200f000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200f000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x49 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt6"; - }; - - watchdog@32010000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x32010000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4a 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt7"; - }; - - i2c@20000000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20000000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xcf 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x18>; - reset-names = "i2c0_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1c>; - }; - - i2c@20001000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20001000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd0 0x04>; - clock-frequency = <0xf4240>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x19>; - reset-names = "i2c1_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1d>; - - max96789@40 { - compatible = "bst,max96789"; - reg = <0x40>; - channel_ids = <0x2a 0x3a 0x2a 0x3b>; - }; - }; - - i2c@20002000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20002000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd1 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x1a>; - reset-names = "i2c2_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1e>; - - max96712@29 { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-ctl"; - reg = <0x29>; - lane-num = <0x02>; - i2c-port = <0x00>; - csi2-port = <0x00>; - lane-speed = <0x960>; - regs = <0x40>; - data-type = <0x2d>; - trigger-mode = <0x01>; - trigger-fps = <0x14>; - trigger-rx-gpio = <0x0a>; - trigger-tx-gpio = <0x08>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x06 0x06 0x06 0x06>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x1f 0x14 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - - endpoint { - remote-endpoint = <0x20>; - phandle = <0x71>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x21>; - phandle = <0x2d>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x22>; - phandle = <0x2e>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x23>; - phandle = <0x2f>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x24>; - phandle = <0x30>; - }; - }; - }; - }; - - max96712@2a { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-ctl"; - reg = <0x2a>; - i2c-port = <0x00>; - csi2-port = <0x00>; - lane-speed = <0x640>; - regs = <0x40>; - data-type = <0x2d>; - trigger-mode = <0x01>; - trigger-fps = <0x1e>; - trigger-rx-gpio = <0x01>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x03 0x03 0x03 0x03>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x1f 0x15 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - - endpoint { - remote-endpoint = <0x25>; - phandle = <0x76>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x26>; - phandle = <0x31>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x27>; - phandle = <0x32>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x28>; - phandle = <0x33>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x29>; - phandle = <0x34>; - }; - }; - }; - }; - - max96712@2e { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-ctl"; - reg = <0x2e>; - lane-num = <0x02>; - i2c-port = <0x00>; - csi2-port = <0x00>; - lane-speed = <0x960>; - regs = <0x40>; - data-type = <0x2d>; - trigger-mode = <0x01>; - trigger-fps = <0x14>; - trigger-rx-gpio = <0x0a>; - trigger-tx-gpio = <0x08>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x06 0x06 0x06 0x06>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x1f 0x17 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - - endpoint { - remote-endpoint = <0x2a>; - phandle = <0x7b>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x2b>; - phandle = <0x35>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x2c>; - phandle = <0x36>; - }; - }; - }; - }; - - camera@70 { - reg = <0x70>; - ser-alias-id = <0x60>; - sensor-alias-id = <0x70>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x2d>; - phandle = <0x21>; - }; - }; - }; - - camera71 { - status = "disabled"; - reg = <0x71>; - ser-alias-id = <0x61>; - sensor-alias-id = <0x71>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x2e>; - phandle = <0x22>; - }; - }; - }; - - camera@72 { - status = "disabled"; - reg = <0x72>; - ser-alias-id = <0x62>; - sensor-alias-id = <0x72>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x2f>; - phandle = <0x23>; - }; - }; - }; - - camera@73 { - status = "disabled"; - reg = <0x73>; - ser-alias-id = <0x63>; - sensor-alias-id = <0x73>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x30>; - phandle = <0x24>; - }; - }; - }; - - camera@54 { - reg = <0x54>; - ser-alias-id = <0x64>; - sensor-alias-id = <0x54>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x31>; - phandle = <0x26>; - }; - }; - }; - - camera@55 { - reg = <0x55>; - ser-alias-id = <0x65>; - sensor-alias-id = <0x55>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x32>; - phandle = <0x27>; - }; - }; - }; - - camera@56 { - reg = <0x56>; - ser-alias-id = <0x66>; - sensor-alias-id = <0x56>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x33>; - phandle = <0x28>; - }; - }; - }; - - camera@57 { - reg = <0x57>; - ser-alias-id = <0x67>; - sensor-alias-id = <0x57>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x34>; - phandle = <0x29>; - }; - }; - }; - - camera@58 { - reg = <0x58>; - ser-alias-id = <0x48>; - sensor-alias-id = <0x58>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x35>; - phandle = <0x2b>; - }; - }; - }; - - camera@59 { - status = "disabled"; - reg = <0x59>; - ser-alias-id = <0x49>; - sensor-alias-id = <0x59>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x36>; - phandle = <0x2c>; - }; - }; - }; - }; - - i2c@20003000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20003000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd2 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x2c>; - reset-names = "i2c3_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x37>; - }; - - i2c@20004000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20004000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd3 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x2d>; - reset-names = "i2c4_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x38>; - - lt9211@2d { - compatible = "bst,lt9211"; - reg = <0x2d>; - }; - }; - - i2c@20005000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20005000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd4 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x26>; - reset-names = "i2c5_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x39>; - }; - - ddr_ecc { - status = "okay"; - compatible = "bst,a1000_ddr_ecc"; - reg = <0x00 0x38000000 0x1400 0x00 0x3c000000 0x1400 0x00 0x33000000 0x140>; - reg-names = "ddr0\0ddr1\0a55_ctrl"; - interrupt-parent = <0x01>; - interrupts = <0x00 0x8b 0x04 0x00 0x8d 0x04>; - interrupt-names = "ddr0_ecc_irq\0ddr1_ecc_irq"; - mbox-names = "bstn-mbox"; - }; - - arm_pmu { - status = "okay"; - compatible = "arm,armv8-pmuv3"; - interrupt-parent = <0x01>; - interrupts = <0x00 0x38 0xff04 0x00 0x39 0xff04 0x00 0x3a 0xff04 0x00 0x3b 0xff04 0x00 0x3c 0xff04 0x00 0x3d 0xff04 0x00 0x3e 0xff04 0x00 0x3f 0xff04>; - interrupt-affinity = <0x3a 0x3b 0x3c 0x3d 0x3e 0x3f 0x40 0x41>; - }; - - noc_pmu@0x32702000 { - status = "okay"; - compatible = "bst,bst_noc_pmu"; - reg = <0x00 0x32702000 0x1000 0x00 0x32703000 0x1000 0x00 0x32704000 0x2000 0x00 0x32708000 0x1000 0x00 0x33402000 0x2400 0x00 0x33422000 0x4400 0x00 0x33401100 0x10 0x00 0x33421480 0x10>; - reg-names = "coresight_cpunoc_etf\0coresight_etr\0coresight_funnel\0coresight_corenoc_etf\0cpu_port_set\0core_port_set\0cpunoc_atb\0corenoc_atb"; - memory-region = <0x42>; - }; - - lsp0_pwm0@20012000 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x56 0x05 0x6b>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20012000 0x14 0x00 0x200120b0 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x43>; - }; - - lsp0_pwm1@20012014 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x57 0x05 0x6b>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20012014 0x14 0x00 0x200120b4 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x44>; - }; - - lsp1_pwm0@20013000 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x71 0x05 0x7a>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20013000 0x14 0x00 0x200130b0 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x45>; - }; - - lsp1_pwm1@20013014 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x72 0x05 0x7a>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20013014 0x14 0x00 0x200130b4 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x46>; - }; - - a55_timer0@32008000 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4b 0x04>; - reg = <0x00 0x32008000 0x14>; - }; - - a55_timer1@32008014 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4c 0x04>; - reg = <0x00 0x32008014 0x14>; - }; - - a55_timer2@32008028 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4d 0x04>; - reg = <0x00 0x32008028 0x14>; - }; - - a55_timer3@3200803c { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4e 0x04>; - reg = <0x00 0x3200803c 0x14>; - }; - - a55_timer4@32008050 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4f 0x04>; - reg = <0x00 0x32008050 0x14>; - }; - - a55_timer5@32008064 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x50 0x04>; - reg = <0x00 0x32008064 0x14>; - }; - - a55_timer6@32008078 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x51 0x04>; - reg = <0x00 0x32008078 0x14>; - }; - - a55_timer7@3200808c { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x52 0x04>; - reg = <0x00 0x3200808c 0x14>; - }; - - spi@2000c000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000c000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x00>; - cs-gpios = <0x47 0x02 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x48>; - }; - - spi@2000d000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000d000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdc 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x01>; - cs-gpios = <0x47 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x49>; - }; - - spi@2000c800 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000c800 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf4 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x02>; - cs-gpios = <0x4a 0x08 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4b 0x4c>; - }; - - spi@2000d800 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000d800 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf5 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x03>; - cs-gpios = <0x4a 0x10 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4d 0x4e>; - }; - - spi@20022000 { - status = "okay"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x20022000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe2 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x04>; - cs-gpios = <0x47 0x02 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4f>; - spi-slave; - - slave@0 { - compatible = "rohm,dh2228fv"; - reg = <0x00>; - spi-max-frequency = <0x989680>; - }; - }; - - spi@20023000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x20023000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe1 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x05>; - cs-gpios = <0x47 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x50>; - }; - - i2s-play@2000e000 { - status = "disable"; - compatible = "snps,designware-i2s"; - reg = <0x00 0x2000e000 0x1000>; - interrupt-names = "play_irq"; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdd 0x04>; - clocks = <0x05 0x61 0x05 0x59>; - clock-names = "i2spclk\0i2sclk"; - play; - channel = <0x02>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x51>; - }; - - i2s-rec@2000f000 { - status = "disable"; - compatible = "snps,designware-i2s"; - reg = <0x00 0x2000f000 0x1000>; - interrupt-names = "record_irq"; - interrupt-parent = <0x01>; - interrupts = <0x00 0xde 0x04>; - clocks = <0x02>; - record; - channel = <0x02>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x52>; - }; - - qspi@00000000 { - status = "okay"; - compatible = "snps,dwc-ssi-1.01a"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x00 0x4000000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd9 0x04>; - clocks = <0x05 0x4d 0x05 0x4e>; - clock-names = "hclk\0wclk"; - work-mode = <0x01>; - reg-io-width = <0x04>; - bst,use-gpio-cs; - spi-rx-bus-width = <0x04>; - spi-tx-bus-width = <0x04>; - cs-gpios = <0x47 0x15 0x00>; - num-cs = <0x01>; - bus-num = <0x06>; - pinctrl-names = "default"; - pinctrl-0 = <0x53>; - - qspi0-nor0@0 { - #address-cells = <0x01>; - #size-cells = <0x01>; - spi-rx-bus-width = <0x01>; - spi-tx-bus-width = <0x01>; - compatible = "jedec,spi-nor"; - status = "okay"; - spi-max-frequency = <0xf4240>; - reg = <0x00>; - mode = <0x00>; - powerctl-fada-gpios = <0x1f 0x1b 0x00>; - powerctl-fadb-gpios = <0x1f 0x1c 0x00>; - - partition@0 { - reg = <0x00 0x1e00000>; - label = "nor0_part0"; - }; - - partition@1e00000 { - reg = <0x1e00000 0x200000>; - label = "nor0_part1"; - }; - }; - }; - - qspi@14000000 { - status = "disable"; - compatible = "snps,dwc-ssi-1.01a"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x14000000 0x4000000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x05 0x4f 0x05 0x50>; - clock-names = "hclk\0wclk"; - work-mode = <0x01>; - reg-io-width = <0x04>; - bst,use-gpio-cs; - spi-rx-bus-width = <0x04>; - spi-tx-bus-width = <0x04>; - cs-gpios = <0x54 0x01 0x00>; - num-cs = <0x01>; - bus-num = <0x07>; - pinctrl-names = "default"; - pinctrl-0 = <0x55>; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x0f>; - snps,rd_osr_lmt = <0x0f>; - snps,axi_fb; - snps,blen = <0x04 0x08 0x10 0x00 0x00 0x00 0x00>; - phandle = <0x56>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x04>; - snps,rx-sched-sp; - phandle = <0x57>; - - queue0 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x00>; - snps,priority = <0x00>; - }; - - queue1 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x01>; - snps,priority = <0x01>; - }; - - queue2 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x02>; - snps,priority = <0x02>; - }; - - queue3 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x03>; - snps,priority = <0x03>; - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x04>; - snps,tx-sched-wrr; - phandle = <0x58>; - - queue0 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x00>; - }; - - queue1 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x01>; - }; - - queue2 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x02>; - }; - - queue3 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x03>; - }; - }; - - thermal@70039000 { - status = "okay"; - compatible = "bst,bst-thermal"; - reg = <0x00 0x70039000 0x1000>; - #thermal-sensor-cells = <0x00>; - phandle = <0x61>; - }; - - ethernet@30000000 { - status = "okay"; - compatible = "bst,dw-eqos-eth"; - reg = <0x00 0x30000000 0x100000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9f 0xff04 0x00 0xa0 0xff04 0x00 0xa1 0xff04 0x00 0xa2 0xff04 0x00 0xa3 0xff04 0x00 0xa4 0xff04 0x00 0xa5 0xff04 0x00 0xa6 0xff04 0x00 0xa7 0xff04 0x00 0xa8 0xff04 0x00 0xa9 0xff04 0x00 0xaa 0xff04>; - interrupt-names = "sbd_irq\0sfty_ce_irq\0sfty_ue_irq\0tx_chan0_irq\0tx_chan1_irq\0tx_chan2_irq\0tx_chan3_irq\0rx_chan0_irq\0rx_chan1_irq\0rx_chan2_irq\0rx_chan3_irq\0eth_lpi"; - ethernet-id = <0x00>; - mac-address = [00 00 00 00 00 00]; - max-frame-size = <0xed8>; - phy-mode = "rgmii"; - snps,multicast-filter-bins = <0x100>; - snps,perfect-filter-entries = <0x80>; - rx-fifo-depth = <0x4000>; - tx-fifo-depth = <0x4000>; - clocks = <0x05 0x0f 0x05 0x13 0x05 0x15 0x05 0x11>; - clock-names = "wclk\0axim_aclk\0pclk\0ptp_ref"; - bst,fix-safety = <0x00>; - bst,dma_int_mode = <0x01>; - snps,fixed-burst; - snps,force_sf_dma_mode; - snps,ps-speed = <0x3e8>; - snps,axi-config = <0x56>; - snps,mtl-rx-config = <0x57>; - snps,mtl-tx-config = <0x58>; - label = "gmac0"; - resets = <0x06 0x11>; - reset-names = "bstgmaceth"; - eth-name = "gmac0"; - eth-number = <0x00>; - mac-mode = "rgmii"; - extend-op = <0x02>; - pinctrl-names = "default"; - pinctrl-0 = <0x59>; - - fixed-link { - speed = <0x3e8>; - full-duplex; - }; - }; - - ethernet@30100000 { - status = "okay"; - compatible = "bst,dw-eqos-eth"; - reg = <0x00 0x30100000 0x100000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xab 0xff04 0x00 0xac 0xff04 0x00 0xad 0xff04 0x00 0xae 0xff04 0x00 0xaf 0xff04 0x00 0xb0 0xff04 0x00 0xb1 0xff04 0x00 0xb2 0xff04 0x00 0xb3 0xff04 0x00 0xb4 0xff04 0x00 0xb5 0xff04 0x00 0xb6 0xff04>; - interrupt-names = "sbd_irq\0sfty_ce_irq\0sfty_ue_irq\0tx_chan0_irq\0tx_chan1_irq\0tx_chan2_irq\0tx_chan3_irq\0rx_chan0_irq\0rx_chan1_irq\0rx_chan2_irq\0rx_chan3_irq\0eth_lpi"; - ethernet-id = <0x01>; - mac-address = [00 00 00 00 00 00]; - max-frame-size = <0xed8>; - phy-mode = "rgmii"; - snps,multicast-filter-bins = <0x100>; - snps,perfect-filter-entries = <0x80>; - rx-fifo-depth = <0x4000>; - tx-fifo-depth = <0x4000>; - clocks = <0x05 0x10 0x05 0x14 0x05 0x16 0x05 0x12>; - clock-names = "wclk\0axim_aclk\0pclk\0ptp_ref"; - bst,fix-safety = <0x00>; - bst,dma_int_mode = <0x01>; - snps,fixed-burst; - snps,force_sf_dma_mode; - snps,ps-speed = <0x3e8>; - snps,axi-config = <0x56>; - snps,mtl-rx-config = <0x57>; - snps,mtl-tx-config = <0x58>; - label = "gmac1"; - resets = <0x06 0x12>; - reset-names = "bstgmaceth"; - pinctrl-names = "default"; - pinctrl-0 = <0x5a>; - extend-op = <0x02>; - eth-name = "gmac1"; - eth-number = <0x01>; - mac-mode = "rgmii"; - phy-handle = <0x5b>; - - mdio1 { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - eth_phy1@1 { - compatible = "marvell,88Q2112\0ethernet-phy-id002B.0983\0ethernet-phy-ieee802.3-c45"; - device_type = "ethernet-phy"; - max-speed = <0x3e8>; - reg = <0x07>; - reset-gpios = <0x1f 0x09 0x01>; - reset-active-low; - reset-assert-us = <0x4e20>; - reset-deassert-us = <0x4e20>; - phandle = <0x5b>; - }; - }; - }; - - phy@30E01000 { - compatible = "bst,dwc-usb-phy"; - #phy-cells = <0x00>; - reg = <0x00 0x30e01000 0x1000>; - usb_mode = "usb20"; - phandle = <0x5e>; - }; - - phy@30E00000 { - compatible = "bst,dwc-usb-phy"; - reg = <0x00 0x30e00000 0x1000>; - #phy-cells = <0x00>; - usb_mode = "usb30"; - pll_type = "internal"; - phandle = <0x5c>; - }; - - usb3 { - compatible = "bst,dwc3usb"; - status = "okay"; - ranges; - #address-cells = <0x02>; - #size-cells = <0x01>; - clock-names = "suspend\0ref\0axi\0apb"; - clocks = <0x05 0x17 0x05 0x18 0x05 0x19 0x05 0x1a>; - resets = <0x06 0x04>; - reset-names = "usb3_reset"; - phys = <0x5c>; - phy-names = "usb-phy"; - pll_type = "internal"; - powerctl-gpios = <0x1f 0x0e 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x5d>; - - dwc3@30200000 { - compatible = "snps,dwc3"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - reg = <0x00 0x30200000 0x100000>; - interrupts = <0x00 0xc8 0x04>; - interrupt-parent = <0x01>; - dr_mode = "host"; - snps,dis_u3_susphy_quirk; - }; - }; - - usb2 { - compatible = "bst,dwc3usb"; - status = "okay"; - ranges; - #address-cells = <0x02>; - #size-cells = <0x01>; - clock-names = "ahb\0ref\0apb"; - clocks = <0x05 0x1b 0x05 0x1d 0x05 0x1c>; - reset-names = "usb2_reset"; - resets = <0x06 0x05>; - phys = <0x5e>; - phy-names = "usb-phy"; - pll_type = "internal"; - - dwc3@30300000 { - status = "okay"; - compatible = "snps,dwc3"; - reg = <0x00 0x30300000 0x100000>; - interrupts = <0x00 0xc9 0x04>; - interrupt-parent = <0x01>; - dr_mode = "peripheral"; - snps,incr-burst-type-adjustment = <0x01 0x04 0x08 0x10>; - snps,reqinfo-for-data-read = <0x08>; - snps,reqinfo-for-descriptor-read = <0x08>; - }; - }; - - dwmmc0@30400000 { - status = "okay"; - compatible = "bst,dwcmshc-sdhci"; - clocks = <0x05 0x1f 0x05 0x1e>; - clock-names = "core\0bus"; - reg = <0x00 0x30400000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xb9 0x04>; - interrupt-names = "IRQDWMMC0"; - #address-cells = <0x01>; - #size-cells = <0x00>; - data-addr = <0x200>; - fifo-watermark-aligned; - clock-frequency = <0x2faf080>; - max-frequency = <0xbebc200>; - min-frequency = <0x61a80>; - broken-64bit-dma; - clear-tarns-mode; - fifo-depth = <0x400>; - card-detect-delay = <0xc8>; - bus-width = <0x08>; - cap-mmc-highspeed; - non-removable; - no-sdio; - no-sd; - keep-power-in-suspend; - no-3-3-v; - sdhci,auto-cmd12; - pinctrl-names = "default"; - pinctrl-0 = <0x5f>; - }; - - dwmmc1@30500000 { - status = "okay"; - compatible = "bst,dwcmshc-sdhci"; - clocks = <0x05 0x21 0x05 0x20>; - clock-names = "core\0bus"; - reg = <0x00 0x30500000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xbd 0x04>; - interrupt-names = "IRQDWMMC1"; - #address-cells = <0x01>; - #size-cells = <0x00>; - data-addr = <0x200>; - fifo-watermark-aligned; - clock-frequency = <0x5f5e100>; - max-frequency = <0xbebc200>; - min-frequency = <0x61a80>; - broken-64bit-dma; - clear-tarns-mode; - fifo-depth = <0x400>; - card-detect-delay = <0xc8>; - bus-width = <0x04>; - cap-sd-highspeed; - sd-uhs-sdr12; - sd-uhs-sdr25; - sd-uhs-sdr50; - no-sdio; - no-mmc; - sdhci,auto-cmd12; - keep-power-in-suspend; - pinctrl-names = "default"; - pinctrl-0 = <0x60>; - }; - - gpu@33300000 { - status = "okay"; - compatible = "arm,mali-450\0arm,mali-utgard"; - reg = <0x00 0x33300000 0x30000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04>; - interrupt-names = "IRQPP0\0IRQPPMMU0\0IRQPP1\0IRQPPMMU1\0IRQGP\0IRQGPMMU\0IRQPMU\0IRQPP"; - clocks = <0x05 0x31 0x05 0x32>; - clock-names = "clk_mali\0clk_mali_apb"; - resets = <0x06 0x0b>; - reset-names = "gpu_reset"; - ppcores = <0x02>; - dedicated_mem_start = <0x00>; - dedicated_mem_size = <0x00>; - }; - - mali-v500@0x55000000 { - status = "okay"; - compatible = "arm,mali-v500"; - reg = <0x00 0x55000000 0xffff>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9a 0x04>; - interrupt-names = "IRQV500"; - resets = <0x06 0x00>; - reset-names = "codec_reset"; - clocks = <0x05 0x3d>; - clock-names = "clk_v500"; - }; - }; - - cooling_dev { - - pwm { - cpumask = <0x0f>; - capacitance = <0x5dc>; - #cooling-cells = <0x02>; - phandle = <0x63>; - }; - }; - - thermal-zones { - - cpu-thermal { - polling-delay-passive = <0x1f4>; - polling-delay = <0x3e8>; - thermal-sensors = <0x61>; - - trips { - - switch_trip { - temperature = <0x15f90>; - hysteresis = <0x7d0>; - type = "passive"; - phandle = <0x62>; - }; - - critical_trip { - temperature = <0x1e848>; - hysteresis = <0x00>; - type = "critical"; - }; - }; - - cooling-maps { - - map0 { - trip = <0x62>; - cooling-device = <0x63 0x00 0x01>; - }; - }; - }; - }; - - pinctrl@70038000 { - status = "okay"; - compatible = "bst,pinctrl-a1000b"; - #address-cells = <0x02>; - #size-cells = <0x02>; - reg = <0x00 0x70038000 0x00 0x1000 0x00 0x33001000 0x00 0x1000>; - reg-names = "aon\0top"; - #gpio-cells = <0x02>; - - spi0_pinctrl { - phandle = <0x48>; - - mux { - pins = "spi0_miso\0spi0_mosi\0spi0_sclk"; - function = "spi0"; - }; - }; - - spi1_pinctrl { - phandle = <0x49>; - - mux { - pins = "spi1_miso\0spi1_mosi\0spi1_sclk"; - function = "spi1"; - }; - }; - - spi2_pinctrl { - phandle = <0x4b>; - - mux { - pins = "uart0_cts\0uart1_cts\0uart1_rts"; - function = "spi2"; - }; - }; - - spi2_cs_pinctrl { - phandle = <0x4c>; - - mux { - pins = "uart0_rts"; - function = "gpio"; - }; - }; - - spi3_pinctrl { - phandle = <0x4d>; - - mux { - pins = "uart2_cts\0uart3_cts\0uart3_rts"; - function = "spi3"; - }; - }; - - spi3_cs_pinctrl { - phandle = <0x4e>; - - mux { - pins = "uart2_rts"; - function = "gpio"; - }; - }; - - spi4_pinctrl { - phandle = <0x4f>; - - mux { - pins = "spi0_miso\0spi0_mosi\0spi0_sclk\0spi0_cs"; - function = "spi0_s"; - }; - }; - - spi5_pinctrl { - phandle = <0x50>; - - mux { - pins = "spi1_miso\0spi1_mosi\0spi1_sclk"; - function = "spi1_s"; - }; - }; - - i2c0_pinctrl { - phandle = <0x1c>; - - mux { - pins = "i2c0_scl\0i2c0_sda"; - function = "i2c0"; - }; - }; - - i2c1_pinctrl { - phandle = <0x1d>; - - mux { - pins = "i2c1_scl\0i2c1_sda"; - function = "i2c1"; - }; - }; - - i2c2_pinctrl { - phandle = <0x1e>; - - mux { - pins = "i2c2_scl\0i2c2_sda"; - function = "i2c2"; - }; - }; - - i2c3_pinctrl { - phandle = <0x37>; - - mux { - pins = "i2c3_scl\0i2c3_sda"; - function = "i2c3"; - }; - }; - - i2c4_pinctrl { - phandle = <0x38>; - - mux { - pins = "i2c4_scl\0i2c4_sda"; - function = "i2c4"; - }; - }; - - i2c5_pinctrl { - phandle = <0x39>; - - mux { - pins = "i2c5_scl\0i2c5_sda"; - function = "i2c5"; - }; - }; - - uart0_pinctrl { - phandle = <0x07>; - - mux { - pins = "uart0_txd\0uart0_rxd"; - function = "uart0"; - }; - }; - - uart1_pinctrl { - phandle = <0x08>; - - mux { - pins = "uart1_txd\0uart1_rxd"; - function = "uart1"; - }; - }; - - uart2_pinctrl { - phandle = <0x09>; - - mux { - pins = "uart2_txd\0uart2_rxd"; - function = "uart2"; - }; - }; - - uart3_pinctrl { - phandle = <0x0a>; - - mux { - pins = "uart3_txd\0uart3_rxd"; - function = "uart3"; - }; - }; - - gpio0_pinctrl { - - mux { - pins = "gpio_29"; - function = "gpio"; - }; - }; - - gpio_special_func_pinctrl { - - mux { - pins = "gpio_24\0gpio_29\0debug4\0debug5\0uart0_cts\0uart0_rts"; - function = "gpio"; - }; - }; - - qspi0_pinctrl { - phandle = <0x53>; - - mux { - pins = "qspi0_cs0"; - function = "gpio"; - }; - }; - - usb3_pinctrl { - phandle = <0x5d>; - - mux { - pins = "gpio_14"; - function = "gpio"; - }; - }; - - qspi1_pinctrl { - phandle = <0x55>; - - mux { - pins = "qspi1_cs1"; - function = "gpio"; - }; - }; - - des_960_1_pinctrl { - - mux { - pins = "qspi1_io1"; - function = "gpio"; - }; - }; - - des_960_2_pinctrl { - - mux { - pins = "qspi1_io3"; - function = "gpio"; - }; - }; - - des_960_3_pinctrl { - - mux { - pins = "qspi1_io5"; - function = "gpio"; - }; - }; - - bist_pinctrl { - - mux { - pins = "spi1_mosi"; - function = "bist"; - }; - }; - - - - - - i2s0_pinctrl { - phandle = <0x51>; - - mux { - pins = "i2s0_ck\0i2s0_mck\0i2s0_sd_out\0i2s0_ws\0qspi0_io4"; - function = "i2s0"; - }; - }; - - i2s1_pinctrl { - phandle = <0x52>; - - mux { - pins = "i2s1_ck\0i2s1_sd_in\0i2s1_ws"; - function = "i2s1"; - }; - }; - - isp_pinctrl { - phandle = <0x66>; - - mux { - pins = "isp_fsync0\0isp_fsync1\0isp_fsync2\0isp_fsync3"; - function = "isp"; - }; - }; - - ptp_pinctrl { - - mux { - pins = "ptp_pps0\0ptp_pps1\0ptp_clk"; - function = "ptp"; - }; - }; - - ptp_pinconfig { - - config { - pins = "ptp_clk"; - drive-strength = <0x02>; - input-enable; - }; - }; - - err_rpt_l0_pinctrl { - - mux { - pins = "err_rpt_l0_n\0err_rpt_l0_p"; - function = "err_rpt_l0"; - }; - }; - - err_rpt_gpio_l0_pinctrl { - - mux { - pins = "err_rpt_l0_n\0err_rpt_l0_p"; - function = "gpio"; - }; - }; - - err_rpt_l1_pinctrl { - - mux { - pins = "err_rpt_l1_n\0err_rpt_l1_p"; - function = "err_rpt_l1"; - }; - }; - - pwm_lsp0_pwm0_pinctrl { - phandle = <0x43>; - - mux { - pins = "pwm0"; - function = "pwm"; - }; - }; - - pwm_lsp0_pwm1_pinctrl { - phandle = <0x44>; - - mux { - pins = "pwm1"; - function = "pwm"; - }; - }; - - pwm_lsp1_pwm0_pinctrl { - phandle = <0x45>; - - mux { - pins = "pwm2"; - function = "pwm"; - }; - }; - - pwm_lsp1_pwm1_pinctrl { - phandle = <0x46>; - - mux { - pins = "pwm3"; - function = "pwm"; - }; - }; - - ts_pinctrl { - - mux { - pins = "ts_trig_in00\0ts_trig_in01\0ts_trig_in10\0ts_trig_in11"; - function = "ts"; - }; - }; - - sdemmc0_pinctrl { - phandle = <0x15>; - - mux { - pins = "sdemmc0_clk\0sdemmc0_cmd\0sdemmc0_dat0\0sdemmc0_dat1\0sdemmc0_dat2\0sdemmc0_dat3\0sdemmc0_dat4\0sdemmc0_dat5\0sdemmc0_dat6\0sdemmc0_dat7\0sdemmc0_rstb\0sdemmc0_cdn\0sdemmc0_wp"; - function = "sdemmc0"; - }; - }; - - sdemmc0_pinconfig { - phandle = <0x5f>; - - config { - pins = "sdemmc0_clk\0sdemmc0_cmd\0sdemmc0_dat0\0sdemmc0_dat1\0sdemmc0_dat2\0sdemmc0_dat3\0sdemmc0_dat4\0sdemmc0_dat5\0sdemmc0_dat6\0sdemmc0_dat7\0sdemmc0_rstb\0sdemmc0_cdn\0sdemmc0_wp"; - drive-strength = <0x02>; - input-enable; - }; - }; - - sdemmc1_pinctrl { - phandle = <0x16>; - - mux { - pins = "sdemmc1_clk\0sdemmc1_cmd\0sdemmc1_dat0\0sdemmc1_dat1\0sdemmc1_dat2\0sdemmc1_dat3\0sdemmc1_dat4\0sdemmc1_dat5\0sdemmc1_dat6\0sdemmc1_dat7\0sdemmc1_rstb\0sdemmc1_cdn\0sdemmc1_wp"; - function = "sdemmc1"; - }; - }; - - sdemmc1_pinconfig { - phandle = <0x60>; - - config { - pins = "sdemmc1_clk\0sdemmc1_cmd\0sdemmc1_dat0\0sdemmc1_dat1\0sdemmc1_dat2\0sdemmc1_dat3\0sdemmc1_dat4\0sdemmc1_dat5\0sdemmc1_dat6\0sdemmc1_dat7\0sdemmc1_rstb\0sdemmc1_cdn\0sdemmc1_wp"; - drive-strength = <0x0f>; - input-enable; - }; - }; - - debug_pinctrl { - phandle = <0x17>; - - mux { - pins = "debug0\0debug1\0debug2\0debug3\0debug4\0debug5\0debug6\0debug7"; - function = "debug"; - }; - }; - - strap_pinctrl { - - mux { - pins = "gpio_24\0gpio_25\0gpio_26\0gpio_27\0gpio_28\0gpio_29\0spi1_sclk\0i2s0_mck\0i2s0_ck\0gpio_107\0gpio_108"; - function = "strap"; - }; - }; - - vout_pinctrl { - phandle = <0x18>; - - mux { - pins = "vout_r0\0vout_r1\0vout_r2\0vout_r3\0vout_r4\0vout_r5\0vout_r6\0vout_r7\0vout_g0\0vout_g1\0vout_g2\0vout_g3\0vout_g4\0vout_g5\0vout_g6\0vout_g7\0vout_b0\0vout_b1\0vout_b2\0vout_b3\0vout_b4\0vout_b5\0vout_b6\0vout_b7\0vout_hs\0vout_vs\0vout_de\0vout_pclk\0vout_pdb"; - function = "vout"; - }; - }; - - vout_pinconfig { - - config { - pins = "vout_r0\0vout_r1\0vout_r2\0vout_r3\0vout_r4\0vout_r5\0vout_r6\0vout_r7\0vout_g0\0vout_g1\0vout_g2\0vout_g3\0vout_g4\0vout_g5\0vout_g6\0vout_g7\0vout_b0\0vout_b1\0vout_b2\0vout_b3\0vout_b4\0vout_b5\0vout_b6\0vout_b7\0vout_hs\0vout_vs\0vout_de\0vout_pclk\0vout_pdb"; - drive-strength = <0x02>; - input-enable; - }; - }; - - vin_pinctrl { - phandle = <0x19>; - - mux { - pins = "vin_b0\0vin_b1\0vin_b2\0vin_b3\0vin_b4\0vin_de\0vin_g0\0vin_g1\0vin_g2\0vin_g3\0vin_g4\0vin_g5\0vin_hs\0vin_llc\0vin_r0\0vin_r1\0vin_r2\0vin_r3\0vin_r4\0vin_vs"; - function = "vin"; - }; - }; - - vin_pinconfig { - - config { - pins = "vin_b0\0vin_b1\0vin_b2\0vin_b3\0vin_b4\0vin_de\0vin_g0\0vin_g1\0vin_g2\0vin_g3\0vin_g4\0vin_g5\0vin_hs\0vin_llc\0vin_r0\0vin_r1\0vin_r2\0vin_r3\0vin_r4\0vin_vs"; - drive-strength = <0x02>; - input-enable; - }; - }; - - rgmii0_pinctrl { - phandle = <0x59>; - - mux { - pins = "rgmii0_txd0\0rgmii0_txd1\0rgmii0_txd2\0rgmii0_txd3\0gmii0_txd4\0gmii0_txd5\0gmii0_txd6\0gmii0_txd7\0gmii0_txer\0rgmii0_txctrl\0mii0_txclk\0rgmii0_gtxclk\0rgmii0_rxd0\0rgmii0_rxd1\0rgmii0_rxd2\0rgmii0_rxd3\0gmii0_rxd4\0gmii0_rxd5\0gmii0_rxd6\0gmii0_rxd7\0gmii0_rxer\0rgmii0_rxctrl\0rgmii0_rxclk\0rgmii0_mdio\0rgmii0_mdc\0rgmii0_intr"; - function = "rgmii0"; - }; - }; - - rgmii0_pinconfig { - - config { - pins = "rgmii0_gtxclk\0rgmii0_mdc\0rgmii0_mdio\0rgmii0_rxclk\0rgmii0_rxctrl\0rgmii0_rxd0\0rgmii0_rxd1\0rgmii0_rxd2\0rgmii0_rxd3\0rgmii0_txctrl\0rgmii0_txd0\0rgmii0_txd1\0rgmii0_txd2\0rgmii0_txd3"; - drive-strength = <0x02>; - input-enable; - }; - }; - - rgmii1_pinctrl { - phandle = <0x5a>; - - mux { - pins = "rgmii1_txd0\0rgmii1_txd1\0rgmii1_txd2\0rgmii1_txd3\0mii1_rxer\0mii1_txclk\0rgmii1_txctrl\0rgmii1_gtxclk\0rgmii1_rxd0\0rgmii1_rxd1\0rgmii1_rxd2\0rgmii1_rxd3\0rgmii1_rxctrl\0rgmii1_rxclk\0rgmii1_mdc\0rgmii1_mdio\0rgmii1_intr"; - function = "rgmii1"; - }; - }; - - rgmii1_pinconfig { - - config { - pins = "rgmii1_gtxclk\0rgmii1_mdc\0rgmii1_mdio\0rgmii1_rxclk\0rgmii1_rxctrl\0rgmii1_rxd0\0rgmii1_rxd1\0rgmii1_rxd2\0rgmii1_rxd3\0rgmii1_txctrl\0rgmii1_txd0\0rgmii1_txd1\0rgmii1_txd2\0rgmii1_txd3"; - drive-strength = <0x02>; - input-enable; - }; - }; - - gmii0_pinconfig { - - config { - pins = "gmii0_rxd4\0gmii0_rxd5\0gmii0_rxd6\0gmii0_rxd7\0gmii0_rxer\0gmii0_txd4\0gmii0_txd5\0gmii0_txd6\0gmii0_txd7\0gmii0_txer"; - drive-strength = <0x02>; - input-enable; - }; - }; - - ssd_pinctrl { - - mux { - pins = "sdemmc0_led_ctl\0sdemmc1_led_ctl\0gpio_26\0gpio_27"; - function = "gpio"; - }; - }; - - gnss_pinctrl { - - mux { - pins = "gpio_31\0gpio_12\0pwm1"; - function = "gpio"; - }; - }; - - fan_pinctrl { - phandle = <0x1a>; - - mux { - pins = "gpio_27\0gpio_28"; - function = "gpio"; - }; - }; - - - - - board_special_func_pinctrl { - phandle = <0x1b>; - - mux { - pins = "vout_pdb\0gpio_31"; - function = "gpio"; - }; - }; - }; - - isp { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,a1000b-isp"; - memory-region = <0x64 0x65>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x06>; - isp-fw-fbuf-addr = <0xa2000000>; - isp-fw-fbuf-size = <0x10000000>; - dma-coherent; - pinctrl-names = "default"; - pinctrl-0 = <0x66>; - - core@0 { - id = <0x00>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x67>; - phandle = <0x72>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x68>; - phandle = <0x73>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x69>; - phandle = <0x74>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x6a>; - phandle = <0x75>; - }; - }; - }; - }; - - core@1 { - id = <0x01>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@4 { - remote-endpoint = <0x6b>; - phandle = <0x77>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@5 { - remote-endpoint = <0x6c>; - phandle = <0x78>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@6 { - remote-endpoint = <0x6d>; - phandle = <0x79>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@7 { - remote-endpoint = <0x6e>; - phandle = <0x7a>; - }; - }; - }; - }; - - core@2 { - id = <0x02>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@8 { - remote-endpoint = <0x6f>; - phandle = <0x7c>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@9 { - remote-endpoint = <0x70>; - phandle = <0x7d>; - }; - }; - }; - }; - }; - - csi@0 { - compatible = "bst,a1000b_csi2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - lane-speed = <0x960>; - id = <0x00>; - resets = <0x06 0x0d>; - reset-names = "csi0_reset"; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x71>; - phandle = <0x20>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x72>; - phandle = <0x67>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x73>; - phandle = <0x68>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x74>; - phandle = <0x69>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x75>; - phandle = <0x6a>; - }; - }; - }; - }; - - csi@1 { - compatible = "bst,a1000b_csi2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - lane-speed = <0x640>; - id = <0x01>; - resets = <0x06 0x0e>; - reset-names = "csi1_reset"; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x76>; - phandle = <0x25>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x77>; - phandle = <0x6b>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x78>; - phandle = <0x6c>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x79>; - phandle = <0x6d>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x7a>; - phandle = <0x6e>; - }; - }; - }; - }; - - csi@3 { - compatible = "bst,a1000b-csi2-2x2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - lane-speed = <0x960>; - resets = <0x06 0x10>; - reset-names = "csi2_reset"; - id = <0x03>; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x7b>; - phandle = <0x2a>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x7c>; - phandle = <0x6f>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x7d>; - phandle = <0x70>; - }; - }; - }; - }; - - chosen { - bootargs = "earlycon=uart8250,mmio32,0x20008000 console=ttyS0,115200n8 memreserve=64M@0xf8000000 rdinit=/sbin/init root=/dev/mmcblk0p7 rw rodata=n"; - stdout-path = "uart0"; - }; - - aliases { - uart0 = "/amba_apu/serial@20008000"; - }; - - memory@90000000 { - device_type = "memory"; - reg = <0x00 0x90000000 0x00 0x60000000>; - }; - - // memory@1b0000000 { - // device_type = "memory"; - // reg = <0x01 0xb0000000 0x00 0x40000000>; - // }; - - reserved-memory { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - pcie_ctrl@8fd00000 { - compatible = "bst,pcie-ctrl"; - reg = <0x00 0x8fd00000 0x00 0x100000>; - no-map; - phandle = <0x0c>; - }; - - bst_atf@8b000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8b000000 0x00 0x2000000>; - no-map; - }; - - bst_tee@8fec0000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8fec0000 0x00 0x40000>; - no-map; - phandle = <0x81>; - }; - - bstn_cma@8ff00000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8ff00000 0x00 0x100000>; - no-map; - phandle = <0x7e>; - }; - - bstn@90000000 { - compatible = "bst,bstn"; - reg = <0x00 0x90000000 0x00 0x2000000>; - no-map; - }; - - bst_lwnn@92000000 { - compatible = "bst,bst_lwnn"; - reg = <0x00 0x92000000 0x00 0x2000000>; - no-map; - }; - - bst_lwnn@94000000 { - compatible = "bst,bst_lwnn"; - reg = <0x00 0x94000000 0x00 0x2000000>; - no-map; - }; - - bst_cv@96000000 { - compatible = "bst,bst_cv"; - reg = <0x00 0x96000000 0x00 0x4000000>; - no-map; - }; - - bst_cv_cma@9a000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x9a000000 0x00 0x2000000>; - align-shift = <0x08>; - no-map; - phandle = <0x7f>; - }; - - vsp@0x9c000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x9c000000 0x00 0x1000000>; - no-map; - phandle = <0x80>; - }; - - vsp_fw@0x9d000000 { - reg = <0x00 0x9d000000 0x00 0x4000000>; - no-map; - }; - - bst_isp@0xa1000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0xa1000000 0x00 0x1000000>; - no-map; - phandle = <0x64>; - }; - - bst_isp_fw@0xa2000000 { - reg = <0x00 0xa2000000 0x00 0x10000000>; - no-map; - }; - - coreip_pub_cma@0xb2000000 { - compatible = "shared-dma-pool"; - align-shift = <0x08>; - reg = <0x00 0xb2000000 0x00 0x36000000>; - reusable; - phandle = <0x65>; - }; - - noc_pmu@0xe8000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0xe8000000 0x00 0x800000>; - reusable; - phandle = <0x42>; - }; - - - ddr0@0xf0000000 { - reg = <0x00 0xf0000000 0x00 0x10000000>; - no-map; - }; - - ddr1@0x1f0000000 { - reg = <0x01 0xf0000000 0x00 0x10000000>; - no-map; - }; - }; - - mbox-poll-clients { - compatible = "bst,ipc-mbox-client"; - reg = <0xfec00020 0x08 0x52030090 0x08 0x53090008 0x08 0xfec00028 0x08>; - #mbox-cells = <0x01>; - phandle = <0x0e>; - }; - - bstn-mbox { - compatible = "bstn,bstn-mbox"; - reg = <0x00 0x33102000 0x00 0x2000 0x00 0x33100000 0x00 0x2000 0x00 0x80000000 0x00 0x04 0x00 0x80002000 0x00 0x04>; - fpga-reset = <0x01>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x71 0xff04 0x00 0x72 0xff04 0x00 0x73 0xff04 0x00 0x74 0xff04 0x00 0x75 0xff04 0x00 0x76 0xff04 0x00 0x77 0xff04 0x00 0x78 0xff04>; - #mbox-cells = <0x01>; - phandle = <0xea>; - }; - - bstn@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bstn-a1000b,cma"; - reg = <0x00 0x50020000 0x00 0x100 0x00 0x90000000 0x00 0x2000000>; - memory-region = <0x65>; - rmem-base = <0x00 0xb2000000>; - rmem-size = <0x00 0x36000000>; - id = <0x00>; - assigned-mem-size = <0x1000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x08>; - firmware = "bstn_dsp_rtos.rbf"; - }; - - bst_cv@0x51030000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst_cv,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x96000000 0x00 0x2000000 0x00 0x98000000 0x00 0x2000000 0x00 0x92000000 0x00 0x2000000 0x00 0x94000000 0x00 0x2000000>; - memory-region = <0x7f>; - assigned-mem-size = <0x4000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x09>; - dsp-num = <0x04>; - ipc-register-addr = <0x8ff00000>; - - dsp@0x96000000 { - index = <0x00>; - firmware = "bst_cv_dsp_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x967ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x98000000 { - index = <0x01>; - firmware = "bst_cv_dsp1_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x987ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x92000000 { - index = <0x02>; - firmware = "bst_cv_dsp2_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x927ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x94000000 { - index = <0x03>; - firmware = "bst_cv_dsp3_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x947ff000>; - ipc-src-core = <0x03>; - }; - }; - - bst_gwarp_scaler@0x51060000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst_gwarp_scaler,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x51050000 0x00 0x100 0x00 0x51060000 0x00 0x100>; - memory-region = <0x65>; - id = <0x00>; - bus-offset = <0x00 0x00>; - assigned-mem-size = <0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9b 0x04>; - interrupt-names = "bst_cv_irq"; - }; - - bare_cv@51030000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bare_cv,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x96000000 0x00 0x1000000 0x00 0x98000000 0x00 0x1000000 0x00 0x97000000 0x00 0x1000000 0x00 0x99000000 0x00 0x1000000>; - memory-region = <0x7f>; - id = <0x01>; - assigned-mem-size = <0x2000000 0x2000000 0x2000000 0x2000000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x09>; - firmware = "bstcv0.rbf\0bstcv1.rbf\0bstcv2.rbf\0bstcv3.rbf"; - }; - - bst_lwnn@0x51030000 { - compatible = "bst,bst_lwnn-a1000b,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x92000000 0x00 0x2000000 0x00 0x94000000 0x00 0x2000000>; - memory-region = <0x65>; - bus-offset = <0x00 0x00>; - mbox-names = "bst-lwnn-mbox"; - dsp-num = <0x02>; - ipc-register-addr = <0x8ff00000>; - - dsp@0x92000000 { - index = <0x02>; - firmware = "bst_lwnn_dsp2_rt.rbf"; - assigned-mem-size = <0x2000>; - rt-init-addr = <0x927ff000>; - ipc-src-core = <0x06>; - }; - - dsp@0x94000000 { - index = <0x03>; - firmware = "bst_lwnn_dsp3_rt.rbf"; - assigned-mem-size = <0x2000>; - rt-init-addr = <0x947ff000>; - ipc-src-core = <0x00>; - }; - }; - - ipc_vsp@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-vsp-ipc"; - reg = <0x00 0x9c000000 0x00 0x100000 0x00 0x9c100000 0x00 0x80000 0x00 0x9c180000 0x00 0x80000 0x00 0x53090004 0x00 0x04 0x00 0x53090010 0x00 0x0c 0x00 0x33102fbc 0x00 0x04 0x00 0x9d000000 0x00 0x4000000>; - memory-region = <0x80>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x07>; - }; - - vsp@1 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-vsp"; - memory-region = <0x65>; - assigned-mem-size = <0x1000>; - clocks = <0x05 0x43>; - clock-names = "vout_display_clk"; - output-format = "HDMI_RGB"; - output-hsize = <0x780>; - output-vsize = <0x438>; - output-fresh = <0x3c>; - }; - - gmwarp@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-gmwarp"; - memory-region = <0x65>; - }; - - encoder@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-encoder"; - memory-region = <0x65>; - }; - - codec_dma_buf@0 { - compatible = "bst,dma_buf_te"; - memory-region = <0x65>; - }; - - firmware { - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - chip-number = <0x02>; - }; - }; - - tee { - #address-cells = <0x02>; - #size-cells = <0x02>; - reg = <0x00 0x18060000 0x00 0x20000>; - memory-region = <0x81>; - mbox-names = "bstn-mbox"; - chip-number = <0x01>; - }; - - ipc_arm0@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm0"; - reg = <0x04 0xfec00000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x00>; - }; - - ipc_arm3@3 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm3"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x03>; - }; - - ipc_arm2@2 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm2"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x02>; - }; - - ipc_arm1@1 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm1"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x01>; - }; - - ipc@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,ipc"; - reg = <0x04 0xfec00000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x05>; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fadb.dts b/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fadb.dts deleted file mode 100644 index 287b9526d..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fadb.dts +++ /dev/null @@ -1,3378 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000018000000 0x0000000000100000; -/ { - compatible = "bst,a1000b"; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "BST A1000B FAD-B"; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - - cpu@0 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x00>; - cpu-idle-states = <0x04>; - phandle = <0x3b>; - }; - - cpu@1 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x100>; - cpu-idle-states = <0x04>; - phandle = <0x3c>; - }; - - cpu@2 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x200>; - cpu-idle-states = <0x04>; - phandle = <0x3d>; - }; - - cpu@3 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x300>; - cpu-idle-states = <0x04>; - phandle = <0x3e>; - }; - - cpu@4 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x400>; - cpu-idle-states = <0x04>; - phandle = <0x3f>; - }; - - cpu@5 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x500>; - cpu-idle-states = <0x04>; - phandle = <0x40>; - }; - - cpu@6 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x600>; - cpu-idle-states = <0x04>; - phandle = <0x41>; - }; - - cpu@7 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x700>; - cpu-idle-states = <0x04>; - phandle = <0x42>; - }; - - l2-cache0 { - compatible = "cache"; - phandle = <0x03>; - }; - }; - - psci { - compatible = "arm,psci"; - method = "hvc"; - cpu_on = <0xc4000003>; - cpu_off = <0x84000002>; - cpu_suspend = <0xc4000001>; - }; - - misc_clk { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x3d0900>; - phandle = <0x02>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupt-parent = <0x01>; - interrupts = <0x01 0x0d 0xff08 0x01 0x0e 0xff08 0x01 0x0b 0xff08 0x01 0x0a 0xff08>; - }; - - amba_apu@0 { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x00 0x00 0xffffffff>; - - a1000bclkc@33002000 { - compatible = "bst,a1000b-clkc"; - osc-clk-frequency = <0x17d7840>; - rgmii0-rxclk-frequency = <0x7735940>; - rgmii1-rxclk-frequency = <0x7735940>; - ptp_clk-frequency = <0x7735940>; - #clock-cells = <0x01>; - reg = <0x00 0x33002000 0x1000 0x00 0x70035000 0x1000 0x00 0x20020000 0x1000 0x00 0x20021000 0x1000>; - phandle = <0x05>; - }; - - a1000rstc@0x3300217c { - compatible = "bst,a1000b-rstc"; - #reset-cells = <0x01>; - reg = <0x00 0x3300217c 0x04 0x00 0x33002180 0x04 0x00 0x70035008 0x04 0x00 0x20020000 0x04 0x00 0x20021000 0x04>; - phandle = <0x06>; - }; - - interrupt-controller@32000000 { - compatible = "arm,gic-400"; - #interrupt-cells = <0x03>; - interrupt-controller; - reg = <0x00 0x32001000 0x1000 0x00 0x32002000 0x2000 0x00 0x32004000 0x2000 0x00 0x32006000 0x2000>; - interrupt-parent = <0x01>; - interrupts = <0x01 0x09 0xff04>; - phandle = <0x01>; - }; - - a1000noc { - compatible = "bst,a1000-noc"; - echo-args = <0x01>; - noc-arg-num = <0x2a>; - noc-args = <0x3342000c 0x00 0x33420008 0x505 0x3342008c 0x00 0x33420088 0x505 0x3342010c 0x00 0x33420108 0x505 0x3342018c 0x00 0x33420188 0x505 0x3342020c 0x00 0x33420208 0x505 0x3342028c 0x00 0x33420288 0x505 0x3342030c 0x00 0x33420308 0x505 0x3342038c 0x00 0x33420388 0x303 0x3342040c 0x00 0x33420408 0x303 0x3342048c 0x00 0x33420488 0x303 0x3342050c 0x00 0x33420508 0x303 0x3342058c 0x00 0x33420588 0x303 0x3342060c 0x00 0x33420608 0x303 0x3342068c 0x00 0x33420688 0x303 0x3342070c 0x00 0x33420708 0x00 0x3342078c 0x00 0x33420788 0x707 0x3342080c 0x00 0x33420808 0x707 0x3342088c 0x00 0x33420888 0x707 0x3342090c 0x00 0x33420908 0x707 0x3340010c 0x00 0x33400108 0x505 0x3340018c 0x00 0x33400188 0x505>; - }; - - serial@20008000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x20008000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd5 0x04>; - clocks = <0x05 0x54 0x05 0x66>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x1d>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x07>; - }; - - serial@2000a000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x2000a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd6 0x04>; - clocks = <0x05 0x55 0x05 0x67>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x17>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x08>; - }; - - serial@2000b000 { - status = "disable"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x2000b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x05 0x70 0x05 0x7d>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x29>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x09>; - }; - - serial@20009000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x20009000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x05 0x6f 0x05 0x7e>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x25>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x0a>; - }; - - dma-controller@33200000 { - status = "okay"; - compatible = "bst,dw-axi-gdma"; - reg = <0x00 0x33200000 0x1000>; - clocks = <0x05 0x2f 0x05 0x30>; - clock-names = "core-clk\0cfgr-clk"; - resets = <0x06 0x0c>; - reset-names = "gdma_reset"; - dma-channels = <0x08>; - snps,dma-masters = <0x01>; - snps,data-width = <0x04>; - snps,block-size = <0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000>; - snps,priority = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07>; - snps,axi-max-burst-len = <0x10>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x90 0xff04 0x00 0x91 0xff04 0x00 0x92 0xff04 0x00 0x93 0xff04 0x00 0x94 0xff04 0x00 0x95 0xff04 0x00 0x96 0xff04 0x00 0x97 0xff04 0x00 0x98 0xff04>; - support-slave; - }; - - pcie-phy@30E02000 { - reg = <0x00 0x30e02000 0x1000>; - reg-names = "phy-base"; - dmc-lane = <0x01>; - dmc-mode = <0x02>; - pcie-ctl0 = <0x01>; - pcie-ctl1 = <0x01>; - phandle = <0x0b>; - }; - - pcie@30600000 { - status = "disable"; - compatible = "bst,dw-pcie-rc"; - device_type = "pci"; - controller-id = <0x00>; - bus-range = <0x00 0xff>; - linux,pci-domain = <0x00>; - reg = <0x00 0x30600000 0x10000 0x00 0x30700000 0x10000 0x00 0x30900000 0x40000 0x00 0x30980000 0x40000 0x00 0x40000000 0x40000>; - reg-names = "dbi\0dbi2\0atu\0dma\0config"; - num-iatu = <0x04>; - num-viewport = <0x04>; - #address-cells = <0x03>; - #size-cells = <0x02>; - ranges = <0x81000000 0x00 0x41000000 0x00 0x41000000 0x00 0x1000000 0x83000000 0x00 0x42000000 0x00 0x42000000 0x00 0x4000000>; - dma-ranges = <0x3000000 0x00 0x80000000 0x00 0x80000000 0x02 0x00>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc5 0x04 0x00 0xbf 0x04 0x00 0xc0 0x04 0x00 0xc1 0x04 0x00 0xc6 0x04>; - interrupt-names = "sys\0dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - pcie-phy = <0x0b>; - max-link-speed = <0x03>; - num-lanes = <0x02>; - }; - - pcie0_ep@30600000 { - status = "disable"; - compatible = "bst,dw-pcie-ep"; - device_type = "pci"; - controller-id = <0x00>; - bus-range = <0x00 0x04>; - reg = <0x00 0x30600000 0x40000 0x00 0x30700000 0x40000 0x00 0x30900000 0x40000 0x00 0x30980000 0x40000 0x00 0x40000000 0x100000>; - reg-names = "dbi\0dbi2\0atu\0dma\0addr_space"; - num-ib-windows = <0x06>; - num-ob-windows = <0x06>; - max-functions = [04]; - pcie-phy = <0x0b>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc5 0x04 0x00 0xbf 0x04 0x00 0xc0 0x04 0x00 0xc1 0x04 0x00 0xc6 0x04>; - interrupt-names = "sys\0dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - }; - - pcie@30a00000 { - status = "disable"; - compatible = "bst,dw-pcie-ep"; - device_type = "pci"; - controller-id = <0x01>; - bus-range = <0x00 0x04>; - reg = <0x00 0x30a00000 0x40000 0x00 0x30b00000 0x40000 0x00 0x30d00000 0x40000 0x00 0x30d80000 0x40000 0x00 0x48000000 0x1000000>; - reg-names = "dbi\0dbi2\0atu\0dma\0addr_space"; - num-ib-windows = <0x06>; - num-ob-windows = <0x06>; - max-functions = [04]; - pcie-phy = <0x0b>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc2 0x04 0x00 0xc3 0x04 0x00 0xc4 0x04 0x00 0xc7 0x04>; - interrupt-names = "dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - max-link-speed = <0x03>; - num-lanes = <0x02>; - picp-ctl = "dma"; - ob-memaddr-def = <0x0c>; - }; - - pcie_vnet@0 { - status = "disable"; - compatible = "bst,pcie-vnet"; - vnet-id = <0x00>; - dma-chan-num = <0x01>; - rx_queues = <0x01>; - tx_queues = <0x01>; - tx-fifo-depth = <0x100>; - rx-fifo-depth = <0x100>; - extend-op = <0x11>; - memory-region = <0x0c>; - }; - - pcie_vnet@1 { - status = "disable"; - compatible = "bst,pcie-vnet"; - vnet-id = <0x01>; - dma-chan-num = <0x01>; - rx_queues = <0x01>; - tx_queues = <0x01>; - tx-fifo-depth = <0x100>; - rx-fifo-depth = <0x100>; - }; - - - gpio@20010000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,dw-apb-gpio"; - reg = <0x00 0x20010000 0x1000>; - clocks = <0x05 0x62>; - clock-names = "bus"; - resets = <0x06 0x1c>; - resets-names = "gpio0_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c>; - - gpio-controller@0 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x00>; - chipnum-base = <0x00>; - interrupt-controller; - #interrupt-cells = <0x03>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdf 0x04 0x00 0xe3 0x04 0x00 0xe4 0x04 0x00 0xe5 0x04 0x00 0xe6 0x04 0x00 0xe7 0x04 0x00 0xe8 0x04 0x00 0xe9 0x04 0x00 0xea 0x04>; - phandle = <0x20>; - }; - - gpio-controller@1 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x01>; - chipnum-base = <0x20>; - phandle = <0x4b>; - }; - - gpio-controller@2 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x02>; - chipnum-base = <0x40>; - phandle = <0x48>; - }; - - gpio-controller@3 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x03>; - chipnum-base = <0x60>; - phandle = <0x55>; - }; - }; - - gpio@20011000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,dw-apb-gpio"; - reg = <0x00 0x20011000 0x1000>; - clocks = <0x05 0x82>; - clock-names = "bus"; - resets = <0x06 0x27>; - resets-names = "gpio1_reset"; - - gpio-controller@0 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x00>; - chipnum-base = <0x80>; - interrupt-controller; - #interrupt-cells = <0x03>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe0 0x04>; - }; - - gpio-controller@1 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x01>; - chipnum-base = <0xa0>; - }; - - gpio-controller@2 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x02>; - chipnum-base = <0xc0>; - }; - - gpio-controller@3 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x03>; - chipnum-base = <0xe0>; - phandle = <0x0d>; - }; - }; - - watchdog@2001a000 { - status = "disable"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5d 0x04>; - clocks = <0x05 0x5a 0x05 0x64>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x1f>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt0"; - }; - - watchdog@2001b000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5e 0x04>; - clocks = <0x05 0x5b 0x05 0x65>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x20>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt1"; - }; - - watchdog@2001c000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001c000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5f 0x04>; - clocks = <0x05 0x74 0x05 0x7b>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x2e>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt2"; - }; - - watchdog@2001d000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001d000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x60 0x04>; - clocks = <0x05 0x75 0x05 0x7c>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x2f>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt3"; - }; - - watchdog@32009000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x32009000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x43 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt0"; - }; - - watchdog@3200a000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x44 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt1"; - }; - - watchdog@3200b000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x45 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt2"; - }; - - watchdog@3200c000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200c000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x46 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt3"; - }; - - watchdog@3200d000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200d000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x47 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt4"; - }; - - watchdog@3200e000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200e000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x48 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt5"; - }; - - watchdog@3200f000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200f000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x49 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt6"; - }; - - watchdog@32010000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x32010000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4a 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt7"; - }; - - i2c@20000000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20000000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xcf 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x18>; - reset-names = "i2c0_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1d>; - }; - - i2c@20001000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20001000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd0 0x04>; - clock-frequency = <0xf4240>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x19>; - reset-names = "i2c1_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1e>; - }; - - i2c@20002000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20002000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd1 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x1a>; - reset-names = "i2c2_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1f>; - - max96712@2a { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-lis"; - reg = <0x2a>; - i2c-port = <0x01>; - csi2-port = <0x01>; - lane-speed = <0x960>; - regs = <0x40>; - data-type = <0x2d>; - trigger-mode = <0x01>; - trigger-fps = <0x14>; - trigger-rx-gpio = <0x02>; - trigger-tx-gpio = <0x08>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x06 0x06 0x06 0x06>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x20 0x14 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - - endpoint { - remote-endpoint = <0x21>; - phandle = <0x72>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x22>; - phandle = <0x2e>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x23>; - phandle = <0x2f>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x24>; - phandle = <0x30>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x25>; - phandle = <0x31>; - }; - }; - }; - }; - - max96712@6b { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-ctl"; - reg = <0x6b>; - i2c-port = <0x00>; - csi2-port = <0x00>; - lane-speed = <0x640>; - regs = <0x44>; - data-type = <0x1e>; - trigger-mode = <0x01>; - trigger-fps = <0x1e>; - trigger-rx-gpio = <0x01>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x03 0x03 0x03 0x03>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x20 0x15 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - - endpoint { - remote-endpoint = <0x26>; - phandle = <0x77>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x27>; - phandle = <0x32>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x28>; - phandle = <0x33>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x29>; - phandle = <0x34>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x2a>; - phandle = <0x35>; - }; - }; - }; - }; - - max96712@29 { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-lis"; - reg = <0x29>; - i2c-port = <0x01>; - csi2-port = <0x01>; - lane-speed = <0x960>; - regs = <0x40>; - data-type = <0x2d>; - trigger-mode = <0x01>; - trigger-fps = <0x14>; - trigger-rx-gpio = <0x02>; - trigger-tx-gpio = <0x08>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x06 0x06 0x06 0x06>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x20 0x17 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - - endpoint { - remote-endpoint = <0x2b>; - phandle = <0x7c>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x2c>; - phandle = <0x36>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x2d>; - phandle = <0x37>; - }; - }; - }; - }; - - camera@70 { - reg = <0x70>; - ser-alias-id = <0x60>; - sensor-alias-id = <0x70>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x2e>; - phandle = <0x22>; - }; - }; - }; - - camera71 { - status = "disabled"; - reg = <0x71>; - ser-alias-id = <0x61>; - sensor-alias-id = <0x71>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x2f>; - phandle = <0x23>; - }; - }; - }; - - camera@72 { - status = "disabled"; - reg = <0x72>; - ser-alias-id = <0x62>; - sensor-alias-id = <0x72>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x30>; - phandle = <0x24>; - }; - }; - }; - - camera@73 { - status = "disabled"; - reg = <0x73>; - ser-alias-id = <0x63>; - sensor-alias-id = <0x73>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x31>; - phandle = <0x25>; - }; - }; - }; - - camera@54 { - reg = <0x54>; - ser-alias-id = <0x64>; - sensor-alias-id = <0x54>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x32>; - phandle = <0x27>; - }; - }; - }; - - camera@55 { - reg = <0x55>; - ser-alias-id = <0x65>; - sensor-alias-id = <0x55>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x33>; - phandle = <0x28>; - }; - }; - }; - - camera@56 { - reg = <0x56>; - ser-alias-id = <0x66>; - sensor-alias-id = <0x56>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x34>; - phandle = <0x29>; - }; - }; - }; - - camera@57 { - reg = <0x57>; - ser-alias-id = <0x67>; - sensor-alias-id = <0x57>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x35>; - phandle = <0x2a>; - }; - }; - }; - - camera@58 { - reg = <0x58>; - ser-alias-id = <0x48>; - sensor-alias-id = <0x58>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x36>; - phandle = <0x2c>; - }; - }; - }; - - camera@59 { - status = "disabled"; - reg = <0x59>; - ser-alias-id = <0x49>; - sensor-alias-id = <0x59>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x37>; - phandle = <0x2d>; - }; - }; - }; - }; - - i2c@20003000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20003000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd2 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x2c>; - reset-names = "i2c3_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x38>; - }; - - i2c@20004000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20004000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd3 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x2d>; - reset-names = "i2c4_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x39>; - - eeprom@56 { - compatible = "atmel,24c02"; - reg = <0x56>; - pagesize = <0x10>; - }; - - lt9211@2d { - compatible = "bst,lt9211"; - reg = <0x2d>; - }; - }; - - i2c@20005000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20005000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd4 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x26>; - reset-names = "i2c5_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x3a>; - }; - - ddr_ecc { - status = "okay"; - compatible = "bst,a1000_ddr_ecc"; - reg = <0x00 0x38000000 0x1400 0x00 0x3c000000 0x1400 0x00 0x33000000 0x140>; - reg-names = "ddr0\0ddr1\0a55_ctrl"; - interrupt-parent = <0x01>; - interrupts = <0x00 0x8b 0x04 0x00 0x8d 0x04>; - interrupt-names = "ddr0_ecc_irq\0ddr1_ecc_irq"; - mbox-names = "bstn-mbox"; - }; - - arm_pmu { - status = "okay"; - compatible = "arm,armv8-pmuv3"; - interrupt-parent = <0x01>; - interrupts = <0x00 0x38 0xff04 0x00 0x39 0xff04 0x00 0x3a 0xff04 0x00 0x3b 0xff04 0x00 0x3c 0xff04 0x00 0x3d 0xff04 0x00 0x3e 0xff04 0x00 0x3f 0xff04>; - interrupt-affinity = <0x3b 0x3c 0x3d 0x3e 0x3f 0x40 0x41 0x42>; - }; - - noc_pmu@0x32702000 { - status = "okay"; - compatible = "bst,bst_noc_pmu"; - reg = <0x00 0x32702000 0x1000 0x00 0x32703000 0x1000 0x00 0x32704000 0x2000 0x00 0x32708000 0x1000 0x00 0x33402000 0x2400 0x00 0x33422000 0x4400 0x00 0x33401100 0x10 0x00 0x33421480 0x10>; - reg-names = "coresight_cpunoc_etf\0coresight_etr\0coresight_funnel\0coresight_corenoc_etf\0cpu_port_set\0core_port_set\0cpunoc_atb\0corenoc_atb"; - memory-region = <0x43>; - }; - - lsp0_pwm0@20012000 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x56 0x05 0x6b>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20012000 0x14 0x00 0x200120b0 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x44>; - }; - - lsp0_pwm1@20012014 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x57 0x05 0x6b>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20012014 0x14 0x00 0x200120b4 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x45>; - }; - - lsp1_pwm0@20013000 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x71 0x05 0x7a>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20013000 0x14 0x00 0x200130b0 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x46>; - }; - - lsp1_pwm1@20013014 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x72 0x05 0x7a>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20013014 0x14 0x00 0x200130b4 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x47>; - }; - - a55_timer0@32008000 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4b 0x04>; - reg = <0x00 0x32008000 0x14>; - }; - - a55_timer1@32008014 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4c 0x04>; - reg = <0x00 0x32008014 0x14>; - }; - - a55_timer2@32008028 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4d 0x04>; - reg = <0x00 0x32008028 0x14>; - }; - - a55_timer3@3200803c { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4e 0x04>; - reg = <0x00 0x3200803c 0x14>; - }; - - a55_timer4@32008050 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4f 0x04>; - reg = <0x00 0x32008050 0x14>; - }; - - a55_timer5@32008064 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x50 0x04>; - reg = <0x00 0x32008064 0x14>; - }; - - a55_timer6@32008078 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x51 0x04>; - reg = <0x00 0x32008078 0x14>; - }; - - a55_timer7@3200808c { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x52 0x04>; - reg = <0x00 0x3200808c 0x14>; - }; - - spi@2000c000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000c000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x00>; - cs-gpios = <0x48 0x02 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x49>; - }; - - spi@2000d000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000d000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdc 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x01>; - cs-gpios = <0x48 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4a>; - }; - - spi@2000c800 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000c800 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf4 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x02>; - cs-gpios = <0x4b 0x08 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4c 0x4d>; - }; - - spi@2000d800 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000d800 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf5 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x03>; - cs-gpios = <0x4b 0x10 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4e 0x4f>; - }; - - spi@20022000 { - status = "okay"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x20022000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe2 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x04>; - cs-gpios = <0x48 0x02 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x50>; - spi-slave; - - slave@0 { - compatible = "rohm,dh2228fv"; - reg = <0x00>; - spi-max-frequency = <0x989680>; - }; - }; - - spi@20023000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x20023000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe1 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x05>; - cs-gpios = <0x48 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x51>; - }; - - i2s-play@2000e000 { - status = "disable"; - compatible = "snps,designware-i2s"; - reg = <0x00 0x2000e000 0x1000>; - interrupt-names = "play_irq"; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdd 0x04>; - clocks = <0x05 0x61 0x05 0x59>; - clock-names = "i2spclk\0i2sclk"; - play; - channel = <0x02>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x52>; - }; - - i2s-rec@2000f000 { - status = "disable"; - compatible = "snps,designware-i2s"; - reg = <0x00 0x2000f000 0x1000>; - interrupt-names = "record_irq"; - interrupt-parent = <0x01>; - interrupts = <0x00 0xde 0x04>; - clocks = <0x02>; - record; - channel = <0x02>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x53>; - }; - - qspi@00000000 { - status = "okay"; - compatible = "snps,dwc-ssi-1.01a"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x00 0x4000000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd9 0x04>; - clocks = <0x05 0x4d 0x05 0x4e>; - clock-names = "hclk\0wclk"; - work-mode = <0x01>; - reg-io-width = <0x04>; - bst,use-gpio-cs; - spi-rx-bus-width = <0x04>; - spi-tx-bus-width = <0x04>; - cs-gpios = <0x48 0x15 0x00>; - num-cs = <0x01>; - bus-num = <0x06>; - pinctrl-names = "default"; - pinctrl-0 = <0x54>; - - qspi0-nor0@0 { - #address-cells = <0x01>; - #size-cells = <0x01>; - spi-rx-bus-width = <0x01>; - spi-tx-bus-width = <0x01>; - compatible = "jedec,spi-nor"; - status = "okay"; - spi-max-frequency = <0xf4240>; - reg = <0x00>; - mode = <0x00>; - powerctl-fad-gpios = <0x20 0x1c 0x00>; - - partition@0 { - reg = <0x00 0x1e00000>; - label = "nor0_part0"; - }; - - partition@1e00000 { - reg = <0x1e00000 0x200000>; - label = "nor0_part1"; - }; - }; - }; - - qspi@14000000 { - status = "disable"; - compatible = "snps,dwc-ssi-1.01a"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x14000000 0x4000000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x05 0x4f 0x05 0x50>; - clock-names = "hclk\0wclk"; - work-mode = <0x01>; - reg-io-width = <0x04>; - bst,use-gpio-cs; - spi-rx-bus-width = <0x04>; - spi-tx-bus-width = <0x04>; - cs-gpios = <0x55 0x01 0x00>; - num-cs = <0x01>; - bus-num = <0x07>; - pinctrl-names = "default"; - pinctrl-0 = <0x56>; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x0f>; - snps,rd_osr_lmt = <0x0f>; - snps,axi_fb; - snps,blen = <0x04 0x08 0x10 0x00 0x00 0x00 0x00>; - phandle = <0x57>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x04>; - snps,rx-sched-sp; - phandle = <0x58>; - - queue0 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x00>; - snps,priority = <0x00>; - }; - - queue1 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x01>; - snps,priority = <0x01>; - }; - - queue2 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x02>; - snps,priority = <0x02>; - }; - - queue3 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x03>; - snps,priority = <0x03>; - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x04>; - snps,tx-sched-wrr; - phandle = <0x59>; - - queue0 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x00>; - }; - - queue1 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x01>; - }; - - queue2 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x02>; - }; - - queue3 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x03>; - }; - }; - - thermal@70039000 { - status = "okay"; - compatible = "bst,bst-thermal"; - reg = <0x00 0x70039000 0x1000>; - #thermal-sensor-cells = <0x00>; - phandle = <0x62>; - }; - - ethernet@30000000 { - status = "okay"; - compatible = "bst,dw-eqos-eth"; - reg = <0x00 0x30000000 0x100000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9f 0xff04 0x00 0xa0 0xff04 0x00 0xa1 0xff04 0x00 0xa2 0xff04 0x00 0xa3 0xff04 0x00 0xa4 0xff04 0x00 0xa5 0xff04 0x00 0xa6 0xff04 0x00 0xa7 0xff04 0x00 0xa8 0xff04 0x00 0xa9 0xff04 0x00 0xaa 0xff04>; - interrupt-names = "sbd_irq\0sfty_ce_irq\0sfty_ue_irq\0tx_chan0_irq\0tx_chan1_irq\0tx_chan2_irq\0tx_chan3_irq\0rx_chan0_irq\0rx_chan1_irq\0rx_chan2_irq\0rx_chan3_irq\0eth_lpi"; - ethernet-id = <0x00>; - mac-address = [00 00 00 00 00 00]; - max-frame-size = <0xed8>; - phy-mode = "rgmii"; - snps,multicast-filter-bins = <0x100>; - snps,perfect-filter-entries = <0x80>; - rx-fifo-depth = <0x4000>; - tx-fifo-depth = <0x4000>; - clocks = <0x05 0x0f 0x05 0x13 0x05 0x15 0x05 0x11>; - clock-names = "wclk\0axim_aclk\0pclk\0ptp_ref"; - bst,fix-safety = <0x00>; - bst,dma_int_mode = <0x01>; - snps,fixed-burst; - snps,force_sf_dma_mode; - snps,ps-speed = <0x3e8>; - snps,axi-config = <0x57>; - snps,mtl-rx-config = <0x58>; - snps,mtl-tx-config = <0x59>; - label = "gmac0"; - resets = <0x06 0x11>; - reset-names = "bstgmaceth"; - eth-name = "gmac0"; - eth-number = <0x00>; - mac-mode = "rgmii"; - extend-op = <0x03>; - pinctrl-names = "default"; - pinctrl-0 = <0x5a>; - - fixed-link { - speed = <0x3e8>; - full-duplex; - }; - }; - - ethernet@30100000 { - status = "disable"; - compatible = "bst,dw-eqos-eth"; - reg = <0x00 0x30100000 0x100000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xab 0xff04 0x00 0xac 0xff04 0x00 0xad 0xff04 0x00 0xae 0xff04 0x00 0xaf 0xff04 0x00 0xb0 0xff04 0x00 0xb1 0xff04 0x00 0xb2 0xff04 0x00 0xb3 0xff04 0x00 0xb4 0xff04 0x00 0xb5 0xff04 0x00 0xb6 0xff04>; - interrupt-names = "sbd_irq\0sfty_ce_irq\0sfty_ue_irq\0tx_chan0_irq\0tx_chan1_irq\0tx_chan2_irq\0tx_chan3_irq\0rx_chan0_irq\0rx_chan1_irq\0rx_chan2_irq\0rx_chan3_irq\0eth_lpi"; - ethernet-id = <0x01>; - mac-address = [00 00 00 00 00 00]; - max-frame-size = <0xed8>; - phy-mode = "rgmii"; - snps,multicast-filter-bins = <0x100>; - snps,perfect-filter-entries = <0x80>; - rx-fifo-depth = <0x4000>; - tx-fifo-depth = <0x4000>; - clocks = <0x05 0x10 0x05 0x14 0x05 0x16 0x05 0x12>; - clock-names = "wclk\0axim_aclk\0pclk\0ptp_ref"; - bst,fix-safety = <0x00>; - bst,dma_int_mode = <0x01>; - snps,fixed-burst; - snps,force_sf_dma_mode; - snps,ps-speed = <0x3e8>; - snps,axi-config = <0x57>; - snps,mtl-rx-config = <0x58>; - snps,mtl-tx-config = <0x59>; - label = "gmac1"; - resets = <0x06 0x12>; - reset-names = "bstgmaceth"; - pinctrl-names = "default"; - pinctrl-0 = <0x5b>; - extend-op = <0x03>; - eth-name = "gmac1"; - eth-number = <0x01>; - mac-mode = "rgmii"; - phy-handle = <0x5c>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - eth_phy1@1 { - compatible = "marvell,88Q2122\0ethernet-phy-id002B.0983\0ethernet-phy-ieee802.3-c45"; - device_type = "ethernet-phy"; - max-speed = <0x3e8>; - reg = <0x01>; - reset-gpios = <0x20 0x09 0x01>; - reset-active-low; - reset-assert-us = <0x4e20>; - reset-deassert-us = <0x4e20>; - phandle = <0x5c>; - }; - }; - }; - - phy@30E01000 { - compatible = "bst,dwc-usb-phy"; - #phy-cells = <0x00>; - reg = <0x00 0x30e01000 0x1000>; - usb_mode = "usb20"; - phandle = <0x5f>; - }; - - phy@30E00000 { - compatible = "bst,dwc-usb-phy"; - reg = <0x00 0x30e00000 0x1000>; - #phy-cells = <0x00>; - usb_mode = "usb30"; - pll_type = "internal"; - phandle = <0x5d>; - }; - - usb3 { - compatible = "bst,dwc3usb"; - status = "okay"; - ranges; - #address-cells = <0x02>; - #size-cells = <0x01>; - clock-names = "suspend\0ref\0axi\0apb"; - clocks = <0x05 0x17 0x05 0x18 0x05 0x19 0x05 0x1a>; - resets = <0x06 0x04>; - reset-names = "usb3_reset"; - phys = <0x5d>; - phy-names = "usb-phy"; - pll_type = "internal"; - powerctl-gpios = <0x20 0x0e 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x5e>; - - dwc3@30200000 { - compatible = "snps,dwc3"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - reg = <0x00 0x30200000 0x100000>; - interrupts = <0x00 0xc8 0x04>; - interrupt-parent = <0x01>; - dr_mode = "host"; - snps,dis_u3_susphy_quirk; - }; - }; - - usb2 { - compatible = "bst,dwc3usb"; - status = "okay"; - ranges; - #address-cells = <0x02>; - #size-cells = <0x01>; - clock-names = "ahb\0ref\0apb"; - clocks = <0x05 0x1b 0x05 0x1d 0x05 0x1c>; - reset-names = "usb2_reset"; - resets = <0x06 0x05>; - phys = <0x5f>; - phy-names = "usb-phy"; - pll_type = "internal"; - - dwc3@30300000 { - status = "okay"; - compatible = "snps,dwc3"; - reg = <0x00 0x30300000 0x100000>; - interrupts = <0x00 0xc9 0x04>; - interrupt-parent = <0x01>; - dr_mode = "peripheral"; - snps,incr-burst-type-adjustment = <0x01 0x04 0x08 0x10>; - snps,reqinfo-for-data-read = <0x08>; - snps,reqinfo-for-descriptor-read = <0x08>; - }; - }; - - dwmmc0@30400000 { - status = "okay"; - compatible = "bst,dwcmshc-sdhci"; - clocks = <0x05 0x1f 0x05 0x1e>; - clock-names = "core\0bus"; - reg = <0x00 0x30400000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xb9 0x04>; - interrupt-names = "IRQDWMMC0"; - #address-cells = <0x01>; - #size-cells = <0x00>; - data-addr = <0x200>; - fifo-watermark-aligned; - clock-frequency = <0x2faf080>; - max-frequency = <0xbebc200>; - min-frequency = <0x61a80>; - broken-64bit-dma; - clear-tarns-mode; - fifo-depth = <0x400>; - card-detect-delay = <0xc8>; - bus-width = <0x08>; - cap-mmc-highspeed; - non-removable; - no-sdio; - no-sd; - keep-power-in-suspend; - no-3-3-v; - sdhci,auto-cmd12; - pinctrl-names = "default"; - pinctrl-0 = <0x60>; - }; - - dwmmc1@30500000 { - status = "okay"; - compatible = "bst,dwcmshc-sdhci"; - clocks = <0x05 0x21 0x05 0x20>; - clock-names = "core\0bus"; - reg = <0x00 0x30500000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xbd 0x04>; - interrupt-names = "IRQDWMMC1"; - #address-cells = <0x01>; - #size-cells = <0x00>; - data-addr = <0x200>; - fifo-watermark-aligned; - clock-frequency = <0x5f5e100>; - max-frequency = <0xbebc200>; - min-frequency = <0x61a80>; - broken-64bit-dma; - clear-tarns-mode; - fifo-depth = <0x400>; - card-detect-delay = <0xc8>; - bus-width = <0x04>; - cap-sd-highspeed; - sd-uhs-sdr12; - sd-uhs-sdr25; - sd-uhs-sdr50; - no-sdio; - no-mmc; - sdhci,auto-cmd12; - keep-power-in-suspend; - pinctrl-names = "default"; - pinctrl-0 = <0x61>; - }; - - gpu@33300000 { - status = "okay"; - compatible = "arm,mali-450\0arm,mali-utgard"; - reg = <0x00 0x33300000 0x30000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04>; - interrupt-names = "IRQPP0\0IRQPPMMU0\0IRQPP1\0IRQPPMMU1\0IRQGP\0IRQGPMMU\0IRQPMU\0IRQPP"; - clocks = <0x05 0x31 0x05 0x32>; - clock-names = "clk_mali\0clk_mali_apb"; - resets = <0x06 0x0b>; - reset-names = "gpu_reset"; - ppcores = <0x02>; - dedicated_mem_start = <0x00>; - dedicated_mem_size = <0x00>; - }; - - mali-v500@0x55000000 { - status = "okay"; - compatible = "arm,mali-v500"; - reg = <0x00 0x55000000 0xffff>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9a 0x04>; - interrupt-names = "IRQV500"; - resets = <0x06 0x00>; - reset-names = "codec_reset"; - clocks = <0x05 0x3d>; - clock-names = "clk_v500"; - }; - }; - - cooling_dev { - - pwm { - cpumask = <0x0f>; - capacitance = <0x5dc>; - #cooling-cells = <0x02>; - phandle = <0x64>; - }; - }; - - thermal-zones { - - cpu-thermal { - polling-delay-passive = <0x1f4>; - polling-delay = <0x3e8>; - thermal-sensors = <0x62>; - - trips { - - switch_trip { - temperature = <0x15f90>; - hysteresis = <0x7d0>; - type = "passive"; - phandle = <0x63>; - }; - - critical_trip { - temperature = <0x1e848>; - hysteresis = <0x00>; - type = "critical"; - }; - }; - - cooling-maps { - - map0 { - trip = <0x63>; - cooling-device = <0x64 0x00 0x01>; - }; - }; - }; - }; - - pinctrl@70038000 { - status = "okay"; - compatible = "bst,pinctrl-a1000b"; - #address-cells = <0x02>; - #size-cells = <0x02>; - reg = <0x00 0x70038000 0x00 0x1000 0x00 0x33001000 0x00 0x1000>; - reg-names = "aon\0top"; - #gpio-cells = <0x02>; - - spi0_pinctrl { - phandle = <0x49>; - - mux { - pins = "spi0_miso\0spi0_mosi\0spi0_sclk"; - function = "spi0"; - }; - }; - - spi1_pinctrl { - phandle = <0x4a>; - - mux { - pins = "spi1_miso\0spi1_mosi\0spi1_sclk"; - function = "spi1"; - }; - }; - - spi2_pinctrl { - phandle = <0x4c>; - - mux { - pins = "uart0_cts\0uart1_cts\0uart1_rts"; - function = "spi2"; - }; - }; - - spi2_cs_pinctrl { - phandle = <0x4d>; - - mux { - pins = "uart0_rts"; - function = "gpio"; - }; - }; - - spi3_pinctrl { - phandle = <0x4e>; - - mux { - pins = "uart2_cts\0uart3_cts\0uart3_rts"; - function = "spi3"; - }; - }; - - spi3_cs_pinctrl { - phandle = <0x4f>; - - mux { - pins = "uart2_rts"; - function = "gpio"; - }; - }; - - spi4_pinctrl { - phandle = <0x50>; - - mux { - pins = "spi0_miso\0spi0_mosi\0spi0_sclk\0spi0_cs"; - function = "spi0_s"; - }; - }; - - spi5_pinctrl { - phandle = <0x51>; - - mux { - pins = "spi1_miso\0spi1_mosi\0spi1_sclk"; - function = "spi1_s"; - }; - }; - - i2c0_pinctrl { - phandle = <0x1d>; - - mux { - pins = "i2c0_scl\0i2c0_sda"; - function = "i2c0"; - }; - }; - - i2c1_pinctrl { - phandle = <0x1e>; - - mux { - pins = "i2c1_scl\0i2c1_sda"; - function = "i2c1"; - }; - }; - - i2c2_pinctrl { - phandle = <0x1f>; - - mux { - pins = "i2c2_scl\0i2c2_sda"; - function = "i2c2"; - }; - }; - - i2c3_pinctrl { - phandle = <0x38>; - - mux { - pins = "i2c3_scl\0i2c3_sda"; - function = "i2c3"; - }; - }; - - i2c4_pinctrl { - phandle = <0x39>; - - mux { - pins = "i2c4_scl\0i2c4_sda"; - function = "i2c4"; - }; - }; - - i2c5_pinctrl { - phandle = <0x3a>; - - mux { - pins = "i2c5_scl\0i2c5_sda"; - function = "i2c5"; - }; - }; - - uart0_pinctrl { - phandle = <0x07>; - - mux { - pins = "uart0_txd\0uart0_rxd"; - function = "uart0"; - }; - }; - - uart1_pinctrl { - phandle = <0x08>; - - mux { - pins = "uart1_txd\0uart1_rxd"; - function = "uart1"; - }; - }; - - uart2_pinctrl { - phandle = <0x09>; - - mux { - pins = "uart2_txd\0uart2_rxd"; - function = "uart2"; - }; - }; - - uart3_pinctrl { - phandle = <0x0a>; - - mux { - pins = "uart3_txd\0uart3_rxd"; - function = "uart3"; - }; - }; - - gpio0_pinctrl { - - mux { - pins = "gpio_29"; - function = "gpio"; - }; - }; - - gpio_special_func_pinctrl { - - mux { - pins = "gpio_24\0gpio_29\0debug4\0debug5\0uart0_cts\0uart0_rts"; - function = "gpio"; - }; - }; - - qspi0_pinctrl { - phandle = <0x54>; - - mux { - pins = "qspi0_cs0"; - function = "gpio"; - }; - }; - - usb3_pinctrl { - phandle = <0x5e>; - - mux { - pins = "gpio_14"; - function = "gpio"; - }; - }; - - qspi1_pinctrl { - phandle = <0x56>; - - mux { - pins = "qspi1_cs1"; - function = "gpio"; - }; - }; - - des_960_1_pinctrl { - - mux { - pins = "qspi1_io1"; - function = "gpio"; - }; - }; - - des_960_2_pinctrl { - - mux { - pins = "qspi1_io3"; - function = "gpio"; - }; - }; - - des_960_3_pinctrl { - - mux { - pins = "qspi1_io5"; - function = "gpio"; - }; - }; - - bist_pinctrl { - - mux { - pins = "spi1_mosi"; - function = "bist"; - }; - }; - - - - i2s0_pinctrl { - phandle = <0x52>; - - mux { - pins = "i2s0_ck\0i2s0_mck\0i2s0_sd_out\0i2s0_ws\0qspi0_io4"; - function = "i2s0"; - }; - }; - - i2s1_pinctrl { - phandle = <0x53>; - - mux { - pins = "i2s1_ck\0i2s1_sd_in\0i2s1_ws"; - function = "i2s1"; - }; - }; - - isp_pinctrl { - phandle = <0x67>; - - mux { - pins = "isp_fsync0\0isp_fsync1\0isp_fsync2\0isp_fsync3"; - function = "isp"; - }; - }; - - ptp_pinctrl { - - mux { - pins = "ptp_pps0\0ptp_pps1\0ptp_clk"; - function = "ptp"; - }; - }; - - ptp_pinconfig { - - config { - pins = "ptp_clk"; - drive-strength = <0x02>; - input-enable; - }; - }; - - err_rpt_l0_pinctrl { - - mux { - pins = "err_rpt_l0_n\0err_rpt_l0_p"; - function = "err_rpt_l0"; - }; - }; - - err_rpt_gpio_l0_pinctrl { - - mux { - pins = "err_rpt_l0_n\0err_rpt_l0_p"; - function = "gpio"; - }; - }; - - err_rpt_l1_pinctrl { - - mux { - pins = "err_rpt_l1_n\0err_rpt_l1_p"; - function = "err_rpt_l1"; - }; - }; - - pwm_lsp0_pwm0_pinctrl { - phandle = <0x44>; - - mux { - pins = "pwm0"; - function = "pwm"; - }; - }; - - pwm_lsp0_pwm1_pinctrl { - phandle = <0x45>; - - mux { - pins = "pwm1"; - function = "pwm"; - }; - }; - - pwm_lsp1_pwm0_pinctrl { - phandle = <0x46>; - - mux { - pins = "pwm2"; - function = "pwm"; - }; - }; - - pwm_lsp1_pwm1_pinctrl { - phandle = <0x47>; - - mux { - pins = "pwm3"; - function = "pwm"; - }; - }; - - ts_pinctrl { - - mux { - pins = "ts_trig_in00\0ts_trig_in01\0ts_trig_in10\0ts_trig_in11"; - function = "ts"; - }; - }; - - sdemmc0_pinctrl { - phandle = <0x15>; - - mux { - pins = "sdemmc0_clk\0sdemmc0_cmd\0sdemmc0_dat0\0sdemmc0_dat1\0sdemmc0_dat2\0sdemmc0_dat3\0sdemmc0_dat4\0sdemmc0_dat5\0sdemmc0_dat6\0sdemmc0_dat7\0sdemmc0_rstb\0sdemmc0_cdn\0sdemmc0_wp"; - function = "sdemmc0"; - }; - }; - - sdemmc0_pinconfig { - phandle = <0x60>; - - config { - pins = "sdemmc0_clk\0sdemmc0_cmd\0sdemmc0_dat0\0sdemmc0_dat1\0sdemmc0_dat2\0sdemmc0_dat3\0sdemmc0_dat4\0sdemmc0_dat5\0sdemmc0_dat6\0sdemmc0_dat7\0sdemmc0_rstb\0sdemmc0_cdn\0sdemmc0_wp"; - drive-strength = <0x02>; - input-enable; - }; - }; - - sdemmc1_pinctrl { - phandle = <0x16>; - - mux { - pins = "sdemmc1_clk\0sdemmc1_cmd\0sdemmc1_dat0\0sdemmc1_dat1\0sdemmc1_dat2\0sdemmc1_dat3\0sdemmc1_dat4\0sdemmc1_dat5\0sdemmc1_dat6\0sdemmc1_dat7\0sdemmc1_rstb\0sdemmc1_cdn\0sdemmc1_wp"; - function = "sdemmc1"; - }; - }; - - sdemmc1_pinconfig { - phandle = <0x61>; - - config { - pins = "sdemmc1_clk\0sdemmc1_cmd\0sdemmc1_dat0\0sdemmc1_dat1\0sdemmc1_dat2\0sdemmc1_dat3\0sdemmc1_dat4\0sdemmc1_dat5\0sdemmc1_dat6\0sdemmc1_dat7\0sdemmc1_rstb\0sdemmc1_cdn\0sdemmc1_wp"; - drive-strength = <0x0f>; - input-enable; - }; - }; - - debug_pinctrl { - phandle = <0x17>; - - mux { - pins = "debug0\0debug1\0debug2\0debug3\0debug4\0debug5\0debug6\0debug7"; - function = "debug"; - }; - }; - - strap_pinctrl { - - mux { - pins = "gpio_24\0gpio_25\0gpio_26\0gpio_27\0gpio_28\0gpio_29\0spi1_sclk\0i2s0_mck\0i2s0_ck\0gpio_107\0gpio_108"; - function = "strap"; - }; - }; - - vout_pinctrl { - phandle = <0x18>; - - mux { - pins = "vout_r0\0vout_r1\0vout_r2\0vout_r3\0vout_r4\0vout_r5\0vout_r6\0vout_r7\0vout_g0\0vout_g1\0vout_g2\0vout_g3\0vout_g4\0vout_g5\0vout_g6\0vout_g7\0vout_b0\0vout_b1\0vout_b2\0vout_b3\0vout_b4\0vout_b5\0vout_b6\0vout_b7\0vout_hs\0vout_vs\0vout_de\0vout_pclk\0vout_pdb"; - function = "vout"; - }; - }; - - vout_pinconfig { - - config { - pins = "vout_r0\0vout_r1\0vout_r2\0vout_r3\0vout_r4\0vout_r5\0vout_r6\0vout_r7\0vout_g0\0vout_g1\0vout_g2\0vout_g3\0vout_g4\0vout_g5\0vout_g6\0vout_g7\0vout_b0\0vout_b1\0vout_b2\0vout_b3\0vout_b4\0vout_b5\0vout_b6\0vout_b7\0vout_hs\0vout_vs\0vout_de\0vout_pclk\0vout_pdb"; - drive-strength = <0x02>; - input-enable; - }; - }; - - vin_pinctrl { - phandle = <0x19>; - - mux { - pins = "vin_b0\0vin_b1\0vin_b2\0vin_b3\0vin_b4\0vin_de\0vin_g0\0vin_g1\0vin_g2\0vin_g3\0vin_g4\0vin_g5\0vin_hs\0vin_llc\0vin_r0\0vin_r1\0vin_r2\0vin_r3\0vin_r4\0vin_vs"; - function = "vin"; - }; - }; - - vin_pinconfig { - - config { - pins = "vin_b0\0vin_b1\0vin_b2\0vin_b3\0vin_b4\0vin_de\0vin_g0\0vin_g1\0vin_g2\0vin_g3\0vin_g4\0vin_g5\0vin_hs\0vin_llc\0vin_r0\0vin_r1\0vin_r2\0vin_r3\0vin_r4\0vin_vs"; - drive-strength = <0x02>; - input-enable; - }; - }; - - rgmii0_pinctrl { - phandle = <0x5a>; - - mux { - pins = "rgmii0_txd0\0rgmii0_txd1\0rgmii0_txd2\0rgmii0_txd3\0gmii0_txd4\0gmii0_txd5\0gmii0_txd6\0gmii0_txd7\0gmii0_txer\0rgmii0_txctrl\0mii0_txclk\0rgmii0_gtxclk\0rgmii0_rxd0\0rgmii0_rxd1\0rgmii0_rxd2\0rgmii0_rxd3\0gmii0_rxd4\0gmii0_rxd5\0gmii0_rxd6\0gmii0_rxd7\0gmii0_rxer\0rgmii0_rxctrl\0rgmii0_rxclk\0rgmii0_mdio\0rgmii0_mdc\0rgmii0_intr"; - function = "rgmii0"; - }; - }; - - rgmii0_pinconfig { - - config { - pins = "rgmii0_gtxclk\0rgmii0_mdc\0rgmii0_mdio\0rgmii0_rxclk\0rgmii0_rxctrl\0rgmii0_rxd0\0rgmii0_rxd1\0rgmii0_rxd2\0rgmii0_rxd3\0rgmii0_txctrl\0rgmii0_txd0\0rgmii0_txd1\0rgmii0_txd2\0rgmii0_txd3"; - drive-strength = <0x02>; - input-enable; - }; - }; - - rgmii1_pinctrl { - phandle = <0x5b>; - - mux { - pins = "rgmii1_txd0\0rgmii1_txd1\0rgmii1_txd2\0rgmii1_txd3\0mii1_rxer\0mii1_txclk\0rgmii1_txctrl\0rgmii1_gtxclk\0rgmii1_rxd0\0rgmii1_rxd1\0rgmii1_rxd2\0rgmii1_rxd3\0rgmii1_rxctrl\0rgmii1_rxclk\0rgmii1_mdc\0rgmii1_mdio\0rgmii1_intr"; - function = "rgmii1"; - }; - }; - - rgmii1_pinconfig { - - config { - pins = "rgmii1_gtxclk\0rgmii1_mdc\0rgmii1_mdio\0rgmii1_rxclk\0rgmii1_rxctrl\0rgmii1_rxd0\0rgmii1_rxd1\0rgmii1_rxd2\0rgmii1_rxd3\0rgmii1_txctrl\0rgmii1_txd0\0rgmii1_txd1\0rgmii1_txd2\0rgmii1_txd3"; - drive-strength = <0x02>; - input-enable; - }; - }; - - gmii0_pinconfig { - - config { - pins = "gmii0_rxd4\0gmii0_rxd5\0gmii0_rxd6\0gmii0_rxd7\0gmii0_rxer\0gmii0_txd4\0gmii0_txd5\0gmii0_txd6\0gmii0_txd7\0gmii0_txer"; - drive-strength = <0x02>; - input-enable; - }; - }; - - ssd_pinctrl { - phandle = <0x1a>; - - mux { - pins = "sdemmc0_led_ctl\0sdemmc1_led_ctl\0gpio_26\0gpio_27"; - function = "gpio"; - }; - }; - - gnss_pinctrl { - phandle = <0x1b>; - - mux { - pins = "gpio_31\0gpio_12\0pwm1"; - function = "gpio"; - }; - }; - - fan_pinctrl { - phandle = <0x1c>; - - mux { - pins = "gpio_27\0gpio_28"; - function = "gpio"; - }; - }; - - - }; - - isp { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,a1000b-isp"; - memory-region = <0x65 0x66>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x06>; - isp-fw-fbuf-addr = <0xa2000000>; - isp-fw-fbuf-size = <0x10000000>; - dma-coherent; - pinctrl-names = "default"; - pinctrl-0 = <0x67>; - - core@0 { - id = <0x00>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x68>; - phandle = <0x73>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x69>; - phandle = <0x74>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x6a>; - phandle = <0x75>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x6b>; - phandle = <0x76>; - }; - }; - }; - }; - - core@1 { - id = <0x01>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@4 { - remote-endpoint = <0x6c>; - phandle = <0x78>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@5 { - remote-endpoint = <0x6d>; - phandle = <0x79>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@6 { - remote-endpoint = <0x6e>; - phandle = <0x7a>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@7 { - remote-endpoint = <0x6f>; - phandle = <0x7b>; - }; - }; - }; - }; - - core@2 { - id = <0x02>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@8 { - remote-endpoint = <0x70>; - phandle = <0x7d>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@9 { - remote-endpoint = <0x71>; - phandle = <0x7e>; - }; - }; - }; - }; - }; - - csi@0 { - compatible = "bst,a1000b_csi2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - lane-speed = <0x960>; - id = <0x00>; - resets = <0x06 0x0d>; - reset-names = "csi0_reset"; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x72>; - phandle = <0x21>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x73>; - phandle = <0x68>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x74>; - phandle = <0x69>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x75>; - phandle = <0x6a>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x76>; - phandle = <0x6b>; - }; - }; - }; - }; - - csi@1 { - compatible = "bst,a1000b_csi2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - lane-speed = <0x640>; - id = <0x01>; - resets = <0x06 0x0e>; - reset-names = "csi1_reset"; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x77>; - phandle = <0x26>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x78>; - phandle = <0x6c>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x79>; - phandle = <0x6d>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x7a>; - phandle = <0x6e>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x7b>; - phandle = <0x6f>; - }; - }; - }; - }; - - csi@3 { - compatible = "bst,a1000b-csi2-2x2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - lane-speed = <0x960>; - resets = <0x06 0x10>; - reset-names = "csi2_reset"; - id = <0x03>; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x7c>; - phandle = <0x2b>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x7d>; - phandle = <0x70>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x7e>; - phandle = <0x71>; - }; - }; - }; - }; - - chosen { - bootargs = "earlycon=uart8250,mmio32,0x20008000 console=ttyS0,115200n8 memreserve=64M@0xf8000000 rdinit=/sbin/init root=/dev/mmcblk0p7 rw"; - stdout-path = "uart0"; - }; - - aliases { - // uart0 = "/amba_apu/serial@20008000"; - }; - - memory@80000000 { - device_type = "memory"; - reg = <0x00 0x80000000 0x00 0x70000000>; - }; - - // memory@1b0000000 { - // device_type = "memory"; - // reg = <0x01 0xb0000000 0x00 0x40000000>; - // }; - - reserved-memory { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - // linux,cma@88000000 { - // compatible = "shared-dma-pool"; - // reusable; - // reg = <0x0 0x84000000 0x0 0x07000000>; - // linux,cma-default; - // }; - - pcie_ctrl@8fd00000 { - compatible = "bst,pcie-ctrl"; - reg = <0x00 0x8fd00000 0x00 0x100000>; - no-map; - phandle = <0x0c>; - }; - - bst_atf@8b000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8b000000 0x00 0x2000000>; - no-map; - }; - - bst_tee@8fec0000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8fec0000 0x00 0x40000>; - no-map; - phandle = <0x82>; - }; - - bstn_cma@8ff00000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8ff00000 0x00 0x100000>; - no-map; - phandle = <0x7f>; - }; - - bstn@90000000 { - compatible = "bst,bstn"; - reg = <0x00 0x90000000 0x00 0x2000000>; - no-map; - }; - - bst_lwnn@92000000 { - compatible = "bst,bst_lwnn"; - reg = <0x00 0x92000000 0x00 0x2000000>; - no-map; - }; - - bst_lwnn@94000000 { - compatible = "bst,bst_lwnn"; - reg = <0x00 0x94000000 0x00 0x2000000>; - no-map; - }; - - bst_cv@96000000 { - compatible = "bst,bst_cv"; - reg = <0x00 0x96000000 0x00 0x4000000>; - no-map; - }; - - bst_cv_cma@9a000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x9a000000 0x00 0x2000000>; - align-shift = <0x08>; - no-map; - phandle = <0x80>; - }; - - vsp@0x9c000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x9c000000 0x00 0x1000000>; - no-map; - phandle = <0x81>; - }; - - vsp_fw@0x9d000000 { - reg = <0x00 0x9d000000 0x00 0x4000000>; - no-map; - }; - - bst_isp@0xa1000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0xa1000000 0x00 0x1000000>; - no-map; - phandle = <0x65>; - }; - - bst_isp_fw@0xa2000000 { - reg = <0x00 0xa2000000 0x00 0x10000000>; - no-map; - }; - - coreip_pub_cma@0xb2000000 { - compatible = "shared-dma-pool"; - align-shift = <0x08>; - reg = <0x00 0xb2000000 0x00 0x36000000>; - reusable; - phandle = <0x66>; - }; - - noc_pmu@0xe8000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0xe8000000 0x00 0x800000>; - reusable; - phandle = <0x43>; - }; - - - ddr0@0xf0000000 { - reg = <0x00 0xf0000000 0x00 0x10000000>; - no-map; - }; - - ddr1@0x1f0000000 { - reg = <0x01 0xf0000000 0x00 0x10000000>; - no-map; - }; - }; - - mbox-poll-clients { - compatible = "bst,ipc-mbox-client"; - reg = <0xfec00020 0x08 0x52030090 0x08 0x53090008 0x08 0xfec00028 0x08>; - #mbox-cells = <0x01>; - phandle = <0x0e>; - }; - - bstn-mbox { - compatible = "bstn,bstn-mbox"; - reg = <0x00 0x33102000 0x00 0x2000 0x00 0x33100000 0x00 0x2000 0x00 0x80000000 0x00 0x04 0x00 0x80002000 0x00 0x04>; - fpga-reset = <0x01>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x71 0xff04 0x00 0x72 0xff04 0x00 0x73 0xff04 0x00 0x74 0xff04 0x00 0x75 0xff04 0x00 0x76 0xff04 0x00 0x77 0xff04 0x00 0x78 0xff04>; - #mbox-cells = <0x01>; - phandle = <0xea>; - }; - - bstn@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bstn-a1000b,cma"; - reg = <0x00 0x50020000 0x00 0x100 0x00 0x90000000 0x00 0x2000000>; - memory-region = <0x66>; - rmem-base = <0x00 0xb2000000>; - rmem-size = <0x00 0x36000000>; - id = <0x00>; - assigned-mem-size = <0x1000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x08>; - firmware = "bstn_dsp_rtos.rbf"; - }; - - bst_cv@0x51030000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst_cv,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x96000000 0x00 0x2000000 0x00 0x98000000 0x00 0x2000000 0x00 0x92000000 0x00 0x2000000 0x00 0x94000000 0x00 0x2000000>; - memory-region = <0x80>; - assigned-mem-size = <0x4000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x09>; - dsp-num = <0x04>; - ipc-register-addr = <0x8ff00000>; - - dsp@0x96000000 { - index = <0x00>; - firmware = "bst_cv_dsp_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x967ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x98000000 { - index = <0x01>; - firmware = "bst_cv_dsp1_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x987ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x92000000 { - index = <0x02>; - firmware = "bst_cv_dsp2_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x927ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x94000000 { - index = <0x03>; - firmware = "bst_cv_dsp3_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x947ff000>; - ipc-src-core = <0x03>; - }; - }; - - bst_gwarp_scaler@0x51060000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst_gwarp_scaler,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x51050000 0x00 0x100 0x00 0x51060000 0x00 0x100>; - memory-region = <0x66>; - id = <0x00>; - bus-offset = <0x00 0x00>; - assigned-mem-size = <0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9b 0x04>; - interrupt-names = "bst_cv_irq"; - }; - - bare_cv@51030000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bare_cv,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x96000000 0x00 0x1000000 0x00 0x98000000 0x00 0x1000000 0x00 0x97000000 0x00 0x1000000 0x00 0x99000000 0x00 0x1000000>; - memory-region = <0x80>; - id = <0x01>; - assigned-mem-size = <0x2000000 0x2000000 0x2000000 0x2000000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x09>; - firmware = "bstcv0.rbf\0bstcv1.rbf\0bstcv2.rbf\0bstcv3.rbf"; - }; - - bst_lwnn@0x51030000 { - compatible = "bst,bst_lwnn-a1000b,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x92000000 0x00 0x2000000 0x00 0x94000000 0x00 0x2000000>; - memory-region = <0x66>; - bus-offset = <0x00 0x00>; - mbox-names = "bst-lwnn-mbox"; - dsp-num = <0x02>; - ipc-register-addr = <0x8ff00000>; - - dsp@0x92000000 { - index = <0x02>; - firmware = "bst_lwnn_dsp2_rt.rbf"; - assigned-mem-size = <0x2000>; - rt-init-addr = <0x927ff000>; - ipc-src-core = <0x06>; - }; - - dsp@0x94000000 { - index = <0x03>; - firmware = "bst_lwnn_dsp3_rt.rbf"; - assigned-mem-size = <0x2000>; - rt-init-addr = <0x947ff000>; - ipc-src-core = <0x00>; - }; - }; - - ipc_vsp@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-vsp-ipc"; - reg = <0x00 0x9c000000 0x00 0x100000 0x00 0x9c100000 0x00 0x80000 0x00 0x9c180000 0x00 0x80000 0x00 0x53090004 0x00 0x04 0x00 0x53090010 0x00 0x0c 0x00 0x33102fbc 0x00 0x04 0x00 0x9d000000 0x00 0x4000000>; - memory-region = <0x81>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x07>; - }; - - vsp@1 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-vsp"; - memory-region = <0x66>; - assigned-mem-size = <0x1000>; - clocks = <0x05 0x43>; - clock-names = "vout_display_clk"; - output-format = "HDMI_RGB"; - output-hsize = <0x780>; - output-vsize = <0x438>; - output-fresh = <0x3c>; - }; - - gmwarp@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-gmwarp"; - memory-region = <0x66>; - }; - - encoder@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-encoder"; - memory-region = <0x66>; - }; - - codec_dma_buf@0 { - compatible = "bst,dma_buf_te"; - memory-region = <0x66>; - }; - - firmware { - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - chip-number = <0x02>; - }; - }; - - tee { - #address-cells = <0x02>; - #size-cells = <0x02>; - reg = <0x00 0x18060000 0x00 0x20000>; - memory-region = <0x82>; - mbox-names = "bstn-mbox"; - chip-number = <0x01>; - }; - - ipc_arm0@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm0"; - reg = <0x04 0xfec00000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x00>; - }; - - ipc_arm3@3 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm3"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x03>; - }; - - ipc_arm2@2 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm2"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x02>; - }; - - ipc_arm1@1 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm1"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x01>; - }; - - ipc@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,ipc"; - reg = <0x04 0xfec00000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x05>; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8.toml b/os/axvisor/configs/vms/linux-aarch64-a1000-smp8.toml deleted file mode 100644 index d3231ee6d..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8.toml +++ /dev/null @@ -1,64 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 8 -# The physical CPU ids. -phys_cpu_ids = [0x00, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700] -# Guest vm physical cpu sets. -phys_cpu_sets = [1, 2, 4, 8, 16, 32, 64, 128] - - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x1_ce80_0000 -# The location of image: "memory" | "fs". -# Load from memory. -image_location = "memory" -# The load address of the kernel image. -kernel_load_addr = 0x1_ce80_0000 -## The file path of the kernel image. -kernel_path = "/path/to/kernel" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x1_cef0_0000 -dtb_path = "/path/to/dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x1_ce80_0000, 0x2000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL - [0x8000_0000, 0x7000_0000, 0xf, 2], -] -# -# Device specifications -# -[devices] -# The interrupt mode. -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ -["most-devices", 0x0, 0x0, 0x8000_0000, 0x1], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.dts b/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.dts deleted file mode 100644 index 18d68f0f7..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.dts +++ /dev/null @@ -1,1302 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000080000000 0x0000000000010000; -/ { - compatible = "phytium,pe2204"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x40000000 0x00 0x40000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial0 = "/soc/uart@2800c000"; - serial1 = "/soc/uart@2800d000"; - serial2 = "/soc/uart@2800e000"; - serial3 = "/soc/uart@2800f000"; - ethernet0 = "/soc/ethernet@3200c000"; - ethernet1 = "/soc/ethernet@3200e000"; - ethernet2 = "/soc/ethernet@32010000"; - ethernet3 = "/soc/ethernet@32012000"; - serial4 = "/soc/uart@28014000"; - serial5 = "/soc/uart@2802A000"; - serial6 = "/soc/uart@28032000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - firmware { - - scmi { - compatible = "arm,scmi"; - mboxes = <0x02 0x00>; - mbox-names = "tx"; - shmem = <0x03>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@13 { - reg = <0x13>; - #clock-cells = <0x01>; - phandle = <0x09>; - }; - - protocol@15 { - reg = <0x15>; - #thermal-sensor-cells = <0x01>; - phandle = <0x04>; - }; - }; - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - }; - }; - - thermal-zones { - - sensor0 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x00>; - }; - - sensor1 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x01>; - }; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - // cluster0 { - - // core0 { - // cpu = <0x05>; - // }; - // }; - - cluster1 { - - core0 { - cpu = <0x06>; - }; - }; - - // cluster2 { - - // core0 { - // cpu = <0x07>; - // }; - - // core1 { - // cpu = <0x08>; - // }; - // }; - }; - - // cpu@0 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x200>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x07>; - // }; - - // cpu@1 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x201>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x08>; - // }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x00>; - // enable-method = "psci"; - // clocks = <0x09 0x00>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x05>; - // }; - - cpu@101 { - device_type = "cpu"; - compatible = "phytium,ftc664\0arm,armv8"; - reg = <0x00 0x100>; - enable-method = "psci"; - clocks = <0x09 0x01>; - capacity-dmips-mhz = <0x161c>; - phandle = <0x06>; - }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0x01 0x07 0x08>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - clocks { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - clk48mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2dc6c00>; - phandle = <0x13>; - }; - - clk50mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2faf080>; - phandle = <0x0d>; - }; - - clk100mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x5f5e100>; - phandle = <0x0c>; - }; - - clk200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbebc200>; - phandle = <0x11>; - }; - - clk250mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xee6b280>; - phandle = <0x12>; - }; - - clk300mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x11e1a300>; - phandle = <0x0b>; - }; - - clk600mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x23c34600>; - phandle = <0x0e>; - }; - - clk1200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x47868c00>; - phandle = <0x0a>; - }; - }; - - iommu@30000000 { - compatible = "arm,smmu-v3"; - reg = <0x00 0x30000000 0x00 0x800000>; - interrupts = <0x00 0xf0 0x01 0x00 0xef 0x01 0x00 0xec 0x01 0x00 0xf2 0x01>; - interrupt-names = "eventq\0priq\0cmdq-sync\0gerror"; - dma-coherent; - #iommu-cells = <0x01>; - phandle = <0x10>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - mmc@28000000 { - compatible = "phytium,mci"; - reg = <0x00 0x28000000 0x00 0x1000>; - interrupts = <0x00 0x48 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "okay"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - }; - - mmc@28001000 { - compatible = "phytium,mci"; - reg = <0x00 0x28001000 0x00 0x1000>; - interrupts = <0x00 0x49 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "okay"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - no-sd; - non-removable; - }; - - nand@28002000 { - compatible = "phytium,nfc"; - reg = <0x00 0x28002000 0x00 0x1000>; - interrupts = <0x00 0x4a 0x04>; - status = "disabled"; - }; - - ddma@28003000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28003000 0x00 0x1000>; - interrupts = <0x00 0x4b 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - ddma@28004000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28004000 0x00 0x1000>; - interrupts = <0x00 0x4c 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - spi@28008000 { - compatible = "phytium,qspi-nor"; - reg = <0x00 0x28008000 0x00 0x1000 0x00 0x00 0x00 0xfffffff>; - reg-names = "qspi\0qspi_mm"; - clocks = <0x0b>; - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x00>; - spi-rx-bus-width = <0x01>; - spi-max-frequency = <0x1312d00>; - status = "okay"; - }; - }; - - uart@2800c000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800c000 0x00 0x1000>; - interrupts = <0x00 0x53 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800e000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800e000 0x00 0x1000>; - interrupts = <0x00 0x55 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800f000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800f000 0x00 0x1000>; - interrupts = <0x00 0x56 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - lpc@28010000 { - compatible = "simple-mfd\0syscon"; - reg = <0x00 0x28010000 0x00 0x1000>; - reg-io-width = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x28010000 0x1000>; - - kcs@24 { - compatible = "phytium,kcs-bmc"; - reg = <0x24 0x01 0x30 0x01 0x3c 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@28 { - compatible = "phytium,kcs-bmc"; - reg = <0x28 0x01 0x34 0x01 0x40 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@2c { - compatible = "phytium,kcs-bmc"; - reg = <0x2c 0x01 0x38 0x01 0x44 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@8c { - compatible = "phytium,kcs-bmc"; - reg = <0x8c 0x01 0x90 0x01 0x94 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - bt@48 { - compatible = "phytium,bt-bmc"; - reg = <0x48 0x20>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - }; - - gpio@28034000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28034000 0x00 0x1000>; - interrupts = <0x00 0x6c 0x04 0x00 0x6d 0x04 0x00 0x6e 0x04 0x00 0x6f 0x04 0x00 0x70 0x04 0x00 0x71 0x04 0x00 0x72 0x04 0x00 0x73 0x04 0x00 0x74 0x04 0x00 0x75 0x04 0x00 0x76 0x04 0x00 0x77 0x04 0x00 0x78 0x04 0x00 0x79 0x04 0x00 0x7a 0x04 0x00 0x7b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28035000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28035000 0x00 0x1000>; - interrupts = <0x00 0x7c 0x04 0x00 0x7d 0x04 0x00 0x7e 0x04 0x00 0x7f 0x04 0x00 0x80 0x04 0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04 0x00 0x89 0x04 0x00 0x8a 0x04 0x00 0x8b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x14>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28036000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28036000 0x00 0x1000>; - interrupts = <0x00 0x8c 0x04 0x00 0x8d 0x04 0x00 0x8e 0x04 0x00 0x8f 0x04 0x00 0x90 0x04 0x00 0x91 0x04 0x00 0x92 0x04 0x00 0x93 0x04 0x00 0x94 0x04 0x00 0x95 0x04 0x00 0x96 0x04 0x00 0x97 0x04 0x00 0x98 0x04 0x00 0x99 0x04 0x00 0x9a 0x04 0x00 0x9b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x15>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28037000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28037000 0x00 0x1000>; - interrupts = <0x00 0x9c 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28038000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28038000 0x00 0x1000>; - interrupts = <0x00 0x9d 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28039000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28039000 0x00 0x1000>; - interrupts = <0x00 0x9e 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - spi@2803a000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803a000 0x00 0x1000>; - interrupts = <0x00 0x9f 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - global-cs = <0x01>; - - spidev@0 { - compatible = "spidev"; - reg = <0x00>; - spi-max-frequency = <0x2faf080>; - status = "disabled"; - }; - }; - - spi@2803b000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803b000 0x00 0x1000>; - interrupts = <0x00 0xa0 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803c000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803c000 0x00 0x1000>; - interrupts = <0x00 0xa1 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803d000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803d000 0x00 0x1000>; - interrupts = <0x00 0xa2 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - watchdog@28040000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28041000 0x00 0x1000 0x00 0x28040000 0x00 0x1000>; - interrupts = <0x00 0xa4 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - watchdog@28042000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28043000 0x00 0x1000 0x00 0x28042000 0x00 0x1000>; - interrupts = <0x00 0xa5 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - pwm@2804a000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804a000 0x00 0x1000>; - interrupts = <0x00 0xad 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - pwm@2804b000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804b000 0x00 0x1000>; - interrupts = <0x00 0xae 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - tacho@28054000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28054000 0x00 0x1000>; - interrupts = <0x00 0xc2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28055000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28055000 0x00 0x1000>; - interrupts = <0x00 0xc3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28056000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28056000 0x00 0x1000>; - interrupts = <0x00 0xc4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28057000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28057000 0x00 0x1000>; - interrupts = <0x00 0xc5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28058000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28058000 0x00 0x1000>; - interrupts = <0x00 0xc6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28059000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28059000 0x00 0x1000>; - interrupts = <0x00 0xc7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805a000 0x00 0x1000>; - interrupts = <0x00 0xc8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805b000 0x00 0x1000>; - interrupts = <0x00 0xc9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805c000 0x00 0x1000>; - interrupts = <0x00 0xca 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805d000 0x00 0x1000>; - interrupts = <0x00 0xcb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805e000 0x00 0x1000>; - interrupts = <0x00 0xcc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805f000 0x00 0x1000>; - interrupts = <0x00 0xcd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28060000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28060000 0x00 0x1000>; - interrupts = <0x00 0xce 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28061000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28061000 0x00 0x1000>; - interrupts = <0x00 0xcf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28062000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28062000 0x00 0x1000>; - interrupts = <0x00 0xd0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28063000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28063000 0x00 0x1000>; - interrupts = <0x00 0xd1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28064000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28064000 0x00 0x1000>; - interrupts = <0x00 0xd2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28065000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28065000 0x00 0x1000>; - interrupts = <0x00 0xd3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28066000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28066000 0x00 0x1000>; - interrupts = <0x00 0xd4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28067000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28067000 0x00 0x1000>; - interrupts = <0x00 0xd5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28068000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28068000 0x00 0x1000>; - interrupts = <0x00 0xd6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28069000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28069000 0x00 0x1000>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806a000 0x00 0x1000>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806b000 0x00 0x1000>; - interrupts = <0x00 0xd9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806c000 0x00 0x1000>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806d000 0x00 0x1000>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806e000 0x00 0x1000>; - interrupts = <0x00 0xdc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806f000 0x00 0x1000>; - interrupts = <0x00 0xdd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28070000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28070000 0x00 0x1000>; - interrupts = <0x00 0xde 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28071000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28071000 0x00 0x1000>; - interrupts = <0x00 0xdf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28072000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28072000 0x00 0x1000>; - interrupts = <0x00 0xe0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28073000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28073000 0x00 0x1000>; - interrupts = <0x00 0xe1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28074000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28074000 0x00 0x1000>; - interrupts = <0x00 0xe2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28075000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28075000 0x00 0x1000>; - interrupts = <0x00 0xe3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28076000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28076000 0x00 0x1000>; - interrupts = <0x00 0xe4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28077000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28077000 0x00 0x1000>; - interrupts = <0x00 0xe5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28078000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28078000 0x00 0x1000>; - interrupts = <0x00 0xe6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28079000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28079000 0x00 0x1000>; - interrupts = <0x00 0xe7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb2@31800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31800000 0x00 0x80000 0x00 0x31990000 0x00 0x10000>; - interrupts = <0x00 0x20 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@31880000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31880000 0x00 0x80000 0x00 0x319a0000 0x00 0x10000>; - interrupts = <0x00 0x21 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@31900000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31900000 0x00 0x80000 0x00 0x319b0000 0x00 0x10000>; - interrupts = <0x00 0x22 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@32800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32800000 0x00 0x40000 0x00 0x32880000 0x00 0x40000>; - interrupts = <0x00 0x0e 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@32840000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32840000 0x00 0x40000 0x00 0x328c0000 0x00 0x40000>; - interrupts = <0x00 0x0f 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - dc@32000000 { - compatible = "phytium,dc"; - reg = <0x00 0x32000000 0x00 0x8000>; - interrupts = <0x00 0x2c 0x04>; - status = "okay"; - pipe_mask = [01]; - edp_mask = [00]; - }; - - i2s_dp0@32009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x32009000 0x00 0x1000 0x00 0x32008000 0x00 0x1000>; - interrupts = <0x00 0x2f 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp0"; - status = "okay"; - }; - - i2s_dp1@3200B000 { - compatible = "phytium,i2s"; - reg = <0x00 0x3200b000 0x00 0x1000 0x00 0x3200a000 0x00 0x1000>; - interrupts = <0x00 0x30 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp1"; - status = "disabled"; - }; - - pmdk_dp { - compatible = "phytium,pmdk-dp"; - status = "okay"; - num-dp = <0x01>; - dp-mask = [01]; - }; - - mailbox@32a00000 { - compatible = "phytium,mbox"; - reg = <0x00 0x32a00000 0x00 0x1000>; - interrupts = <0x00 0x16 0x04>; - #mbox-cells = <0x01>; - phandle = <0x02>; - }; - - rng@32a36000 { - compatible = "phytium,rng"; - reg = <0x00 0x32a36000 0x00 0x1000>; - status = "okay"; - }; - - sram@32a10000 { - compatible = "phytium,pe220x-sram-ns\0mmio-sram"; - reg = <0x00 0x32a10000 0x00 0x2000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x32a10000 0x2000>; - - scp-shmem@0 { - compatible = "arm,scmi-shmem"; - reg = <0x1000 0x400>; - }; - - scp-shmem@1 { - compatible = "arm,scmi-shmem"; - reg = <0x1400 0x400>; - phandle = <0x03>; - }; - }; - - gdma@32b34000 { - compatible = "phytium,gdma"; - dma-channels = <0x10>; - max-outstanding = <0x10>; - reg = <0x00 0x32b34000 0x00 0x1000>; - interrupts = <0x00 0xea 0x04>; - #dma-cells = <0x01>; - }; - - spinlock@32b36000 { - compatible = "phytium,hwspinlock"; - reg = <0x00 0x32b36000 0x00 0x1000>; - #hwlock-cells = <0x01>; - nr-locks = <0x20>; - status = "disabled"; - }; - - pcie@40000000 { - compatible = "pci-host-ecam-generic"; - device_type = "pci"; - #address-cells = <0x03>; - #size-cells = <0x02>; - #interrupt-cells = <0x01>; - reg = <0x00 0x40000000 0x00 0x10000000>; - msi-parent = <0x0f>; - bus-range = <0x00 0xff>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x07 0x04>; - ranges = <0x1000000 0x00 0x00 0x00 0x50000000 0x00 0xf00000 0x2000000 0x00 0x58000000 0x00 0x58000000 0x00 0x28000000 0x3000000 0x10 0x00 0x10 0x00 0x10 0x00>; - iommu-map = <0x00 0x10 0x00 0x10000>; - status = "okay"; - }; - - edac@32b28000 { - compatible = "phytium,pe220x-edac"; - reg = <0x00 0x32b28000 0x00 0x1000 0x00 0x31400000 0x00 0x1000 0x00 0x31401000 0x00 0x1000>; - interrupts = <0x00 0x00 0x04 0x00 0x01 0x04>; - status = "disabled"; - }; - - hda@28006000 { - compatible = "phytium,hda"; - reg = <0x00 0x28006000 0x00 0x1000>; - interrupts = <0x00 0x4e 0x04>; - status = "disabled"; - }; - - i2s@28009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x28009000 0x00 0x1000 0x00 0x28005000 0x00 0x1000>; - interrupts = <0x00 0x4d 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - status = "okay"; - #sound-dai-cells = <0x00>; - dai-name = "phytium-i2s-lsd"; - phandle = <0x16>; - }; - - can@2800a000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800a000 0x00 0x1000>; - interrupts = <0x00 0x51 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - can@2800b000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800b000 0x00 0x1000>; - interrupts = <0x00 0x52 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - keypad@2807a000 { - compatible = "phytium,keypad"; - reg = <0x00 0x2807a000 0x00 0x1000>; - interrupts = <0x00 0xbd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb3@31a08000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a08000 0x00 0x18000>; - interrupts = <0x00 0x10 0x04>; - status = "okay"; - }; - - usb3@31a28000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a28000 0x00 0x18000>; - interrupts = <0x00 0x11 0x04>; - status = "okay"; - }; - - sata@31a40000 { - compatible = "generic-ahci"; - reg = <0x00 0x31a40000 0x00 0x1000>; - interrupts = <0x00 0x2a 0x04>; - status = "disabled"; - }; - - sata@32014000 { - compatible = "generic-ahci"; - reg = <0x00 0x32014000 0x00 0x1000>; - interrupts = <0x00 0x2b 0x04>; - status = "disabled"; - }; - - ethernet@3200c000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200c000 0x00 0x2000>; - interrupts = <0x00 0x37 0x04 0x00 0x38 0x04 0x00 0x39 0x04 0x00 0x3a 0x04 0x00 0x1c 0x04 0x00 0x1d 0x04 0x00 0x1e 0x04 0x00 0x1f 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - support-tsn; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@3200e000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200e000 0x00 0x2000>; - interrupts = <0x00 0x3b 0x04 0x00 0x3c 0x04 0x00 0x3d 0x04 0x00 0x3e 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@32010000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32010000 0x00 0x2000>; - interrupts = <0x00 0x40 0x04 0x00 0x41 0x04 0x00 0x42 0x04 0x00 0x43 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - ethernet@32012000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32012000 0x00 0x2000>; - interrupts = <0x00 0x44 0x04 0x00 0x45 0x04 0x00 0x46 0x04 0x00 0x47 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - vpu@32b00000 { - compatible = "phytium,vpu"; - reg = <0x00 0x32b00000 0x00 0x20000>; - interrupts = <0x00 0x0c 0x04>; - status = "okay"; - }; - - i2c@28026000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28026000 0x00 0x1000>; - interrupts = <0x00 0x65 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28030000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28030000 0x00 0x1000>; - interrupts = <0x00 0x6a 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - es8336@10 { - #sound-dai-cells = <0x00>; - compatible = "everest,es8336"; - reg = <0x10>; - phandle = <0x17>; - }; - }; - - uart@28014000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28014000 0x00 0x1000>; - interrupts = <0x00 0x5c 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - i2c@28016000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28016000 0x00 0x1000>; - interrupts = <0x00 0x5d 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28024000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28024000 0x00 0x1000>; - interrupts = <0x00 0x64 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - uart@2802A000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2802a000 0x00 0x1000>; - interrupts = <0x00 0x67 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@28032000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28032000 0x00 0x1000>; - interrupts = <0x00 0x6b 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 root=/dev/mmcblk0p1 rootfstype=ext4 rootwait rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory { - device_type = "memory"; - reg = <0x20 0x40000000 0x00 0x40000000>; - }; - - leds { - compatible = "gpio-leds"; - - sysled { - label = "sysled"; - gpios = <0x14 0x05 0x00>; - linux,default-trigger = "none"; - }; - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "phytium,pe220x-i2s-audio"; - simple-audio-card,pin-switches = "mic-in"; - simple-audio-card,widgets = "Microphone\0mic-in\0Headphone\0Headphones"; - simple-audio-card,routing = "MIC2\0mic-in"; - simple-audio-card,hp-det-gpio = <0x15 0x0b 0x01>; - - simple-audio-card,cpu { - sound-dai = <0x16>; - }; - - simple-audio-card,codec { - sound-dai = <0x17>; - }; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.toml b/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.toml deleted file mode 100644 index ba5a6d324..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.toml +++ /dev/null @@ -1,62 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x100] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_4008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x20_4008_0000 -## The file path of the kernel image. -kernel_path = "/guest/linux/phytiumpi" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_4000_0000 -#dtb_path = "/path/to/axvisor/configs/vms/linux-aarch64-e2000_smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_4000_0000, 0x4000_0000, 0x7, 1], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -interrupt_mode = "passthrough" -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.dts b/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.dts deleted file mode 100644 index 0d3b86b4c..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.dts +++ /dev/null @@ -1,1302 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000080000000 0x0000000000010000; -/ { - compatible = "phytium,pe2204"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x00000000 0x00 0x40000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial0 = "/soc/uart@2800c000"; - serial1 = "/soc/uart@2800d000"; - serial2 = "/soc/uart@2800e000"; - serial3 = "/soc/uart@2800f000"; - ethernet0 = "/soc/ethernet@3200c000"; - ethernet1 = "/soc/ethernet@3200e000"; - ethernet2 = "/soc/ethernet@32010000"; - ethernet3 = "/soc/ethernet@32012000"; - serial4 = "/soc/uart@28014000"; - serial5 = "/soc/uart@2802A000"; - serial6 = "/soc/uart@28032000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - firmware { - - scmi { - compatible = "arm,scmi"; - mboxes = <0x02 0x00>; - mbox-names = "tx"; - shmem = <0x03>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@13 { - reg = <0x13>; - #clock-cells = <0x01>; - phandle = <0x09>; - }; - - protocol@15 { - reg = <0x15>; - #thermal-sensor-cells = <0x01>; - phandle = <0x04>; - }; - }; - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - }; - }; - - thermal-zones { - - sensor0 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x00>; - }; - - sensor1 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x01>; - }; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - cluster0 { - - core0 { - cpu = <0x05>; - }; - }; - - // cluster1 { - - // core0 { - // cpu = <0x06>; - // }; - // }; - - cluster2 { - - core0 { - cpu = <0x07>; - }; - - // core1 { - // cpu = <0x08>; - // }; - }; - }; - - cpu@0 { - device_type = "cpu"; - compatible = "phytium,ftc310\0arm,armv8"; - reg = <0x00 0x200>; - enable-method = "psci"; - clocks = <0x09 0x02>; - capacity-dmips-mhz = <0xb22>; - phandle = <0x07>; - }; - - // cpu@1 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x201>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x08>; - // }; - - cpu@100 { - device_type = "cpu"; - compatible = "phytium,ftc664\0arm,armv8"; - reg = <0x00 0x00>; - enable-method = "psci"; - clocks = <0x09 0x00>; - capacity-dmips-mhz = <0x161c>; - phandle = <0x05>; - }; - - // cpu@101 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x100>; - // enable-method = "psci"; - // clocks = <0x09 0x01>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x06>; - // }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0x01 0x07 0x08>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - clocks { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - clk48mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2dc6c00>; - phandle = <0x13>; - }; - - clk50mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2faf080>; - phandle = <0x0d>; - }; - - clk100mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x5f5e100>; - phandle = <0x0c>; - }; - - clk200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbebc200>; - phandle = <0x11>; - }; - - clk250mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xee6b280>; - phandle = <0x12>; - }; - - clk300mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x11e1a300>; - phandle = <0x0b>; - }; - - clk600mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x23c34600>; - phandle = <0x0e>; - }; - - clk1200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x47868c00>; - phandle = <0x0a>; - }; - }; - - iommu@30000000 { - compatible = "arm,smmu-v3"; - reg = <0x00 0x30000000 0x00 0x800000>; - interrupts = <0x00 0xf0 0x01 0x00 0xef 0x01 0x00 0xec 0x01 0x00 0xf2 0x01>; - interrupt-names = "eventq\0priq\0cmdq-sync\0gerror"; - dma-coherent; - #iommu-cells = <0x01>; - phandle = <0x10>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - mmc@28000000 { - compatible = "phytium,mci"; - reg = <0x00 0x28000000 0x00 0x1000>; - interrupts = <0x00 0x48 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "okay"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - }; - - mmc@28001000 { - compatible = "phytium,mci"; - reg = <0x00 0x28001000 0x00 0x1000>; - interrupts = <0x00 0x49 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "okay"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - no-sd; - non-removable; - }; - - nand@28002000 { - compatible = "phytium,nfc"; - reg = <0x00 0x28002000 0x00 0x1000>; - interrupts = <0x00 0x4a 0x04>; - status = "disabled"; - }; - - ddma@28003000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28003000 0x00 0x1000>; - interrupts = <0x00 0x4b 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - ddma@28004000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28004000 0x00 0x1000>; - interrupts = <0x00 0x4c 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - spi@28008000 { - compatible = "phytium,qspi-nor"; - reg = <0x00 0x28008000 0x00 0x1000 0x00 0x00 0x00 0xfffffff>; - reg-names = "qspi\0qspi_mm"; - clocks = <0x0b>; - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x00>; - spi-rx-bus-width = <0x01>; - spi-max-frequency = <0x1312d00>; - status = "okay"; - }; - }; - - uart@2800c000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800c000 0x00 0x1000>; - interrupts = <0x00 0x53 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800e000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800e000 0x00 0x1000>; - interrupts = <0x00 0x55 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800f000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800f000 0x00 0x1000>; - interrupts = <0x00 0x56 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - lpc@28010000 { - compatible = "simple-mfd\0syscon"; - reg = <0x00 0x28010000 0x00 0x1000>; - reg-io-width = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x28010000 0x1000>; - - kcs@24 { - compatible = "phytium,kcs-bmc"; - reg = <0x24 0x01 0x30 0x01 0x3c 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@28 { - compatible = "phytium,kcs-bmc"; - reg = <0x28 0x01 0x34 0x01 0x40 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@2c { - compatible = "phytium,kcs-bmc"; - reg = <0x2c 0x01 0x38 0x01 0x44 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@8c { - compatible = "phytium,kcs-bmc"; - reg = <0x8c 0x01 0x90 0x01 0x94 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - bt@48 { - compatible = "phytium,bt-bmc"; - reg = <0x48 0x20>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - }; - - gpio@28034000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28034000 0x00 0x1000>; - interrupts = <0x00 0x6c 0x04 0x00 0x6d 0x04 0x00 0x6e 0x04 0x00 0x6f 0x04 0x00 0x70 0x04 0x00 0x71 0x04 0x00 0x72 0x04 0x00 0x73 0x04 0x00 0x74 0x04 0x00 0x75 0x04 0x00 0x76 0x04 0x00 0x77 0x04 0x00 0x78 0x04 0x00 0x79 0x04 0x00 0x7a 0x04 0x00 0x7b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28035000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28035000 0x00 0x1000>; - interrupts = <0x00 0x7c 0x04 0x00 0x7d 0x04 0x00 0x7e 0x04 0x00 0x7f 0x04 0x00 0x80 0x04 0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04 0x00 0x89 0x04 0x00 0x8a 0x04 0x00 0x8b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x14>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28036000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28036000 0x00 0x1000>; - interrupts = <0x00 0x8c 0x04 0x00 0x8d 0x04 0x00 0x8e 0x04 0x00 0x8f 0x04 0x00 0x90 0x04 0x00 0x91 0x04 0x00 0x92 0x04 0x00 0x93 0x04 0x00 0x94 0x04 0x00 0x95 0x04 0x00 0x96 0x04 0x00 0x97 0x04 0x00 0x98 0x04 0x00 0x99 0x04 0x00 0x9a 0x04 0x00 0x9b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x15>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28037000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28037000 0x00 0x1000>; - interrupts = <0x00 0x9c 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28038000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28038000 0x00 0x1000>; - interrupts = <0x00 0x9d 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28039000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28039000 0x00 0x1000>; - interrupts = <0x00 0x9e 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - spi@2803a000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803a000 0x00 0x1000>; - interrupts = <0x00 0x9f 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - global-cs = <0x01>; - - spidev@0 { - compatible = "spidev"; - reg = <0x00>; - spi-max-frequency = <0x2faf080>; - status = "disabled"; - }; - }; - - spi@2803b000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803b000 0x00 0x1000>; - interrupts = <0x00 0xa0 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803c000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803c000 0x00 0x1000>; - interrupts = <0x00 0xa1 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803d000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803d000 0x00 0x1000>; - interrupts = <0x00 0xa2 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - watchdog@28040000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28041000 0x00 0x1000 0x00 0x28040000 0x00 0x1000>; - interrupts = <0x00 0xa4 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - watchdog@28042000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28043000 0x00 0x1000 0x00 0x28042000 0x00 0x1000>; - interrupts = <0x00 0xa5 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - pwm@2804a000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804a000 0x00 0x1000>; - interrupts = <0x00 0xad 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - pwm@2804b000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804b000 0x00 0x1000>; - interrupts = <0x00 0xae 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - tacho@28054000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28054000 0x00 0x1000>; - interrupts = <0x00 0xc2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28055000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28055000 0x00 0x1000>; - interrupts = <0x00 0xc3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28056000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28056000 0x00 0x1000>; - interrupts = <0x00 0xc4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28057000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28057000 0x00 0x1000>; - interrupts = <0x00 0xc5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28058000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28058000 0x00 0x1000>; - interrupts = <0x00 0xc6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28059000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28059000 0x00 0x1000>; - interrupts = <0x00 0xc7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805a000 0x00 0x1000>; - interrupts = <0x00 0xc8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805b000 0x00 0x1000>; - interrupts = <0x00 0xc9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805c000 0x00 0x1000>; - interrupts = <0x00 0xca 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805d000 0x00 0x1000>; - interrupts = <0x00 0xcb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805e000 0x00 0x1000>; - interrupts = <0x00 0xcc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805f000 0x00 0x1000>; - interrupts = <0x00 0xcd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28060000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28060000 0x00 0x1000>; - interrupts = <0x00 0xce 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28061000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28061000 0x00 0x1000>; - interrupts = <0x00 0xcf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28062000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28062000 0x00 0x1000>; - interrupts = <0x00 0xd0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28063000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28063000 0x00 0x1000>; - interrupts = <0x00 0xd1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28064000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28064000 0x00 0x1000>; - interrupts = <0x00 0xd2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28065000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28065000 0x00 0x1000>; - interrupts = <0x00 0xd3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28066000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28066000 0x00 0x1000>; - interrupts = <0x00 0xd4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28067000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28067000 0x00 0x1000>; - interrupts = <0x00 0xd5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28068000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28068000 0x00 0x1000>; - interrupts = <0x00 0xd6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28069000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28069000 0x00 0x1000>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806a000 0x00 0x1000>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806b000 0x00 0x1000>; - interrupts = <0x00 0xd9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806c000 0x00 0x1000>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806d000 0x00 0x1000>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806e000 0x00 0x1000>; - interrupts = <0x00 0xdc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806f000 0x00 0x1000>; - interrupts = <0x00 0xdd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28070000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28070000 0x00 0x1000>; - interrupts = <0x00 0xde 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28071000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28071000 0x00 0x1000>; - interrupts = <0x00 0xdf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28072000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28072000 0x00 0x1000>; - interrupts = <0x00 0xe0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28073000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28073000 0x00 0x1000>; - interrupts = <0x00 0xe1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28074000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28074000 0x00 0x1000>; - interrupts = <0x00 0xe2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28075000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28075000 0x00 0x1000>; - interrupts = <0x00 0xe3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28076000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28076000 0x00 0x1000>; - interrupts = <0x00 0xe4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28077000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28077000 0x00 0x1000>; - interrupts = <0x00 0xe5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28078000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28078000 0x00 0x1000>; - interrupts = <0x00 0xe6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28079000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28079000 0x00 0x1000>; - interrupts = <0x00 0xe7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb2@31800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31800000 0x00 0x80000 0x00 0x31990000 0x00 0x10000>; - interrupts = <0x00 0x20 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@31880000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31880000 0x00 0x80000 0x00 0x319a0000 0x00 0x10000>; - interrupts = <0x00 0x21 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@31900000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31900000 0x00 0x80000 0x00 0x319b0000 0x00 0x10000>; - interrupts = <0x00 0x22 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@32800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32800000 0x00 0x40000 0x00 0x32880000 0x00 0x40000>; - interrupts = <0x00 0x0e 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@32840000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32840000 0x00 0x40000 0x00 0x328c0000 0x00 0x40000>; - interrupts = <0x00 0x0f 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - dc@32000000 { - compatible = "phytium,dc"; - reg = <0x00 0x32000000 0x00 0x8000>; - interrupts = <0x00 0x2c 0x04>; - status = "okay"; - pipe_mask = [01]; - edp_mask = [00]; - }; - - i2s_dp0@32009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x32009000 0x00 0x1000 0x00 0x32008000 0x00 0x1000>; - interrupts = <0x00 0x2f 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp0"; - status = "okay"; - }; - - i2s_dp1@3200B000 { - compatible = "phytium,i2s"; - reg = <0x00 0x3200b000 0x00 0x1000 0x00 0x3200a000 0x00 0x1000>; - interrupts = <0x00 0x30 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp1"; - status = "disabled"; - }; - - pmdk_dp { - compatible = "phytium,pmdk-dp"; - status = "okay"; - num-dp = <0x01>; - dp-mask = [01]; - }; - - mailbox@32a00000 { - compatible = "phytium,mbox"; - reg = <0x00 0x32a00000 0x00 0x1000>; - interrupts = <0x00 0x16 0x04>; - #mbox-cells = <0x01>; - phandle = <0x02>; - }; - - rng@32a36000 { - compatible = "phytium,rng"; - reg = <0x00 0x32a36000 0x00 0x1000>; - status = "okay"; - }; - - sram@32a10000 { - compatible = "phytium,pe220x-sram-ns\0mmio-sram"; - reg = <0x00 0x32a10000 0x00 0x2000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x32a10000 0x2000>; - - scp-shmem@0 { - compatible = "arm,scmi-shmem"; - reg = <0x1000 0x400>; - }; - - scp-shmem@1 { - compatible = "arm,scmi-shmem"; - reg = <0x1400 0x400>; - phandle = <0x03>; - }; - }; - - gdma@32b34000 { - compatible = "phytium,gdma"; - dma-channels = <0x10>; - max-outstanding = <0x10>; - reg = <0x00 0x32b34000 0x00 0x1000>; - interrupts = <0x00 0xea 0x04>; - #dma-cells = <0x01>; - }; - - spinlock@32b36000 { - compatible = "phytium,hwspinlock"; - reg = <0x00 0x32b36000 0x00 0x1000>; - #hwlock-cells = <0x01>; - nr-locks = <0x20>; - status = "disabled"; - }; - - pcie@40000000 { - compatible = "pci-host-ecam-generic"; - device_type = "pci"; - #address-cells = <0x03>; - #size-cells = <0x02>; - #interrupt-cells = <0x01>; - reg = <0x00 0x40000000 0x00 0x10000000>; - msi-parent = <0x0f>; - bus-range = <0x00 0xff>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x07 0x04>; - ranges = <0x1000000 0x00 0x00 0x00 0x50000000 0x00 0xf00000 0x2000000 0x00 0x58000000 0x00 0x58000000 0x00 0x28000000 0x3000000 0x10 0x00 0x10 0x00 0x10 0x00>; - iommu-map = <0x00 0x10 0x00 0x10000>; - status = "okay"; - }; - - edac@32b28000 { - compatible = "phytium,pe220x-edac"; - reg = <0x00 0x32b28000 0x00 0x1000 0x00 0x31400000 0x00 0x1000 0x00 0x31401000 0x00 0x1000>; - interrupts = <0x00 0x00 0x04 0x00 0x01 0x04>; - status = "disabled"; - }; - - hda@28006000 { - compatible = "phytium,hda"; - reg = <0x00 0x28006000 0x00 0x1000>; - interrupts = <0x00 0x4e 0x04>; - status = "disabled"; - }; - - i2s@28009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x28009000 0x00 0x1000 0x00 0x28005000 0x00 0x1000>; - interrupts = <0x00 0x4d 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - status = "okay"; - #sound-dai-cells = <0x00>; - dai-name = "phytium-i2s-lsd"; - phandle = <0x16>; - }; - - can@2800a000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800a000 0x00 0x1000>; - interrupts = <0x00 0x51 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - can@2800b000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800b000 0x00 0x1000>; - interrupts = <0x00 0x52 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - keypad@2807a000 { - compatible = "phytium,keypad"; - reg = <0x00 0x2807a000 0x00 0x1000>; - interrupts = <0x00 0xbd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb3@31a08000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a08000 0x00 0x18000>; - interrupts = <0x00 0x10 0x04>; - status = "okay"; - }; - - usb3@31a28000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a28000 0x00 0x18000>; - interrupts = <0x00 0x11 0x04>; - status = "okay"; - }; - - sata@31a40000 { - compatible = "generic-ahci"; - reg = <0x00 0x31a40000 0x00 0x1000>; - interrupts = <0x00 0x2a 0x04>; - status = "disabled"; - }; - - sata@32014000 { - compatible = "generic-ahci"; - reg = <0x00 0x32014000 0x00 0x1000>; - interrupts = <0x00 0x2b 0x04>; - status = "disabled"; - }; - - ethernet@3200c000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200c000 0x00 0x2000>; - interrupts = <0x00 0x37 0x04 0x00 0x38 0x04 0x00 0x39 0x04 0x00 0x3a 0x04 0x00 0x1c 0x04 0x00 0x1d 0x04 0x00 0x1e 0x04 0x00 0x1f 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - support-tsn; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@3200e000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200e000 0x00 0x2000>; - interrupts = <0x00 0x3b 0x04 0x00 0x3c 0x04 0x00 0x3d 0x04 0x00 0x3e 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@32010000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32010000 0x00 0x2000>; - interrupts = <0x00 0x40 0x04 0x00 0x41 0x04 0x00 0x42 0x04 0x00 0x43 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - ethernet@32012000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32012000 0x00 0x2000>; - interrupts = <0x00 0x44 0x04 0x00 0x45 0x04 0x00 0x46 0x04 0x00 0x47 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - vpu@32b00000 { - compatible = "phytium,vpu"; - reg = <0x00 0x32b00000 0x00 0x20000>; - interrupts = <0x00 0x0c 0x04>; - status = "okay"; - }; - - i2c@28026000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28026000 0x00 0x1000>; - interrupts = <0x00 0x65 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28030000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28030000 0x00 0x1000>; - interrupts = <0x00 0x6a 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - es8336@10 { - #sound-dai-cells = <0x00>; - compatible = "everest,es8336"; - reg = <0x10>; - phandle = <0x17>; - }; - }; - - uart@28014000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28014000 0x00 0x1000>; - interrupts = <0x00 0x5c 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - i2c@28016000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28016000 0x00 0x1000>; - interrupts = <0x00 0x5d 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28024000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28024000 0x00 0x1000>; - interrupts = <0x00 0x64 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - uart@2802A000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2802a000 0x00 0x1000>; - interrupts = <0x00 0x67 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@28032000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28032000 0x00 0x1000>; - interrupts = <0x00 0x6b 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 root=/dev/mmcblk0p1 rootfstype=ext4 rootwait rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory@00 { - device_type = "memory"; - reg = <0x20 0x40000000 0x00 0x40000000>; - }; - - leds { - compatible = "gpio-leds"; - - sysled { - label = "sysled"; - gpios = <0x14 0x05 0x00>; - linux,default-trigger = "none"; - }; - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "phytium,pe220x-i2s-audio"; - simple-audio-card,pin-switches = "mic-in"; - simple-audio-card,widgets = "Microphone\0mic-in\0Headphone\0Headphones"; - simple-audio-card,routing = "MIC2\0mic-in"; - simple-audio-card,hp-det-gpio = <0x15 0x0b 0x01>; - - simple-audio-card,cpu { - sound-dai = <0x16>; - }; - - simple-audio-card,codec { - sound-dai = <0x17>; - }; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.toml b/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.toml deleted file mode 100644 index 84d2ab001..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.toml +++ /dev/null @@ -1,63 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 2 -# The physical CPU ids. -phys_cpu_ids = [0x200, 0x00] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_4008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x20_4008_0000 -## The file path of the kernel image. -kernel_path = "/guest/linux/phytiumpi" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_4000_0000 -#dtb_path = "/path/to/axvisor/configs/vms/linux-aarch64-e2000_smp2.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_4000_0000, 0x4000_0000, 0x7, 1], # System RAM MAP_IDENTICAL - # [0xa000_0000, 0x2000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -interrupt_mode = "passthrough" -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.dts b/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.dts deleted file mode 100644 index a37731fbd..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.dts +++ /dev/null @@ -1,397 +0,0 @@ -/dts-v1/; - -/ { - interrupt-parent = <0x8002>; - dma-coherent; - model = "linux,dummy-virt"; - #size-cells = <0x02>; - #address-cells = <0x02>; - compatible = "linux,dummy-virt"; - - psci { - migrate = <0xc4000005>; - cpu_on = <0xc4000003>; - cpu_off = <0x84000002>; - cpu_suspend = <0xc4000001>; - method = "smc"; - compatible = "arm,psci-1.0\0arm,psci-0.2\0arm,psci"; - }; - - memory@80000000 { - reg = <0x00 0x80000000 0x00 0x40000000>; - device_type = "memory"; - }; - - platform-bus@c000000 { - interrupt-parent = <0x8002>; - ranges = <0x00 0x00 0xc000000 0x2000000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "qemu,platform\0simple-bus"; - }; - - fw-cfg@9020000 { - dma-coherent; - reg = <0x00 0x9020000 0x00 0x18>; - compatible = "qemu,fw-cfg-mmio"; - }; - - virtio_mmio@a000000 { - dma-coherent; - interrupts = <0x00 0x10 0x01>; - reg = <0x00 0xa000000 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000200 { - dma-coherent; - interrupts = <0x00 0x11 0x01>; - reg = <0x00 0xa000200 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000400 { - dma-coherent; - interrupts = <0x00 0x12 0x01>; - reg = <0x00 0xa000400 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000600 { - dma-coherent; - interrupts = <0x00 0x13 0x01>; - reg = <0x00 0xa000600 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000800 { - dma-coherent; - interrupts = <0x00 0x14 0x01>; - reg = <0x00 0xa000800 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000a00 { - dma-coherent; - interrupts = <0x00 0x15 0x01>; - reg = <0x00 0xa000a00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000c00 { - dma-coherent; - interrupts = <0x00 0x16 0x01>; - reg = <0x00 0xa000c00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000e00 { - dma-coherent; - interrupts = <0x00 0x17 0x01>; - reg = <0x00 0xa000e00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001000 { - dma-coherent; - interrupts = <0x00 0x18 0x01>; - reg = <0x00 0xa001000 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001200 { - dma-coherent; - interrupts = <0x00 0x19 0x01>; - reg = <0x00 0xa001200 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001400 { - dma-coherent; - interrupts = <0x00 0x1a 0x01>; - reg = <0x00 0xa001400 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001600 { - dma-coherent; - interrupts = <0x00 0x1b 0x01>; - reg = <0x00 0xa001600 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001800 { - dma-coherent; - interrupts = <0x00 0x1c 0x01>; - reg = <0x00 0xa001800 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001a00 { - dma-coherent; - interrupts = <0x00 0x1d 0x01>; - reg = <0x00 0xa001a00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001c00 { - dma-coherent; - interrupts = <0x00 0x1e 0x01>; - reg = <0x00 0xa001c00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001e00 { - dma-coherent; - interrupts = <0x00 0x1f 0x01>; - reg = <0x00 0xa001e00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002000 { - dma-coherent; - interrupts = <0x00 0x20 0x01>; - reg = <0x00 0xa002000 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002200 { - dma-coherent; - interrupts = <0x00 0x21 0x01>; - reg = <0x00 0xa002200 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002400 { - dma-coherent; - interrupts = <0x00 0x22 0x01>; - reg = <0x00 0xa002400 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002600 { - dma-coherent; - interrupts = <0x00 0x23 0x01>; - reg = <0x00 0xa002600 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002800 { - dma-coherent; - interrupts = <0x00 0x24 0x01>; - reg = <0x00 0xa002800 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002a00 { - dma-coherent; - interrupts = <0x00 0x25 0x01>; - reg = <0x00 0xa002a00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002c00 { - dma-coherent; - interrupts = <0x00 0x26 0x01>; - reg = <0x00 0xa002c00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002e00 { - dma-coherent; - interrupts = <0x00 0x27 0x01>; - reg = <0x00 0xa002e00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003000 { - dma-coherent; - interrupts = <0x00 0x28 0x01>; - reg = <0x00 0xa003000 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003200 { - dma-coherent; - interrupts = <0x00 0x29 0x01>; - reg = <0x00 0xa003200 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003400 { - dma-coherent; - interrupts = <0x00 0x2a 0x01>; - reg = <0x00 0xa003400 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003600 { - dma-coherent; - interrupts = <0x00 0x2b 0x01>; - reg = <0x00 0xa003600 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003800 { - dma-coherent; - interrupts = <0x00 0x2c 0x01>; - reg = <0x00 0xa003800 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003a00 { - dma-coherent; - interrupts = <0x00 0x2d 0x01>; - reg = <0x00 0xa003a00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003c00 { - dma-coherent; - interrupts = <0x00 0x2e 0x01>; - reg = <0x00 0xa003c00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003e00 { - dma-coherent; - interrupts = <0x00 0x2f 0x01>; - reg = <0x00 0xa003e00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - gpio-keys { - compatible = "gpio-keys"; - - poweroff { - gpios = <0x8004 0x03 0x00>; - linux,code = <0x74>; - label = "GPIO Key Poweroff"; - }; - }; - - pl061@9030000 { - phandle = <0x8004>; - clock-names = "apb_pclk"; - clocks = <0x8000>; - interrupts = <0x00 0x07 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - compatible = "arm,pl061\0arm,primecell"; - reg = <0x00 0x9030000 0x00 0x1000>; - }; - - // pcie@10000000 { - // interrupt-map-mask = <0x1800 0x00 0x00 0x07>; - // interrupt-map = <0x00 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x05 0x04>; - // #interrupt-cells = <0x01>; - // ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>; - // reg = <0x40 0x10000000 0x00 0x10000000>; - // msi-map = <0x00 0x8003 0x00 0x10000>; - // dma-coherent; - // bus-range = <0x00 0xff>; - // linux,pci-domain = <0x00>; - // #size-cells = <0x02>; - // #address-cells = <0x03>; - // device_type = "pci"; - // compatible = "pci-host-ecam-generic"; - // }; - - pl031@9010000 { - clock-names = "apb_pclk"; - clocks = <0x8000>; - interrupts = <0x00 0x02 0x04>; - reg = <0x00 0x9010000 0x00 0x1000>; - compatible = "arm,pl031\0arm,primecell"; - }; - - pl011@9000000 { - clock-names = "uartclk\0apb_pclk"; - clocks = <0x8000 0x8000>; - interrupts = <0x00 0x01 0x04>; - reg = <0x00 0x9000000 0x00 0x1000>; - compatible = "arm,pl011\0arm,primecell"; - }; - - pmu { - interrupts = <0x01 0x07 0x04>; - compatible = "arm,armv8-pmuv3"; - }; - - intc@8000000 { - phandle = <0x8002>; - interrupts = <0x01 0x09 0x04>; - reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; - #redistributor-regions = <0x01>; - compatible = "arm,gic-v3"; - ranges; - #size-cells = <0x02>; - #address-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x03>; - - its@8080000 { - phandle = <0x8003>; - reg = <0x00 0x8080000 0x00 0x20000>; - #msi-cells = <0x01>; - msi-controller; - compatible = "arm,gic-v3-its"; - }; - }; - - flash@0 { - bank-width = <0x04>; - reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>; - compatible = "cfi-flash"; - }; - - cpus { - #size-cells = <0x00>; - #address-cells = <0x01>; - - cpu-map { - - socket0 { - - cluster0 { - - core0 { - cpu = <0x8001>; - }; - }; - }; - }; - - cpu@0 { - phandle = <0x8001>; - reg = <0x00>; - compatible = "arm,cortex-a72"; - device_type = "cpu"; - }; - }; - - timer { - interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>; - always-on; - compatible = "arm,armv8-timer\0arm,armv7-timer"; - }; - - apb-pclk { - phandle = <0x8000>; - clock-output-names = "clk24mhz"; - clock-frequency = <0x16e3600>; - #clock-cells = <0x00>; - compatible = "fixed-clock"; - }; - - aliases { - serial0 = "/pl011@9000000"; - }; - - chosen { - bootargs = "root=/dev/vda rw init=/init"; - stdout-path = "/pl011@9000000"; - rng-seed = <0xc63e7c7f 0x25869631 0xfa243fbb 0xe48363de 0xfd9dc3ce 0x5127dfe3 0x88de6403 0xb04a0eef>; - kaslr-seed = <0xdcf4331d 0x73a35db>; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml b/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml deleted file mode 100644 index b9b90e6c4..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml +++ /dev/null @@ -1,70 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux-qemu" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu sets. -phys_cpu_ids = [0] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8020_0000 -# The location of image: "memory" | "fs". -# load from memory. -image_location = "memory" -# The file path of the kernel image. -# kernel_path = "linux-6.6.62.bin" -kernel_path = "/tmp/.axvisor-images/qemu_aarch64_linux/qemu-aarch64" -# The load address of the kernel image. -kernel_load_addr = 0x8020_0000 -# The file path of the device tree blob (DTB). -#dtb_path = "tmp/linux-aarch64-qemu-smp1.dtb" -# The load address of the device tree blob (DTB). -dtb_load_addr = 0x8000_0000 - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] - -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [ - # ["gppt-gicd", 0x0800_0000, 0x1_0000, 0, 0x21, []], - # ["gppt-gicr", 0x080a_0000, 0x2_0000, 0, 0x20, [1, 0x2_0000, 0]], # 1 vcpu, stride 0x20000, starts with pcpu 0 - # ["gppt-gits", 0x0808_0000, 0x2_0000, 0, 0x22, [0x0808_0000]], # host_gits_base -] - -interrupt_mode = "passthrough" diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.dts b/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.dts deleted file mode 100644 index bdc546145..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.dts +++ /dev/null @@ -1,6108 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000008300000 0x000000000001c000; -/memreserve/ 0x000000000a200000 0x00000000008cf15d; -/ { - serial-number = "425ca8fc29ade692"; - compatible = "rockchip,rk3568-firefly-roc-pc-se\0rockchip,rk3568"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Firefly RK3568-ROC-PC-SE HDMI (Linux)"; - - memory { - reg = <0x00 0x80000000 0x00 0x60000000>; - device_type = "memory"; - }; - - ddr3-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x12c>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x21>; - phy_ca_drv_odten = <0x21>; - phy_clk_drv_odten = <0x21>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x21>; - phy_ca_drv_odtoff = <0x21>; - phy_clk_drv_odtoff = <0x21>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0xa7>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x14d>; - phy_odt_en_freq = <0x14d>; - phy_dq_sr_odten = <0x0f>; - phy_ca_sr_odten = <0x03>; - phy_clk_sr_odten = <0x00>; - phy_dq_sr_odtoff = <0x0f>; - phy_ca_sr_odtoff = <0x03>; - phy_clk_sr_odtoff = <0x00>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x15>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - phandle = <0xb7>; - }; - - ddr4-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x271>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x25>; - phy_ca_drv_odten = <0x25>; - phy_clk_drv_odten = <0x25>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x25>; - phy_ca_drv_odtoff = <0x25>; - phy_clk_drv_odtoff = <0x25>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0x8b>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x1f4>; - phy_odt_en_freq = <0x1f4>; - phy_dq_sr_odten = <0x0e>; - phy_ca_sr_odten = <0x01>; - phy_clk_sr_odten = <0x01>; - phy_dq_sr_odtoff = <0x0e>; - phy_ca_sr_odtoff = <0x01>; - phy_clk_sr_odtoff = <0x01>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x0c>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x22777788>; - dq_map_cs0_dq_h = <0xd7888877>; - dq_map_cs1_dq_l = <0x22777788>; - dq_map_cs1_dq_h = <0xd7888877>; - phandle = <0xb8>; - }; - - lpddr3-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x25>; - phy_ca_drv_odten = <0x25>; - phy_clk_drv_odten = <0x27>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x25>; - phy_ca_drv_odtoff = <0x25>; - phy_clk_drv_odtoff = <0x27>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0x94>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x14d>; - phy_odt_en_freq = <0x14d>; - phy_dq_sr_odten = <0x0f>; - phy_ca_sr_odten = <0x01>; - phy_clk_sr_odten = <0x0f>; - phy_dq_sr_odtoff = <0x0f>; - phy_ca_sr_odtoff = <0x01>; - phy_clk_sr_odtoff = <0x0f>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0x8d>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - phandle = <0xb9>; - }; - - lpddr4-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x618>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x1e>; - phy_ca_drv_odten = <0x26>; - phy_clk_drv_odten = <0x26>; - dram_dq_drv_odten = <0x28>; - phy_dq_drv_odtoff = <0x1e>; - phy_ca_drv_odtoff = <0x26>; - phy_clk_drv_odtoff = <0x26>; - dram_dq_drv_odtoff = <0x28>; - dram_odt = <0x50>; - phy_odt = <0x3c>; - phy_odt_puup_en = <0x00>; - phy_odt_pudn_en = <0x00>; - dram_dq_odt_en_freq = <0x320>; - phy_odt_en_freq = <0x320>; - phy_dq_sr_odten = <0x00>; - phy_ca_sr_odten = <0x0f>; - phy_clk_sr_odten = <0x0f>; - phy_dq_sr_odtoff = <0x00>; - phy_ca_sr_odtoff = <0x0f>; - phy_clk_sr_odtoff = <0x0f>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - lp4_ca_odt = <0x78>; - lp4_drv_pu_cal_odten = <0x01>; - lp4_drv_pu_cal_odtoff = <0x01>; - phy_lp4_drv_pulldown_en_odten = <0x00>; - phy_lp4_drv_pulldown_en_odtoff = <0x00>; - lp4_ca_odt_en_freq = <0x320>; - phy_lp4_cs_drv_odten = <0x00>; - phy_lp4_cs_drv_odtoff = <0x00>; - lp4_odte_ck_en = <0x01>; - lp4_odte_cs_en = <0x01>; - lp4_odtd_ca_en = <0x00>; - phy_lp4_dq_vref_odten = <0xa6>; - lp4_dq_vref_odten = <0x12c>; - lp4_ca_vref_odten = <0x17c>; - phy_lp4_dq_vref_odtoff = <0x1a4>; - lp4_dq_vref_odtoff = <0x1a4>; - lp4_ca_vref_odtoff = <0x1a4>; - phandle = <0xba>; - }; - - lpddr4x-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x618>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x1d>; - phy_ca_drv_odten = <0x24>; - phy_clk_drv_odten = <0x24>; - dram_dq_drv_odten = <0x28>; - phy_dq_drv_odtoff = <0x1d>; - phy_ca_drv_odtoff = <0x24>; - phy_clk_drv_odtoff = <0x24>; - dram_dq_drv_odtoff = <0x28>; - dram_odt = <0x50>; - phy_odt = <0x3c>; - phy_odt_puup_en = <0x00>; - phy_odt_pudn_en = <0x00>; - dram_dq_odt_en_freq = <0x320>; - phy_odt_en_freq = <0x320>; - phy_dq_sr_odten = <0x00>; - phy_ca_sr_odten = <0x00>; - phy_clk_sr_odten = <0x00>; - phy_dq_sr_odtoff = <0x00>; - phy_ca_sr_odtoff = <0x00>; - phy_clk_sr_odtoff = <0x00>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - lp4_ca_odt = <0x78>; - lp4_drv_pu_cal_odten = <0x00>; - lp4_drv_pu_cal_odtoff = <0x00>; - phy_lp4_drv_pulldown_en_odten = <0x00>; - phy_lp4_drv_pulldown_en_odtoff = <0x00>; - lp4_ca_odt_en_freq = <0x320>; - phy_lp4_cs_drv_odten = <0x00>; - phy_lp4_cs_drv_odtoff = <0x00>; - lp4_odte_ck_en = <0x00>; - lp4_odte_cs_en = <0x00>; - lp4_odtd_ca_en = <0x00>; - phy_lp4_dq_vref_odten = <0xa6>; - lp4_dq_vref_odten = <0xe4>; - lp4_ca_vref_odten = <0x157>; - phy_lp4_dq_vref_odtoff = <0x1a4>; - lp4_dq_vref_odtoff = <0x1a4>; - lp4_ca_vref_odtoff = <0x157>; - phandle = <0xbb>; - }; - - aliases { - csi2dphy0 = "/csi2-dphy0"; - csi2dphy1 = "/csi2-dphy1"; - csi2dphy2 = "/csi2-dphy2"; - dsi0 = "/dsi@fe060000"; - dsi1 = "/dsi@fe070000"; - ethernet0 = "/ethernet@fe2a0000"; - ethernet1 = "/ethernet@fe010000"; - gpio0 = "/pinctrl/gpio0@fdd60000"; - gpio1 = "/pinctrl/gpio1@fe740000"; - gpio2 = "/pinctrl/gpio2@fe750000"; - gpio3 = "/pinctrl/gpio3@fe760000"; - gpio4 = "/pinctrl/gpio4@fe770000"; - i2c0 = "/i2c@fdd40000"; - i2c1 = "/i2c@fe5a0000"; - i2c2 = "/i2c@fe5b0000"; - i2c3 = "/i2c@fe5c0000"; - i2c4 = "/i2c@fe5d0000"; - i2c5 = "/i2c@fe5e0000"; - mmc0 = "/sdhci@fe310000"; - mmc1 = "/dwmmc@fe2b0000"; - mmc2 = "/dwmmc@fe2c0000"; - mmc3 = "/dwmmc@fe000000"; - serial0 = "/serial@fdd50000"; - serial1 = "/serial@fe650000"; - serial2 = "/serial@fe660000"; - serial3 = "/serial@fe670000"; - serial4 = "/serial@fe680000"; - serial5 = "/serial@fe690000"; - serial6 = "/serial@fe6a0000"; - serial7 = "/serial@fe6b0000"; - serial8 = "/serial@fe6c0000"; - serial9 = "/serial@fe6d0000"; - spi0 = "/spi@fe610000"; - spi1 = "/spi@fe620000"; - spi2 = "/spi@fe630000"; - spi3 = "/spi@fe640000"; - lvds0 = "/syscon@fdc60000/lvds"; - lvds1 = "/syscon@fdc60000/lvds1"; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x00>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - #cooling-cells = <0x02>; - dynamic-power-coefficient = <0xbb>; - cpu-supply = <0x05>; - phandle = <0x0c>; - }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x00 0x100>; - // enable-method = "psci"; - // clocks = <0x02 0x00>; - // operating-points-v2 = <0x03>; - // cpu-idle-states = <0x04>; - // phandle = <0x0d>; - // }; - - // cpu@200 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x00 0x200>; - // enable-method = "psci"; - // clocks = <0x02 0x00>; - // operating-points-v2 = <0x03>; - // cpu-idle-states = <0x04>; - // phandle = <0x0e>; - // }; - - // cpu@300 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x00 0x300>; - // enable-method = "psci"; - // clocks = <0x02 0x00>; - // operating-points-v2 = <0x03>; - // cpu-idle-states = <0x04>; - // phandle = <0x0f>; - // }; - - idle-states { - entry-method = "psci"; - - cpu-sleep { - compatible = "arm,idle-state"; - local-timer-stop; - arm,psci-suspend-param = <0x10000>; - entry-latency-us = <0x64>; - exit-latency-us = <0x78>; - min-residency-us = <0x3e8>; - phandle = <0x04>; - }; - }; - }; - - cpu0-opp-table { - compatible = "operating-points-v2"; - opp-shared; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x06 0x07 0x08 0x09 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0x118c30>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-freq = <0x639c0>; - rockchip,pvtm-volt = <0xdbba0>; - rockchip,pvtm-ch = <0x00 0x05>; - rockchip,pvtm-sample-time = <0x3e8>; - rockchip,pvtm-number = <0x0a>; - rockchip,pvtm-error = <0x3e8>; - rockchip,pvtm-ref-temp = <0x28>; - rockchip,pvtm-temp-prop = <0x1a 0x1a>; - rockchip,thermal-zone = "soc-thermal"; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x7c8 0x124f8>; - phandle = <0x03>; - - opp-408000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x18519600>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-816000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x30a32c00>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - opp-suspend; - }; - - opp-1104000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x41cdb400>; - opp-microvolt = <0xdbba0 0xdbba0 0x118c30>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0x118c30>; - opp-microvolt-L1 = <0xcf850 0xcf850 0x118c30>; - opp-microvolt-L2 = <0xcf850 0xcf850 0x118c30>; - opp-microvolt-L3 = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1416000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x54667200>; - opp-microvolt = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L0 = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L1 = <0xee098 0xee098 0x118c30>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0x118c30>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1608000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x5fd82200>; - opp-microvolt = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L0 = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L1 = <0x100590 0x100590 0x118c30>; - opp-microvolt-L2 = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L3 = <0xf4240 0xf4240 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1800000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x6b49d200>; - opp-microvolt = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L0 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L1 = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L2 = <0x106738 0x106738 0x118c30>; - opp-microvolt-L3 = <0x100590 0x100590 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1992000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x76bb8200>; - opp-microvolt = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L0 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L1 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L2 = <0x112a88 0x112a88 0x118c30>; - opp-microvolt-L3 = <0x10c8e0 0x10c8e0 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-j-1008000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x3c14dc00>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-j-1416000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x54667200>; - opp-microvolt = <0xdbba0 0xdbba0 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-m-1608000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x5fd82200>; - opp-microvolt = <0xf4240 0xf4240 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - }; - - arm-pmu { - compatible = "arm,cortex-a55-pmu\0arm,armv8-pmuv3"; - interrupts = <0x00 0xe4 0x04 0x00 0xe5 0x04 0x00 0xe6 0x04 0x00 0xe7 0x04>; - interrupt-affinity = <0x0c 0x0d 0x0e 0x0f>; - }; - - cpuinfo { - compatible = "rockchip,cpuinfo"; - nvmem-cells = <0x10 0x11 0x12>; - nvmem-cell-names = "id\0cpu-version\0cpu-code"; - }; - - display-subsystem { - compatible = "rockchip,display-subsystem"; - memory-region = <0x13 0x14>; - memory-region-names = "drm-logo\0drm-cubic-lut"; - ports = <0x15>; - devfreq = <0x16>; - - route { - - route-dsi0 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x17>; - }; - - route-dsi1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x18>; - }; - - route-edp { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x19>; - }; - - route-hdmi { - status = "okay"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1a>; - }; - - route-lvds { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1b>; - }; - - route-rgb { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1c>; - }; - }; - }; - - edac { - compatible = "rockchip,rk3568-edac"; - interrupts = <0x00 0xad 0x04 0x00 0xaf 0x04>; - interrupt-names = "ce\0ue"; - status = "disabled"; - }; - - firmware { - - scmi { - compatible = "arm,scmi-smc"; - shmem = <0x1d>; - arm,smc-id = <0x82000010>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@14 { - reg = <0x14>; - #clock-cells = <0x01>; - rockchip,clk-init = <0x41cdb400>; - phandle = <0x02>; - }; - }; - - sdei { - compatible = "arm,sdei-1.0"; - method = "smc"; - }; - }; - - mipi-csi2 { - compatible = "rockchip,rk3568-mipi-csi2"; - rockchip,hw = <0x1e>; - status = "disabled"; - }; - - mpp-srv { - compatible = "rockchip,mpp-service"; - rockchip,taskqueue-count = <0x06>; - rockchip,resetgroup-count = <0x06>; - status = "okay"; - phandle = <0x7b>; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - }; - - reserved-memory { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - drm-logo@00000000 { - compatible = "rockchip,drm-logo"; - reg = <0x00 0xedf00000 0x00 0x2e0000>; - phandle = <0x13>; - }; - - drm-cubic-lut@00000000 { - compatible = "rockchip,drm-cubic-lut"; - reg = <0x00 0xeff00000 0x00 0x8000>; - phandle = <0x14>; - }; - - ramoops@110000 { - compatible = "ramoops"; - reg = <0x00 0x110000 0x00 0xf0000>; - record-size = <0x20000>; - console-size = <0x80000>; - ftrace-size = <0x00>; - pmsg-size = <0x50000>; - }; - }; - - rockchip-suspend { - compatible = "rockchip,pm-rk3568"; - status = "okay"; - rockchip,sleep-debug-en = <0x01>; - rockchip,sleep-mode-config = <0x5ec>; - rockchip,wakeup-config = <0x10>; - }; - - rockchip-system-monitor { - compatible = "rockchip,system-monitor"; - rockchip,thermal-zone = "soc-thermal"; - }; - - thermal-zones { - - soc-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - sustainable-power = <0x389>; - thermal-sensors = <0x1f 0x00>; - - trips { - - trip-point-0 { - temperature = <0x124f8>; - hysteresis = <0x7d0>; - type = "passive"; - }; - - trip-point-1 { - temperature = <0x14c08>; - hysteresis = <0x7d0>; - type = "passive"; - phandle = <0x20>; - }; - - soc-crit { - temperature = <0x1c138>; - hysteresis = <0x7d0>; - type = "critical"; - }; - }; - - cooling-maps { - - map0 { - trip = <0x20>; - cooling-device = <0x0c 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - - map1 { - trip = <0x20>; - cooling-device = <0x21 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - }; - }; - - gpu-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x1f 0x01>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; - arm,no-tick-in-suspend; - }; - - external-gmac0-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "gmac0_clkin"; - #clock-cells = <0x00>; - phandle = <0xc7>; - }; - - external-gmac1-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "gmac1_clkin"; - #clock-cells = <0x00>; - phandle = <0x92>; - }; - - xpcs-gmac0-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "clk_gmac0_xpcs_mii"; - #clock-cells = <0x00>; - }; - - xpcs-gmac1-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "clk_gmac1_xpcs_mii"; - #clock-cells = <0x00>; - }; - - i2s1-mclkin-rx { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s1_mclkin_rx"; - }; - - i2s1-mclkin-tx { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s1_mclkin_tx"; - }; - - i2s2-mclkin { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s2_mclkin"; - }; - - i2s3-mclkin { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s3_mclkin"; - }; - - mpll { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2faf0800>; - clock-output-names = "mpll"; - }; - - xin24m { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x16e3600>; - clock-output-names = "xin24m"; - }; - - xin32k { - compatible = "fixed-clock"; - clock-frequency = <0x8000>; - clock-output-names = "xin32k"; - #clock-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x22>; - }; - - scmi-shmem@10f000 { - compatible = "arm,scmi-shmem"; - reg = <0x00 0x10f000 0x00 0x100>; - phandle = <0x1d>; - }; - - sata@fc000000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc000000 0x00 0x1000>; - clocks = <0x23 0x96 0x23 0x97 0x23 0x98>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x5e 0x04>; - interrupt-names = "hostc"; - phys = <0x24 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "disabled"; - }; - - sata@fc400000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc400000 0x00 0x1000>; - clocks = <0x23 0x9b 0x23 0x9c 0x23 0x9d>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x5f 0x04>; - interrupt-names = "hostc"; - phys = <0x26 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "disabled"; - }; - - sata@fc800000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc800000 0x00 0x1000>; - clocks = <0x23 0xa0 0x23 0xa1 0x23 0xa2>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x60 0x04>; - interrupt-names = "hostc"; - phys = <0x27 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "okay"; - }; - - usbdrd { - compatible = "rockchip,rk3568-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x23 0xa6 0x23 0xa7 0x23 0xa5 0x23 0x7f>; - clock-names = "ref_clk\0suspend_clk\0bus_clk\0pipe_clk"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - dwc3@fcc00000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfcc00000 0x00 0x400000>; - interrupts = <0x00 0xa9 0x04>; - dr_mode = "otg"; - phys = <0x28 0x24 0x04>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - power-domains = <0x25 0x0f>; - resets = <0x23 0x94>; - reset-names = "usb3-otg"; - snps,dis_enblslpm_quirk; - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,dis_rxdet_inp3_quirk; - snps,parkmode-disable-hs-quirk; - snps,parkmode-disable-ss-quirk; - quirk-skip-phy-init; - status = "okay"; - extcon = <0x29>; - usb-role-switch; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x2a>; - phandle = <0x4d>; - }; - }; - }; - }; - - usbhost { - compatible = "rockchip,rk3568-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x23 0xa9 0x23 0xaa 0x23 0xa8 0x23 0x7f>; - clock-names = "ref_clk\0suspend_clk\0bus_clk\0pipe_clk"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - dwc3@fd000000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfd000000 0x00 0x400000>; - interrupts = <0x00 0xaa 0x04>; - dr_mode = "host"; - phys = <0x2b 0x26 0x04>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - power-domains = <0x25 0x0f>; - resets = <0x23 0x95>; - reset-names = "usb3-host"; - snps,dis_enblslpm_quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,dis_rxdet_inp3_quirk; - snps,parkmode-disable-hs-quirk; - snps,parkmode-disable-ss-quirk; - status = "okay"; - }; - }; - - interrupt-controller@fd400000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0xfd400000 0x00 0x10000 0x00 0xfd460000 0x00 0xc0000>; - interrupts = <0x01 0x09 0x04>; - phandle = <0x01>; - - interrupt-controller@fd440000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <0x01>; - reg = <0x00 0xfd440000 0x00 0x20000>; - status = "okay"; - phandle = <0xbe>; - }; - }; - - usb@fd800000 { - compatible = "generic-ehci"; - reg = <0x00 0xfd800000 0x00 0x40000>; - interrupts = <0x00 0x82 0x04>; - clocks = <0x23 0xbd 0x23 0xbe 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2d>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd840000 { - compatible = "generic-ohci"; - reg = <0x00 0xfd840000 0x00 0x40000>; - interrupts = <0x00 0x83 0x04>; - clocks = <0x23 0xbd 0x23 0xbe 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2d>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd880000 { - compatible = "generic-ehci"; - reg = <0x00 0xfd880000 0x00 0x40000>; - interrupts = <0x00 0x85 0x04>; - clocks = <0x23 0xbf 0x23 0xc0 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2e>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd8c0000 { - compatible = "generic-ohci"; - reg = <0x00 0xfd8c0000 0x00 0x40000>; - interrupts = <0x00 0x86 0x04>; - clocks = <0x23 0xbf 0x23 0xc0 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2e>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - syscon@fda00000 { - compatible = "rockchip,rk3568-xpcs\0syscon"; - reg = <0x00 0xfda00000 0x00 0x200000>; - status = "disabled"; - }; - - syscon@fdc20000 { - compatible = "rockchip,rk3568-pmugrf\0syscon\0simple-mfd"; - reg = <0x00 0xfdc20000 0x00 0x10000>; - phandle = <0x3c>; - - io-domains { - compatible = "rockchip,rk3568-pmu-io-voltage-domain"; - status = "okay"; - pmuio1-supply = <0x2f>; - pmuio2-supply = <0x2f>; - vccio1-supply = <0x30>; - vccio3-supply = <0x31>; - vccio4-supply = <0x32>; - vccio5-supply = <0x33>; - vccio6-supply = <0x32>; - vccio7-supply = <0x33>; - }; - - reboot-mode { - compatible = "syscon-reboot-mode"; - offset = <0x200>; - mode-bootloader = <0x5242c301>; - mode-charge = <0x5242c30b>; - mode-fastboot = <0x5242c309>; - mode-loader = <0x5242c301>; - mode-normal = <0x5242c300>; - mode-recovery = <0x5242c303>; - mode-ums = <0x5242c30c>; - mode-panic = <0x5242c307>; - mode-watchdog = <0x5242c308>; - }; - }; - - syscon@fdc50000 { - compatible = "rockchip,rk3568-pipegrf\0syscon"; - reg = <0x00 0xfdc50000 0x00 0x1000>; - phandle = <0x12a>; - }; - - syscon@fdc60000 { - compatible = "rockchip,rk3568-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfdc60000 0x00 0x10000>; - phandle = <0x3b>; - - io-domains { - compatible = "rockchip,rk3568-io-voltage-domain"; - status = "disabled"; - }; - - lvds { - compatible = "rockchip,rk3568-lvds"; - phys = <0x34>; - phy-names = "phy"; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x1b>; - status = "disabled"; - phandle = <0xa3>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x35>; - status = "disabled"; - phandle = <0xa5>; - }; - }; - }; - }; - - lvds1 { - compatible = "rockchip,rk3568-lvds"; - phys = <0x36>; - phy-names = "phy"; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x37>; - phandle = <0xa4>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x38>; - phandle = <0xa7>; - }; - }; - }; - }; - - rgb { - compatible = "rockchip,rk3568-rgb"; - pinctrl-names = "default"; - pinctrl-0 = <0x39>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x1c>; - status = "disabled"; - phandle = <0xa6>; - }; - }; - }; - }; - }; - - syscon@fdc70000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc70000 0x00 0x1000>; - phandle = <0x12b>; - }; - - syscon@fdc80000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc80000 0x00 0x1000>; - phandle = <0x12c>; - }; - - syscon@fdc90000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc90000 0x00 0x1000>; - phandle = <0x12d>; - }; - - syscon@fdca0000 { - compatible = "rockchip,rk3568-usb2phy-grf\0syscon"; - reg = <0x00 0xfdca0000 0x00 0x8000>; - phandle = <0x135>; - }; - - syscon@fdca8000 { - compatible = "rockchip,rk3568-usb2phy-grf\0syscon"; - reg = <0x00 0xfdca8000 0x00 0x8000>; - phandle = <0x137>; - }; - - syscon@fdcb0000 { - compatible = "rockchip,rk3568-edp-phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfdcb0000 0x00 0x100>; - clocks = <0x23 0x192>; - - edp-phy { - compatible = "rockchip,rk3568-edp-phy"; - clocks = <0x3a 0x29>; - clock-names = "refclk"; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0xae>; - }; - }; - - syscon@fdcb8000 { - compatible = "rockchip,pcie30-phy-grf\0syscon"; - reg = <0x00 0xfdcb8000 0x00 0x10000>; - phandle = <0x138>; - }; - - sram@fdcc0000 { - compatible = "mmio-sram"; - reg = <0x00 0xfdcc0000 0x00 0xb000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0xfdcc0000 0xb000>; - - rkvdec-sram@0 { - reg = <0x00 0xb000>; - phandle = <0x84>; - }; - }; - - clock-controller@fdd00000 { - compatible = "rockchip,rk3568-pmucru"; - reg = <0x00 0xfdd00000 0x00 0x1000>; - rockchip,grf = <0x3b>; - rockchip,pmugrf = <0x3c>; - #clock-cells = <0x01>; - #reset-cells = <0x01>; - assigned-clocks = <0x3a 0x32>; - assigned-clock-parents = <0x3a 0x05>; - phandle = <0x3a>; - }; - - clock-controller@fdd20000 { - compatible = "rockchip,rk3568-cru"; - reg = <0x00 0xfdd20000 0x00 0x1000>; - rockchip,grf = <0x3b>; - #clock-cells = <0x01>; - #reset-cells = <0x01>; - assigned-clocks = <0x3a 0x05 0x23 0x106 0x23 0x10b 0x3a 0x01 0x3a 0x2b 0x23 0x03 0x23 0x19b 0x23 0x09 0x23 0x19c 0x23 0x19d 0x23 0x1a1 0x23 0x19e 0x23 0x19f 0x23 0x1a0 0x23 0x04 0x23 0x10d 0x23 0x10e 0x23 0x173 0x23 0x174 0x23 0x175 0x23 0x176 0x23 0xc9 0x23 0xca 0x23 0x06 0x23 0x7e 0x23 0x7f 0x23 0x3d 0x23 0x41 0x23 0x45 0x23 0x49 0x23 0x4d 0x23 0x4d 0x23 0x55 0x23 0x51 0x23 0x5d 0x23 0xdd>; - assigned-clock-rates = <0x8000 0x11e1a300 0x11e1a300 0xbebc200 0x5f5e100 0x3b9aca00 0x1dcd6500 0x13d92d40 0xee6b280 0x7735940 0x5f5e100 0x3b9aca0 0x2faf080 0x17d7840 0x46cf7100 0x8f0d180 0x5f5e100 0x1dcd6500 0x17d78400 0x8f0d180 0x5f5e100 0x11e1a300 0x8f0d180 0x47868c00 0x17d78400 0x5f5e100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x1dcd6500>; - assigned-clock-parents = <0x3a 0x08 0x23 0x04 0x23 0x04>; - phandle = <0x23>; - }; - - i2c@fdd40000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfdd40000 0x00 0x1000>; - clocks = <0x3a 0x07 0x3a 0x2d>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x2e 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x3d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - tcs4525@1c { - compatible = "tcs,tcs452x"; - reg = <0x1c>; - vin-supply = <0x3e>; - regulator-compatible = "fan53555-reg"; - regulator-name = "vdd_cpu"; - regulator-min-microvolt = <0xadf34>; - regulator-max-microvolt = <0x1535b0>; - regulator-ramp-delay = <0x8fc>; - fcs,suspend-voltage-selector = <0x01>; - regulator-boot-on; - regulator-always-on; - phandle = <0x05>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - pmic@20 { - compatible = "rockchip,rk809"; - reg = <0x20>; - interrupt-parent = <0x3f>; - interrupts = <0x03 0x08>; - pinctrl-names = "default\0pmic-sleep\0pmic-power-off\0pmic-reset"; - pinctrl-0 = <0x40>; - pinctrl-1 = <0x41 0x42>; - pinctrl-2 = <0x43 0x44>; - pinctrl-3 = <0x43 0x45>; - rockchip,system-power-controller; - wakeup-source; - #clock-cells = <0x01>; - clock-output-names = "rk808-clkout1\0rk808-clkout2"; - pmic-reset-func = <0x00>; - not-save-power-en = <0x01>; - vcc1-supply = <0x46>; - vcc2-supply = <0x46>; - vcc3-supply = <0x46>; - vcc4-supply = <0x46>; - vcc5-supply = <0x46>; - vcc6-supply = <0x46>; - vcc7-supply = <0x46>; - vcc8-supply = <0x46>; - vcc9-supply = <0x46>; - phandle = <0x152>; - - pwrkey { - status = "okay"; - }; - - pinctrl_rk8xx { - gpio-controller; - #gpio-cells = <0x02>; - - rk817_slppin_null { - pins = "gpio_slp"; - function = "pin_fun0"; - }; - - rk817_slppin_slp { - pins = "gpio_slp"; - function = "pin_fun1"; - phandle = <0x42>; - }; - - rk817_slppin_pwrdn { - pins = "gpio_slp"; - function = "pin_fun2"; - phandle = <0x44>; - }; - - rk817_slppin_rst { - pins = "gpio_slp"; - function = "pin_fun3"; - phandle = <0x45>; - }; - }; - - regulators { - - DCDC_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_logic"; - phandle = <0x75>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_gpu"; - phandle = <0x77>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-initial-mode = <0x02>; - regulator-name = "vcc_ddr"; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - DCDC_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_npu"; - phandle = <0x71>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG1 { - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda0v9_image"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda_0v9"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda0v9_pmu"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0xdbba0>; - }; - }; - - LDO_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vccio_acodec"; - phandle = <0x30>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vccio_sd"; - phandle = <0x31>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG6 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vcc3v3_pmu"; - phandle = <0x2f>; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x325aa0>; - }; - }; - - LDO_REG7 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca_1v8"; - phandle = <0x129>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG8 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca1v8_pmu"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x1b7740>; - }; - }; - - LDO_REG9 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca1v8_image"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcc_1v8"; - phandle = <0x32>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - SWITCH_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vcc_3v3"; - phandle = <0x33>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - SWITCH_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vcc3v3_sd"; - phandle = <0xcf>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - - codec { - #sound-dai-cells = <0x00>; - compatible = "rockchip,rk809-codec\0rockchip,rk817-codec"; - clocks = <0x23 0x1a3>; - clock-names = "mclk"; - assigned-clocks = <0x23 0x1a3 0x23 0x1a6>; - assigned-clock-rates = <0xbb8000>; - assigned-clock-parents = <0x23 0x48 0x23 0x48>; - pinctrl-names = "default\0spk_gpio"; - pinctrl-0 = <0x47>; - pinctrl-1 = <0x48>; - hp-volume = <0x03>; - spk-volume = <0x03>; - mic-in-differential; - board-spk-from-hp; - capture-volume = <0x00>; - io-channels = <0x49 0x07>; - hp-det-adc-value = <0x3e8>; - status = "okay"; - hp-adc-drift-scope = <0x64>; - phandle = <0x14b>; - }; - - rtc { - status = "disabled"; - }; - }; - - fusb302@22 { - compatible = "fcs,fusb302"; - reg = <0x22>; - interrupt-parent = <0x3f>; - fcs,int_n = <0x3f 0x11 0x08>; - fusb340-switch-gpios = <0x4a 0x12 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4b>; - vbus-supply = <0x4c>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x4d>; - phandle = <0x2a>; - }; - }; - }; - - connector { - compatible = "usb-c-connector"; - label = "USB-C"; - data-role = "dual"; - power-role = "dual"; - try-power-role = "sink"; - op-sink-microwatt = <0xf4240>; - sink-pdos = <0x40190fa>; - source-pdos = <0x4019096>; - }; - }; - }; - - serial@fdd50000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfdd50000 0x00 0x100>; - interrupts = <0x00 0x74 0x04>; - clocks = <0x3a 0x0b 0x3a 0x2c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x00 0x4e 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x4f>; - status = "disabled"; - }; - - pwm@fdd70000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70000 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x50>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70010 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x51>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70020 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x52>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70030 0x00 0x10>; - interrupts = <0x00 0x52 0x04 0x00 0x56 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x53>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - power-management@fdd90000 { - compatible = "rockchip,rk3568-pmu\0syscon\0simple-mfd"; - reg = <0x00 0xfdd90000 0x00 0x1000>; - - power-controller { - compatible = "rockchip,rk3568-power-controller"; - #power-domain-cells = <0x01>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x25>; - - pd_npu@6 { - reg = <0x06>; - clocks = <0x23 0x27 0x23 0x25 0x23 0x26>; - pm_qos = <0x54>; - }; - - pd_gpu@7 { - reg = <0x07>; - clocks = <0x23 0x19 0x23 0x1a>; - pm_qos = <0x55>; - }; - - pd_vi@8 { - reg = <0x08>; - clocks = <0x23 0xcc 0x23 0xcd>; - pm_qos = <0x56 0x57 0x58>; - }; - - pd_vo@9 { - reg = <0x09>; - clocks = <0x23 0xda 0x23 0xdb 0x23 0xdc>; - pm_qos = <0x59 0x5a 0x5b>; - }; - - pd_rga@10 { - reg = <0x0a>; - clocks = <0x23 0xf1 0x23 0xf2>; - pm_qos = <0x5c 0x5d 0x5e 0x5f 0x60 0x61>; - }; - - pd_vpu@11 { - reg = <0x0b>; - clocks = <0x23 0xed>; - pm_qos = <0x62>; - }; - - pd_rkvdec@13 { - clocks = <0x23 0x107>; - reg = <0x0d>; - pm_qos = <0x63>; - }; - - pd_rkvenc@14 { - reg = <0x0e>; - clocks = <0x23 0x102>; - pm_qos = <0x64 0x65 0x66>; - }; - - pd_pipe@15 { - reg = <0x0f>; - clocks = <0x23 0x7f>; - pm_qos = <0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e>; - }; - }; - }; - - pvtm@fde00000 { - compatible = "rockchip,rk3568-core-pvtm"; - reg = <0x00 0xfde00000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@0 { - reg = <0x00>; - clocks = <0x23 0x13 0x23 0x1c2>; - clock-names = "clk\0pclk"; - resets = <0x23 0x1a 0x23 0x19>; - reset-names = "rts\0rst-p"; - thermal-zone = "soc-thermal"; - }; - }; - - npu@fde40000 { - compatible = "rockchip,rk3568-rknpu\0rockchip,rknpu"; - reg = <0x00 0xfde40000 0x00 0x10000>; - interrupts = <0x00 0x97 0x04>; - clocks = <0x02 0x02 0x23 0x23 0x23 0x28 0x23 0x29>; - clock-names = "scmi_clk\0clk\0aclk\0hclk"; - assigned-clocks = <0x23 0x23>; - assigned-clock-rates = <0x23c34600>; - resets = <0x23 0x2b 0x23 0x2c>; - reset-names = "srst_a\0srst_h"; - power-domains = <0x25 0x06>; - operating-points-v2 = <0x6f>; - iommus = <0x70>; - status = "okay"; - rknpu-supply = <0x71>; - }; - - npu-opp-table { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x72 0x07 0x08 0x73 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x3e8 0xc350>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x6f>; - - opp-200000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0xbebc200>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-300000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-400000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-700000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L0 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L1 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L2 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L3 = <0xcf850 0xcf850 0xf4240>; - }; - - opp-800000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L0 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L1 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L2 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L3 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-900000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xee098 0xee098 0xf4240>; - opp-microvolt-L0 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L1 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L2 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L3 = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-1000000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L0 = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L1 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0xf4240>; - status = "disabled"; - }; - - opp-j-600000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-m-900000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xe1d48 0xe1d48 0xf4240>; - }; - }; - - bus-npu { - compatible = "rockchip,rk3568-bus"; - rockchip,busfreq-policy = "clkfreq"; - clocks = <0x02 0x02>; - clock-names = "bus"; - operating-points-v2 = <0x74>; - status = "okay"; - bus-supply = <0x75>; - pvtm-supply = <0x05>; - }; - - bus-npu-opp-table { - compatible = "operating-points-v2"; - opp-shared; - nvmem-cells = <0x07>; - nvmem-cell-names = "pvtm"; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x16378 0x01 0x16379 0x186a0 0x02>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x74>; - - opp-700000000 { - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - opp-microvolt-L2 = <0xd59f8>; - }; - - opp-900000000 { - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xdbba0>; - }; - - opp-1000000000 { - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xe7ef0>; - opp-microvolt-L0 = <0xe7ef0>; - opp-microvolt-L1 = <0xe1d48>; - opp-microvolt-L2 = <0xdbba0>; - }; - }; - - iommu@fde4b000 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfde4b000 0x00 0x40>; - interrupts = <0x00 0x97 0x04>; - interrupt-names = "rknpu_mmu"; - clocks = <0x23 0x28 0x23 0x29>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x06>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x70>; - }; - - gpu@fde60000 { - compatible = "arm,mali-bifrost"; - reg = <0x00 0xfde60000 0x00 0x4000>; - interrupts = <0x00 0x27 0x04 0x00 0x29 0x04 0x00 0x28 0x04>; - interrupt-names = "GPU\0MMU\0JOB"; - upthreshold = <0x28>; - downdifferential = <0x0a>; - clocks = <0x02 0x01 0x23 0x1b>; - clock-names = "clk_mali\0clk_gpu"; - power-domains = <0x25 0x07>; - #cooling-cells = <0x02>; - operating-points-v2 = <0x76>; - status = "okay"; - mali-supply = <0x77>; - phandle = <0x21>; - - power-model { - compatible = "simple-power-model"; - leakage-range = <0x05 0x0f>; - ls = <0xffffa23e 0x5927 0x00>; - static-coefficient = <0x186a0>; - dynamic-coefficient = <0x3b9>; - ts = <0xfffe56a6 0xf87a 0xfffffab5 0x14>; - thermal-zone = "gpu-thermal"; - }; - }; - - opp-table2 { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x78 0x07 0x08 0x79 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x320 0xc350>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x76>; - - opp-200000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0xbebc200>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-300000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x11e1a300>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-400000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L1 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L2 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L3 = <0xcf850 0xcf850 0xf4240>; - }; - - opp-700000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L0 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L1 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L2 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L3 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-800000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L0 = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L1 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0xf4240>; - }; - - opp-j-600000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-m-800000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xe7ef0 0xe7ef0 0xf4240>; - }; - }; - - pvtm@fde80000 { - compatible = "rockchip,rk3568-gpu-pvtm"; - reg = <0x00 0xfde80000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@1 { - reg = <0x01>; - clocks = <0x23 0x1e 0x23 0x1d>; - clock-names = "clk\0pclk"; - resets = <0x23 0x24 0x23 0x23>; - reset-names = "rts\0rst-p"; - thermal-zone = "gpu-thermal"; - }; - }; - - pvtm@fde90000 { - compatible = "rockchip,rk3568-npu-pvtm"; - reg = <0x00 0xfde90000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@2 { - reg = <0x02>; - clocks = <0x23 0x2b 0x23 0x2a 0x23 0x25>; - clock-names = "clk\0pclk\0hclk"; - resets = <0x23 0x2e 0x23 0x2d>; - reset-names = "rts\0rst-p"; - thermal-zone = "soc-thermal"; - }; - }; - - vdpu@fdea0400 { - compatible = "rockchip,vpu-decoder-v2"; - reg = <0x00 0xfdea0400 0x00 0x400>; - interrupts = <0x00 0x8b 0x04>; - interrupt-names = "irq_dec"; - clocks = <0x23 0xee 0x23 0xef>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - resets = <0x23 0x11a 0x23 0x11b>; - reset-names = "video_a\0video_h"; - iommus = <0x7a>; - power-domains = <0x25 0x0b>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x00>; - rockchip,resetgroup-node = <0x00>; - status = "okay"; - }; - - iommu@fdea0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdea0800 0x00 0x40>; - interrupts = <0x00 0x8a 0x04>; - interrupt-names = "vdpu_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xee 0x23 0xef>; - power-domains = <0x25 0x0b>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7a>; - }; - - rk_rga@fdeb0000 { - compatible = "rockchip,rga2"; - reg = <0x00 0xfdeb0000 0x00 0x1000>; - interrupts = <0x00 0x5a 0x04>; - clocks = <0x23 0xf3 0x23 0xf4 0x23 0xf5>; - clock-names = "aclk_rga\0hclk_rga\0clk_rga"; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - ebc@fdec0000 { - compatible = "rockchip,rk3568-ebc-tcon"; - reg = <0x00 0xfdec0000 0x00 0x5000>; - interrupts = <0x00 0x11 0x04>; - clocks = <0x23 0xf9 0x23 0xfa>; - clock-names = "hclk\0dclk"; - power-domains = <0x25 0x0a>; - rockchip,grf = <0x3b>; - pinctrl-names = "default"; - pinctrl-0 = <0x7c>; - status = "disabled"; - }; - - jpegd@fded0000 { - compatible = "rockchip,rkv-jpeg-decoder-v1"; - reg = <0x00 0xfded0000 0x00 0x400>; - interrupts = <0x00 0x3e 0x04>; - clocks = <0x23 0xfb 0x23 0xfc>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,disable-auto-freq; - resets = <0x23 0x12c 0x23 0x12d>; - reset-names = "video_a\0video_h"; - iommus = <0x7d>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x01>; - rockchip,resetgroup-node = <0x01>; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - iommu@fded0480 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfded0480 0x00 0x40>; - interrupts = <0x00 0x3d 0x04>; - interrupt-names = "jpegd_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xfb 0x23 0xfc>; - power-domains = <0x25 0x0a>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7d>; - }; - - vepu@fdee0000 { - compatible = "rockchip,vpu-encoder-v2"; - reg = <0x00 0xfdee0000 0x00 0x400>; - interrupts = <0x00 0x40 0x04>; - clocks = <0x23 0xfd 0x23 0xfe>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,disable-auto-freq; - resets = <0x23 0x12e 0x23 0x12f>; - reset-names = "video_a\0video_h"; - iommus = <0x7e>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x02>; - rockchip,resetgroup-node = <0x02>; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - iommu@fdee0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdee0800 0x00 0x40>; - interrupts = <0x00 0x3f 0x04>; - interrupt-names = "vepu_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xfd 0x23 0xfe>; - power-domains = <0x25 0x0a>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7e>; - }; - - iep@fdef0000 { - compatible = "rockchip,iep-v2"; - reg = <0x00 0xfdef0000 0x00 0x500>; - interrupts = <0x00 0x38 0x04>; - clocks = <0x23 0xf6 0x23 0xf7 0x23 0xf8>; - clock-names = "aclk\0hclk\0sclk"; - resets = <0x23 0x127 0x23 0x128 0x23 0x129>; - reset-names = "rst_a\0rst_h\0rst_s"; - power-domains = <0x25 0x0a>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x05>; - rockchip,resetgroup-node = <0x05>; - iommus = <0x7f>; - status = "okay"; - }; - - iommu@fdef0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdef0800 0x00 0x100>; - interrupts = <0x00 0x38 0x04>; - interrupt-names = "iep_mmu"; - clocks = <0x23 0xf6 0x23 0xf7>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - power-domains = <0x25 0x0a>; - status = "okay"; - phandle = <0x7f>; - }; - - eink@fdf00000 { - compatible = "rockchip,rk3568-eink-tcon"; - reg = <0x00 0xfdf00000 0x00 0x74>; - interrupts = <0x00 0xb2 0x04>; - clocks = <0x23 0xff 0x23 0x100>; - clock-names = "pclk\0hclk"; - status = "disabled"; - }; - - rkvenc@fdf40000 { - compatible = "rockchip,rkv-encoder-v1"; - reg = <0x00 0xfdf40000 0x00 0x400>; - interrupts = <0x00 0x8c 0x04>; - interrupt-names = "irq_enc"; - clocks = <0x23 0x103 0x23 0x104 0x23 0x105>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core"; - rockchip,normal-rates = <0x11b3dc40 0x00 0x11b3dc40>; - resets = <0x23 0x133 0x23 0x134 0x23 0x135>; - reset-names = "video_a\0video_h\0video_core"; - assigned-clocks = <0x23 0x103 0x23 0x105>; - assigned-clock-rates = <0x11b3dc40 0x11b3dc40>; - iommus = <0x80>; - node-name = "rkvenc"; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x03>; - rockchip,resetgroup-node = <0x03>; - power-domains = <0x25 0x0e>; - operating-points-v2 = <0x81>; - status = "okay"; - venc-supply = <0x75>; - }; - - rkvenc-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x07>; - nvmem-cell-names = "pvtm"; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x16378 0x01 0x16379 0x186a0 0x02>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x81>; - - opp-297000000 { - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - opp-microvolt-L2 = <0xd59f8>; - }; - - opp-400000000 { - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xe7ef0>; - opp-microvolt-L0 = <0xe7ef0>; - opp-microvolt-L1 = <0xe1d48>; - opp-microvolt-L2 = <0xdbba0>; - }; - }; - - iommu@fdf40f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdf40f00 0x00 0x40 0x00 0xfdf40f40 0x00 0x40>; - interrupts = <0x00 0x8d 0x04 0x00 0x8e 0x04>; - interrupt-names = "rkvenc_mmu0\0rkvenc_mmu1"; - clocks = <0x23 0x103 0x23 0x104>; - clock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - #iommu-cells = <0x00>; - power-domains = <0x25 0x0e>; - status = "okay"; - phandle = <0x80>; - }; - - rkvdec@fdf80200 { - compatible = "rockchip,rkv-decoder-rk3568\0rockchip,rkv-decoder-v2"; - reg = <0x00 0xfdf80200 0x00 0x400 0x00 0xfdf80100 0x00 0x100>; - reg-names = "regs\0link"; - interrupts = <0x00 0x5b 0x04>; - interrupt-names = "irq_dec"; - clocks = <0x23 0x108 0x23 0x109 0x23 0x10a 0x23 0x10b 0x23 0x10c>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_cabac\0clk_core\0clk_hevc_cabac"; - rockchip,normal-rates = <0x11b3dc40 0x00 0x11b3dc40 0x11b3dc40 0x23c34600>; - rockchip,advanced-rates = <0x179a7b00 0x00 0x179a7b00 0x179a7b00 0x23c34600>; - rockchip,default-max-load = <0x1fe000>; - resets = <0x23 0x142 0x23 0x143 0x23 0x144 0x23 0x145 0x23 0x146>; - assigned-clocks = <0x23 0x108 0x23 0x10a 0x23 0x10b 0x23 0x10c>; - assigned-clock-rates = <0x11b3dc40 0x11b3dc40 0x11b3dc40 0x11b3dc40>; - reset-names = "video_a\0video_h\0video_cabac\0video_core\0video_hevc_cabac"; - power-domains = <0x25 0x0d>; - operating-points-v2 = <0x82>; - vdec-supply = <0x75>; - iommus = <0x83>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x04>; - rockchip,resetgroup-node = <0x04>; - rockchip,sram = <0x84>; - rockchip,rcb-iova = <0x10000000 0x10000>; - rockchip,rcb-min-width = <0x200>; - rockchip,task-capacity = <0x10>; - status = "okay"; - }; - - rkvdec-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x85 0x07>; - nvmem-cell-names = "leakage\0pvtm"; - rockchip,leakage-voltage-sel = <0x01 0x50 0x00 0x51 0xfe 0x01>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x186a0 0x01>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x82>; - - opp-297000000 { - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - }; - - opp-400000000 { - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xdbba0>; - }; - }; - - iommu@fdf80800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdf80800 0x00 0x40 0x00 0xfdf80840 0x00 0x40>; - interrupts = <0x00 0x5c 0x04>; - interrupt-names = "rkvdec_mmu"; - clocks = <0x23 0x108 0x23 0x109>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x0d>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x83>; - }; - - mipi-csi2-hw@fdfb0000 { - compatible = "rockchip,rk3568-mipi-csi2-hw"; - reg = <0x00 0xfdfb0000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x08 0x04 0x00 0x09 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x23 0xd5>; - clock-names = "pclk_csi2host"; - resets = <0x23 0xff>; - reset-names = "srst_csihost_p"; - status = "okay"; - phandle = <0x1e>; - }; - - rkcif@fdfe0000 { - compatible = "rockchip,rk3568-cif"; - reg = <0x00 0xfdfe0000 0x00 0x8000>; - reg-names = "cif_regs"; - interrupts = <0x00 0x92 0x04>; - interrupt-names = "cif-intr"; - clocks = <0x23 0xce 0x23 0xcf 0x23 0xd0 0x23 0xd1>; - clock-names = "aclk_cif\0hclk_cif\0dclk_cif\0iclk_cif_g"; - resets = <0x23 0xf7 0x23 0xf8 0x23 0xf9 0x23 0xfb 0x23 0xfa>; - reset-names = "rst_cif_a\0rst_cif_h\0rst_cif_d\0rst_cif_p\0rst_cif_i"; - assigned-clocks = <0x23 0xd0>; - assigned-clock-rates = <0x11e1a300>; - power-domains = <0x25 0x08>; - rockchip,grf = <0x3b>; - iommus = <0x86>; - status = "disabled"; - phandle = <0x87>; - }; - - iommu@fdfe0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdfe0800 0x00 0x100>; - interrupts = <0x00 0x92 0x04>; - interrupt-names = "cif_mmu"; - clocks = <0x23 0xce 0x23 0xcf>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x08>; - rockchip,disable-mmu-reset; - #iommu-cells = <0x00>; - status = "disabled"; - phandle = <0x86>; - }; - - rkcif_dvp { - compatible = "rockchip,rkcif-dvp"; - rockchip,hw = <0x87>; - status = "disabled"; - phandle = <0x88>; - }; - - rkcif_dvp_sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x88>; - status = "disabled"; - }; - - rkcif_mipi_lvds { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x87>; - status = "disabled"; - phandle = <0x89>; - }; - - rkcif_mipi_lvds_sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x89>; - status = "disabled"; - }; - - rkisp@fdff0000 { - compatible = "rockchip,rk3568-rkisp"; - reg = <0x00 0xfdff0000 0x00 0x10000>; - interrupts = <0x00 0x39 0x04 0x00 0x3a 0x04 0x00 0x3c 0x04>; - interrupt-names = "mipi_irq\0mi_irq\0isp_irq"; - clocks = <0x23 0xd2 0x23 0xd3 0x23 0xd4>; - clock-names = "aclk_isp\0hclk_isp\0clk_isp"; - resets = <0x23 0xfd 0x23 0xfc>; - reset-names = "isp\0isp-h"; - rockchip,grf = <0x3b>; - power-domains = <0x25 0x08>; - iommus = <0x8a>; - rockchip,iq-feature = <0x1bfb 0xfffe67ff>; - status = "okay"; - phandle = <0x8b>; - }; - - iommu@fdff1a00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdff1a00 0x00 0x100>; - interrupts = <0x00 0x3b 0x04>; - interrupt-names = "isp_mmu"; - clocks = <0x23 0xd2 0x23 0xd3>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x08>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "okay"; - phandle = <0x8a>; - }; - - rkisp-vir0 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x8b>; - status = "okay"; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x8c>; - phandle = <0x134>; - }; - }; - }; - - rkisp-vir1 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x8b>; - status = "disabled"; - }; - - uio@fe010000 { - compatible = "rockchip,uio-gmac"; - reg = <0x00 0xfe010000 0x00 0x10000>; - rockchip,ethernet = <0x8d>; - status = "disabled"; - }; - - ethernet@fe010000 { - local-mac-address = [5e 4f fd 70 05 c6]; - compatible = "rockchip,rk3568-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe010000 0x00 0x10000>; - interrupts = <0x00 0x20 0x04 0x00 0x1d 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0x3b>; - clocks = <0x23 0x186 0x23 0x189 0x23 0x189 0x23 0xc7 0x23 0xc3 0x23 0xc4 0x23 0x189 0x23 0xc8 0x23 0xac 0x23 0xab>; - clock-names = "stmmaceth\0mac_clk_rx\0mac_clk_tx\0clk_mac_refout\0aclk_mac\0pclk_mac\0clk_mac_speed\0ptp_ref\0pclk_xpcs\0clk_xpcs_eee"; - resets = <0x23 0xec>; - reset-names = "stmmaceth"; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0x8e>; - snps,mtl-rx-config = <0x8f>; - snps,mtl-tx-config = <0x90>; - status = "okay"; - phy-mode = "rgmii"; - clock_in_out = "input"; - snps,reset-gpio = <0x91 0x19 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - assigned-clocks = <0x23 0x189 0x23 0x186>; - assigned-clock-parents = <0x23 0x187 0x92>; - pinctrl-names = "default"; - pinctrl-0 = <0x93 0x94 0x95 0x96 0x97 0x98>; - tx_delay = <0x3e>; - rx_delay = <0x32>; - phy-handle = <0x99>; - phandle = <0x8d>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x00>; - led_status_value = <0x6940>; - phandle = <0x99>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0x8e>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x01>; - phandle = <0x8f>; - - queue0 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x01>; - phandle = <0x90>; - - queue0 { - }; - }; - }; - - vop@fe040000 { - compatible = "rockchip,rk3568-vop"; - reg = <0x00 0xfe040000 0x00 0x3000 0x00 0xfe044000 0x00 0x1000>; - reg-names = "regs\0gamma_lut"; - rockchip,grf = <0x3b>; - interrupts = <0x00 0x94 0x04>; - clocks = <0x23 0xdd 0x23 0xde 0x23 0xdf 0x23 0xe0 0x23 0xe1>; - clock-names = "aclk_vop\0hclk_vop\0dclk_vp0\0dclk_vp1\0dclk_vp2"; - iommus = <0x9a>; - power-domains = <0x25 0x09>; - status = "okay"; - assigned-clocks = <0x23 0xdf 0x23 0xe0>; - assigned-clock-parents = <0x3a 0x02 0x23 0x05>; - disable-win-move; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - phandle = <0x15>; - - port@0 { - rockchip,primary-plane = <0x04>; - rockchip,plane-mask = <0x15>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x9b>; - phandle = <0x17>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x9c>; - phandle = <0x18>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x9d>; - phandle = <0x19>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0x9e>; - phandle = <0x1a>; - }; - }; - - port@1 { - rockchip,primary-plane = <0x05>; - rockchip,plane-mask = <0x22>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x01>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x9f>; - phandle = <0xa8>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa0>; - phandle = <0xa9>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xa1>; - phandle = <0xaf>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0xa2>; - phandle = <0xad>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0xa3>; - phandle = <0x1b>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0xa4>; - phandle = <0x37>; - }; - }; - - port@2 { - rockchip,primary-plane = <0x03>; - rockchip,plane-mask = <0x08>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x02>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xa5>; - phandle = <0x35>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa6>; - phandle = <0x1c>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xa7>; - phandle = <0x38>; - }; - }; - }; - }; - - iommu@fe043e00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfe043e00 0x00 0x100 0x00 0xfe043f00 0x00 0x100>; - interrupts = <0x00 0x94 0x04>; - interrupt-names = "vop_mmu"; - clocks = <0x23 0xdd 0x23 0xde>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - rockchip,disable-device-link-resume; - status = "okay"; - phandle = <0x9a>; - }; - - dsi@fe060000 { - compatible = "rockchip,rk3568-mipi-dsi"; - reg = <0x00 0xfe060000 0x00 0x10000>; - interrupts = <0x00 0x44 0x04>; - clocks = <0x23 0xe8 0x23 0xda>; - clock-names = "pclk\0hclk"; - resets = <0x23 0x110>; - reset-names = "apb"; - phys = <0x34>; - phy-names = "dphy"; - power-domains = <0x25 0x09>; - rockchip,grf = <0x3b>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x17>; - status = "disabled"; - phandle = <0x9b>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa8>; - status = "disabled"; - phandle = <0x9f>; - }; - }; - }; - }; - - dsi@fe070000 { - compatible = "rockchip,rk3568-mipi-dsi"; - reg = <0x00 0xfe070000 0x00 0x10000>; - interrupts = <0x00 0x45 0x04>; - clocks = <0x23 0xe9 0x23 0xda>; - clock-names = "pclk\0hclk"; - resets = <0x23 0x111>; - reset-names = "apb"; - phys = <0x36>; - phy-names = "dphy"; - power-domains = <0x25 0x09>; - rockchip,grf = <0x3b>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x18>; - status = "disabled"; - phandle = <0x9c>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa9>; - status = "disabled"; - phandle = <0xa0>; - }; - }; - }; - }; - - hdmi@fe0a0000 { - compatible = "rockchip,rk3568-dw-hdmi"; - reg = <0x00 0xfe0a0000 0x00 0x20000>; - interrupts = <0x00 0x2d 0x04>; - clocks = <0x23 0xe6 0x23 0xe7 0x23 0x193 0x3a 0x02 0x23 0xde>; - clock-names = "iahb\0isfr\0cec\0ref\0hclk"; - power-domains = <0x25 0x09>; - reg-io-width = <0x04>; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xaa 0xab 0xac>; - status = "okay"; - phandle = <0x147>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x1a>; - status = "okay"; - phandle = <0x9e>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xad>; - status = "disabled"; - phandle = <0xa2>; - }; - }; - }; - }; - - edp@fe0c0000 { - compatible = "rockchip,rk3568-edp"; - reg = <0x00 0xfe0c0000 0x00 0x10000>; - interrupts = <0x00 0x12 0x04>; - clocks = <0x3a 0x29 0x23 0xea 0x23 0xeb 0x23 0xda>; - clock-names = "dp\0pclk\0spdif\0hclk"; - resets = <0x23 0x113 0x23 0x112>; - reset-names = "dp\0apb"; - phys = <0xae>; - phy-names = "dp"; - power-domains = <0x25 0x09>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x19>; - status = "disabled"; - phandle = <0x9d>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xaf>; - status = "disabled"; - phandle = <0xa1>; - }; - }; - }; - }; - - nocp-cpu@fe102000 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102000 0x00 0x400>; - phandle = <0xb5>; - }; - - nocp-gpu-vpu-rga-venc@fe102400 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102400 0x00 0x400>; - }; - - nocp-vdec@fe102800 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102800 0x00 0x400>; - }; - - nocp-vi-usb-peri-pipe@fe102c00 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102c00 0x00 0x400>; - }; - - nocp-vo@fe103000 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe103000 0x00 0x400>; - }; - - qos@fe128000 { - compatible = "syscon"; - reg = <0x00 0xfe128000 0x00 0x20>; - phandle = <0x55>; - }; - - qos@fe138080 { - compatible = "syscon"; - reg = <0x00 0xfe138080 0x00 0x20>; - phandle = <0x64>; - }; - - qos@fe138100 { - compatible = "syscon"; - reg = <0x00 0xfe138100 0x00 0x20>; - phandle = <0x65>; - }; - - qos@fe138180 { - compatible = "syscon"; - reg = <0x00 0xfe138180 0x00 0x20>; - phandle = <0x66>; - }; - - qos@fe148000 { - compatible = "syscon"; - reg = <0x00 0xfe148000 0x00 0x20>; - phandle = <0x56>; - }; - - qos@fe148080 { - compatible = "syscon"; - reg = <0x00 0xfe148080 0x00 0x20>; - phandle = <0x57>; - }; - - qos@fe148100 { - compatible = "syscon"; - reg = <0x00 0xfe148100 0x00 0x20>; - phandle = <0x58>; - }; - - qos@fe150000 { - compatible = "syscon"; - reg = <0x00 0xfe150000 0x00 0x20>; - phandle = <0x62>; - }; - - qos@fe158000 { - compatible = "syscon"; - reg = <0x00 0xfe158000 0x00 0x20>; - phandle = <0x5c>; - }; - - qos@fe158100 { - compatible = "syscon"; - reg = <0x00 0xfe158100 0x00 0x20>; - phandle = <0x5d>; - }; - - qos@fe158180 { - compatible = "syscon"; - reg = <0x00 0xfe158180 0x00 0x20>; - phandle = <0x5e>; - }; - - qos@fe158200 { - compatible = "syscon"; - reg = <0x00 0xfe158200 0x00 0x20>; - phandle = <0x5f>; - }; - - qos@fe158280 { - compatible = "syscon"; - reg = <0x00 0xfe158280 0x00 0x20>; - phandle = <0x60>; - }; - - qos@fe158300 { - compatible = "syscon"; - reg = <0x00 0xfe158300 0x00 0x20>; - phandle = <0x61>; - }; - - qos@fe180000 { - compatible = "syscon"; - reg = <0x00 0xfe180000 0x00 0x20>; - phandle = <0x54>; - }; - - qos@fe190000 { - compatible = "syscon"; - reg = <0x00 0xfe190000 0x00 0x20>; - phandle = <0x67>; - }; - - qos@fe190080 { - compatible = "syscon"; - reg = <0x00 0xfe190080 0x00 0x20>; - phandle = <0x68>; - }; - - qos@fe190100 { - compatible = "syscon"; - reg = <0x00 0xfe190100 0x00 0x20>; - phandle = <0x69>; - }; - - qos@fe190200 { - compatible = "syscon"; - reg = <0x00 0xfe190200 0x00 0x20>; - phandle = <0x6a>; - }; - - qos@fe190280 { - compatible = "syscon"; - reg = <0x00 0xfe190280 0x00 0x20>; - phandle = <0x6b>; - }; - - qos@fe190300 { - compatible = "syscon"; - reg = <0x00 0xfe190300 0x00 0x20>; - phandle = <0x6c>; - }; - - qos@fe190380 { - compatible = "syscon"; - reg = <0x00 0xfe190380 0x00 0x20>; - phandle = <0x6d>; - }; - - qos@fe190400 { - compatible = "syscon"; - reg = <0x00 0xfe190400 0x00 0x20>; - phandle = <0x6e>; - }; - - qos@fe198000 { - compatible = "syscon"; - reg = <0x00 0xfe198000 0x00 0x20>; - phandle = <0x63>; - }; - - qos@fe1a8000 { - compatible = "syscon"; - reg = <0x00 0xfe1a8000 0x00 0x20>; - phandle = <0x59>; - }; - - qos@fe1a8080 { - compatible = "syscon"; - reg = <0x00 0xfe1a8080 0x00 0x20>; - phandle = <0x5a>; - }; - - qos@fe1a8100 { - compatible = "syscon"; - reg = <0x00 0xfe1a8100 0x00 0x20>; - phandle = <0x5b>; - }; - - dwmmc@fe000000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe000000 0x00 0x4000>; - interrupts = <0x00 0x64 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xc1 0x23 0xc2 0x23 0x18e 0x23 0x18f>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xeb>; - reset-names = "reset"; - status = "okay"; - no-sd; - no-mmc; - bus-width = <0x04>; - disable-wp; - cap-sd-highspeed; - cap-sdio-irq; - keep-power-in-suspend; - pinctrl-names = "default"; - pinctrl-0 = <0xb0 0xb1 0xb2>; - sd-uhs-sdr104; - mmc-pwrseq = <0xb3>; - non-removable; - }; - - dfi@fe230000 { - reg = <0x00 0xfe230000 0x00 0x400>; - compatible = "rockchip,rk3568-dfi"; - rockchip,pmugrf = <0x3c>; - status = "okay"; - phandle = <0xb4>; - }; - - dmc { - compatible = "rockchip,rk3568-dmc"; - interrupts = <0x00 0x0a 0x04>; - interrupt-names = "complete"; - devfreq-events = <0xb4 0xb5>; - clocks = <0x02 0x03>; - clock-names = "dmc_clk"; - operating-points-v2 = <0xb6>; - vop-bw-dmc-freq = <0x00 0x11e 0x4f1a0 0x11f 0x1869f 0x80e80>; - vop-frame-bw-dmc-freq = <0x00 0x26c 0x4f1a0 0x26d 0x1869f 0xbe6e0>; - cpu-bw-dmc-freq = <0x00 0x15e 0x4f1a0 0x15f 0x190 0x80e80 0x191 0x1869f 0xbe6e0>; - upthreshold = <0x28>; - downdifferential = <0x14>; - system-status-level = <0x01 0x04 0x08 0x08 0x02 0x01 0x10 0x04 0x10000 0x04 0x1000 0x08 0x4000 0x08 0x2000 0x08 0xc00 0x08>; - auto-min-freq = <0x4f1a0>; - auto-freq-en = <0x01>; - #cooling-cells = <0x02>; - status = "okay"; - center-supply = <0x75>; - phandle = <0x16>; - }; - - dmc-fsp { - compatible = "rockchip,rk3568-dmc-fsp"; - debug_print_level = <0x00>; - ddr3_params = <0xb7>; - ddr4_params = <0xb8>; - lpddr3_params = <0xb9>; - lpddr4_params = <0xba>; - lpddr4x_params = <0xbb>; - status = "okay"; - }; - - dmc-opp-table { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x85 0x07 0x08 0xbc 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x618 0x124f8>; - rockchip,leakage-voltage-sel = <0x01 0x50 0x00 0x51 0xfe 0x01>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x186a0 0x01>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0xb6>; - - opp-1560000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x5cfbb600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L1 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-j-m-1560000000 { - opp-supported-hw = <0x06 0xffff>; - opp-hz = <0x00 0x5cfbb600>; - opp-microvolt = <0xd59f8 0xd59f8 0xf4240>; - }; - }; - - pcie@fe260000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x00 0x0f>; - clocks = <0x23 0x81 0x23 0x82 0x23 0x83 0x23 0x84 0x23 0x85>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0x4b 0x04 0x00 0x4a 0x04 0x00 0x49 0x04 0x00 0x48 0x04 0x00 0x47 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xbd 0x00 0x00 0x00 0x00 0x02 0xbd 0x01 0x00 0x00 0x00 0x03 0xbd 0x02 0x00 0x00 0x00 0x04 0xbd 0x03>; - linux,pci-domain = <0x00>; - num-ib-windows = <0x06>; - num-viewport = <0x08>; - num-ob-windows = <0x02>; - max-link-speed = <0x02>; - msi-map = <0x00 0xbe 0x00 0x1000>; - num-lanes = <0x01>; - phys = <0x27 0x02>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf4000000 0x00 0xf4000000 0x00 0x100000 0x81000000 0x00 0xf4100000 0x00 0xf4100000 0x00 0x100000 0x82000000 0x00 0xf4200000 0x00 0xf4200000 0x00 0x1e00000 0xc3000000 0x03 0x00 0x03 0x00 0x00 0x40000000>; - reg = <0x03 0xc0000000 0x00 0x400000 0x00 0xfe260000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xa1>; - reset-names = "pipe"; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x48 0x01>; - phandle = <0xbd>; - }; - }; - - pcie@fe270000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x10 0x1f>; - clocks = <0x23 0x88 0x23 0x89 0x23 0x8a 0x23 0x8b 0x23 0x8c>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0xa0 0x04 0x00 0x9f 0x04 0x00 0x9e 0x04 0x00 0x9d 0x04 0x00 0x9c 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xbf 0x00 0x00 0x00 0x00 0x02 0xbf 0x01 0x00 0x00 0x00 0x03 0xbf 0x02 0x00 0x00 0x00 0x04 0xbf 0x03>; - linux,pci-domain = <0x01>; - num-ib-windows = <0x06>; - num-ob-windows = <0x02>; - num-viewport = <0x08>; - max-link-speed = <0x03>; - msi-map = <0x1000 0xbe 0x1000 0x1000>; - num-lanes = <0x01>; - phys = <0xc0>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf2000000 0x00 0xf2000000 0x00 0x100000 0x81000000 0x00 0xf2100000 0x00 0xf2100000 0x00 0x100000 0x82000000 0x00 0xf2200000 0x00 0xf2200000 0x00 0x1e00000 0xc3000000 0x03 0x40000000 0x03 0x40000000 0x00 0x40000000>; - reg = <0x03 0xc0400000 0x00 0x400000 0x00 0xfe270000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xb1>; - reset-names = "pipe"; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9d 0x01>; - phandle = <0xbf>; - }; - }; - - pcie@fe280000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x20 0x2f>; - clocks = <0x23 0x8f 0x23 0x90 0x23 0x91 0x23 0x92 0x23 0x93>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0xa5 0x04 0x00 0xa4 0x04 0x00 0xa3 0x04 0x00 0xa2 0x04 0x00 0xa1 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xc1 0x00 0x00 0x00 0x00 0x02 0xc1 0x01 0x00 0x00 0x00 0x03 0xc1 0x02 0x00 0x00 0x00 0x04 0xc1 0x03>; - linux,pci-domain = <0x02>; - num-ib-windows = <0x06>; - num-viewport = <0x08>; - num-ob-windows = <0x02>; - max-link-speed = <0x03>; - msi-map = <0x2000 0xbe 0x2000 0x1000>; - num-lanes = <0x02>; - phys = <0xc0>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf0000000 0x00 0xf0000000 0x00 0x100000 0x81000000 0x00 0xf0100000 0x00 0xf0100000 0x00 0x100000 0x82000000 0x00 0xf0200000 0x00 0xf0200000 0x00 0x1e00000 0xc3000000 0x03 0x80000000 0x03 0x80000000 0x00 0x40000000>; - reg = <0x03 0xc0800000 0x00 0x400000 0x00 0xfe280000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xc1>; - reset-names = "pipe"; - status = "okay"; - reset-gpios = <0x91 0x1e 0x00>; - vpcie3v3-supply = <0xc2>; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xa2 0x01>; - phandle = <0xc1>; - }; - }; - - uio@fe2a0000 { - compatible = "rockchip,uio-gmac"; - reg = <0x00 0xfe2a0000 0x00 0x10000>; - rockchip,ethernet = <0xc3>; - status = "disabled"; - }; - - ethernet@fe2a0000 { - local-mac-address = [5a 4f fd 70 05 c6]; - compatible = "rockchip,rk3568-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe2a0000 0x00 0x10000>; - interrupts = <0x00 0x1b 0x04 0x00 0x18 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0x3b>; - clocks = <0x23 0x182 0x23 0x185 0x23 0x185 0x23 0xb8 0x23 0xb4 0x23 0xb5 0x23 0x185 0x23 0xb9 0x23 0xac 0x23 0xab>; - clock-names = "stmmaceth\0mac_clk_rx\0mac_clk_tx\0clk_mac_refout\0aclk_mac\0pclk_mac\0clk_mac_speed\0ptp_ref\0pclk_xpcs\0clk_xpcs_eee"; - resets = <0x23 0xd7>; - reset-names = "stmmaceth"; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0xc4>; - snps,mtl-rx-config = <0xc5>; - snps,mtl-tx-config = <0xc6>; - status = "okay"; - phy-mode = "rgmii"; - clock_in_out = "input"; - snps,reset-gpio = <0x91 0x1b 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - assigned-clocks = <0x23 0x185 0x23 0x182>; - assigned-clock-parents = <0x23 0x183 0xc7>; - pinctrl-names = "default"; - pinctrl-0 = <0xc8 0xc9 0xca 0xcb 0xcc 0xcd>; - tx_delay = <0x4a>; - rx_delay = <0x2e>; - phy-handle = <0xce>; - phandle = <0xc3>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x00>; - led_status_value = <0x6940>; - phandle = <0xce>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0xc4>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x01>; - phandle = <0xc5>; - - queue0 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x01>; - phandle = <0xc6>; - - queue0 { - }; - }; - }; - - dwmmc@fe2b0000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2b0000 0x00 0x4000>; - interrupts = <0x00 0x62 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xb0 0x23 0xb1 0x23 0x18a 0x23 0x18b>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xd4>; - reset-names = "reset"; - status = "okay"; - no-sdio; - no-mmc; - bus-width = <0x04>; - cap-mmc-highspeed; - cap-sd-highspeed; - disable-wp; - sd-uhs-sdr104; - vmmc-supply = <0xcf>; - vqmmc-supply = <0x31>; - pinctrl-names = "default"; - pinctrl-0 = <0xd0 0xd1 0xd2 0xd3>; - }; - - dwmmc@fe2c0000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2c0000 0x00 0x4000>; - interrupts = <0x00 0x63 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xb2 0x23 0xb3 0x23 0x18c 0x23 0x18d>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xd6>; - reset-names = "reset"; - status = "disabled"; - }; - - spi@fe300000 { - compatible = "rockchip,sfc"; - reg = <0x00 0xfe300000 0x00 0x4000>; - interrupts = <0x00 0x65 0x04>; - clocks = <0x23 0x78 0x23 0x76>; - clock-names = "clk_sfc\0hclk_sfc"; - assigned-clocks = <0x23 0x78>; - assigned-clock-rates = <0x2faf080>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <0xd4>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x00>; - spi-max-frequency = <0x2faf080>; - spi-rx-bus-width = <0x01>; - spi-tx-bus-width = <0x01>; - }; - }; - - sdhci@fe310000 { - compatible = "rockchip,rk3568-dwcmshc\0rockchip,dwcmshc-sdhci"; - reg = <0x00 0xfe310000 0x00 0x10000>; - interrupts = <0x00 0x13 0x04>; - assigned-clocks = <0x23 0x7b 0x23 0x7d 0x23 0x7c>; - assigned-clock-rates = <0xbebc200 0x16e3600 0xbebc200>; - clocks = <0x23 0x7c 0x23 0x7a 0x23 0x79 0x23 0x7b 0x23 0x7d>; - clock-names = "core\0bus\0axi\0block\0timer"; - resets = <0x23 0x78 0x23 0x76 0x23 0x75 0x23 0x77 0x23 0x79>; - reset-names = "core\0bus\0axi\0block\0timer"; - status = "okay"; - bus-width = <0x08>; - no-sdio; - no-sd; - non-removable; - max-frequency = <0xbebc200>; - full-pwr-cycle-in-suspend; - }; - - nandc@fe330000 { - compatible = "rockchip,rk-nandc-v9"; - reg = <0x00 0xfe330000 0x00 0x4000>; - interrupts = <0x00 0x46 0x04>; - nandc_id = <0x00>; - clocks = <0x23 0x75 0x23 0x74>; - clock-names = "clk_nandc\0hclk_nandc"; - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - nand@0 { - reg = <0x00>; - nand-bus-width = <0x08>; - nand-ecc-mode = "hw"; - nand-ecc-strength = <0x10>; - nand-ecc-step-size = <0x400>; - }; - }; - - crypto@fe380000 { - compatible = "rockchip,rk3568-crypto"; - reg = <0x00 0xfe380000 0x00 0x4000>; - interrupts = <0x00 0x04 0x04>; - clocks = <0x23 0x6a 0x23 0x6b 0x23 0x6c 0x23 0x6d>; - clock-names = "aclk\0hclk\0sclk\0apb_pclk"; - assigned-clocks = <0x23 0x6c>; - assigned-clock-rates = <0xbebc200>; - resets = <0x23 0x69>; - reset-names = "crypto-rst"; - status = "disabled"; - }; - - rng@fe388000 { - compatible = "rockchip,cryptov2-rng"; - reg = <0x00 0xfe388000 0x00 0x2000>; - clocks = <0x23 0x70 0x23 0x6f>; - clock-names = "clk_trng\0hclk_trng"; - resets = <0x23 0x6d>; - reset-names = "reset"; - status = "okay"; - }; - - otp@fe38c000 { - compatible = "rockchip,rk3568-otp"; - reg = <0x00 0xfe38c000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - clocks = <0x23 0x73 0x23 0x72 0x23 0x71 0x23 0x181>; - clock-names = "usr\0sbpi\0apb\0phy"; - resets = <0x23 0x1cf>; - reset-names = "otp_phy"; - - cpu-code@2 { - reg = <0x02 0x02>; - phandle = <0x12>; - }; - - specification-serial-number@7 { - reg = <0x07 0x01>; - bits = <0x00 0x05>; - phandle = <0x0a>; - }; - - cpu-version@8 { - reg = <0x08 0x01>; - bits = <0x03 0x03>; - phandle = <0x11>; - }; - - mbist-vmin@9 { - reg = <0x09 0x01>; - bits = <0x00 0x04>; - phandle = <0x08>; - }; - - id@a { - reg = <0x0a 0x10>; - phandle = <0x10>; - }; - - cpu-leakage@1a { - reg = <0x1a 0x01>; - phandle = <0x06>; - }; - - log-leakage@1b { - reg = <0x1b 0x01>; - phandle = <0x85>; - }; - - npu-leakage@1c { - reg = <0x1c 0x01>; - phandle = <0x72>; - }; - - gpu-leakage@1d { - reg = <0x1d 0x01>; - phandle = <0x78>; - }; - - core-pvtm@2a { - reg = <0x2a 0x02>; - phandle = <0x07>; - }; - - cpu-tsadc-trim-l@2e { - reg = <0x2e 0x01>; - phandle = <0x125>; - }; - - cpu-tsadc-trim-h@2f { - reg = <0x2f 0x01>; - bits = <0x00 0x04>; - phandle = <0x126>; - }; - - npu-tsadc-trim-l@30 { - reg = <0x30 0x01>; - phandle = <0x127>; - }; - - npu-tsadc-trim-h@31 { - reg = <0x31 0x01>; - bits = <0x00 0x04>; - phandle = <0x128>; - }; - - tsadc-trim-base-frac@31 { - reg = <0x31 0x01>; - bits = <0x04 0x04>; - phandle = <0x122>; - }; - - tsadc-trim-base@32 { - reg = <0x32 0x01>; - phandle = <0x121>; - }; - - cpu-opp-info@36 { - reg = <0x36 0x06>; - phandle = <0x09>; - }; - - gpu-opp-info@3c { - reg = <0x3c 0x06>; - phandle = <0x79>; - }; - - npu-opp-info@42 { - reg = <0x42 0x06>; - phandle = <0x73>; - }; - - dmc-opp-info@48 { - reg = <0x48 0x06>; - phandle = <0xbc>; - }; - - remark-spec-serial-number@56 { - reg = <0x56 0x01>; - bits = <0x00 0x05>; - phandle = <0x0b>; - }; - }; - - i2s@fe400000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe400000 0x00 0x1000>; - interrupts = <0x00 0x34 0x04>; - clocks = <0x23 0x3f 0x23 0x43 0x23 0x39>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x00>; - dma-names = "tx"; - resets = <0x23 0x50 0x23 0x51>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x146>; - }; - - i2s@fe410000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe410000 0x00 0x1000>; - interrupts = <0x00 0x35 0x04>; - clocks = <0x23 0x47 0x23 0x4b 0x23 0x3a>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x02 0xd5 0x03>; - dma-names = "tx\0rx"; - resets = <0x23 0x52 0x23 0x53>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xd6 0xd7 0xd8 0xd9>; - status = "okay"; - rockchip,clk-trcm = <0x01>; - phandle = <0xe8>; - }; - - i2s@fe420000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe420000 0x00 0x1000>; - interrupts = <0x00 0x36 0x04>; - clocks = <0x23 0x4f 0x23 0x4f 0x23 0x3b>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x04 0xd5 0x05>; - dma-names = "tx\0rx"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,clk-trcm = <0x01>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xda 0xdb 0xdc 0xdd>; - status = "disabled"; - }; - - i2s@fe430000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe430000 0x00 0x1000>; - interrupts = <0x00 0x37 0x04>; - clocks = <0x23 0x53 0x23 0x57 0x23 0x3c>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x06 0xd5 0x07>; - dma-names = "tx\0rx"; - resets = <0x23 0x55 0x23 0x56>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,clk-trcm = <0x01>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xde 0xdf 0xe0 0xe1>; - status = "disabled"; - phandle = <0x144>; - }; - - pdm@fe440000 { - compatible = "rockchip,rk3568-pdm\0rockchip,pdm"; - reg = <0x00 0xfe440000 0x00 0x1000>; - clocks = <0x23 0x5a 0x23 0x59>; - clock-names = "pdm_clk\0pdm_hclk"; - dmas = <0xd5 0x09>; - dma-names = "rx"; - pinctrl-names = "default"; - pinctrl-0 = <0xe2 0xe3 0xe4 0xe5 0xe6 0xe7>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x149>; - }; - - vad@fe450000 { - compatible = "rockchip,rk3568-vad"; - reg = <0x00 0xfe450000 0x00 0x10000>; - reg-names = "vad"; - clocks = <0x23 0x5b>; - clock-names = "hclk"; - interrupts = <0x00 0x89 0x04>; - rockchip,audio-src = <0xe8>; - rockchip,det-channel = <0x00>; - rockchip,mode = <0x00>; - #sound-dai-cells = <0x00>; - status = "disabled"; - rockchip,buffer-time-ms = <0x80>; - phandle = <0x14e>; - }; - - spdif@fe460000 { - compatible = "rockchip,rk3568-spdif"; - reg = <0x00 0xfe460000 0x00 0x1000>; - interrupts = <0x00 0x66 0x04>; - dmas = <0xd5 0x01>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x23 0x5f 0x23 0x5c>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xe9>; - status = "disabled"; - phandle = <0x14c>; - }; - - audpwm@fe470000 { - compatible = "rockchip,rk3568-audio-pwm\0rockchip,audio-pwm-v1"; - reg = <0x00 0xfe470000 0x00 0x1000>; - clocks = <0x23 0x63 0x23 0x60>; - clock-names = "clk\0hclk"; - dmas = <0xd5 0x08>; - dma-names = "tx"; - #sound-dai-cells = <0x00>; - rockchip,sample-width-bits = <0x0b>; - rockchip,interpolat-points = <0x01>; - status = "disabled"; - }; - - codec-digital@fe478000 { - compatible = "rockchip,rk3568-codec-digital\0rockchip,codec-digital-v1"; - reg = <0x00 0xfe478000 0x00 0x1000>; - clocks = <0x23 0x67 0x23 0x66 0x23 0x65 0x23 0x64>; - clock-names = "adc\0dac\0i2c\0pclk"; - pinctrl-names = "default"; - pinctrl-0 = <0xea>; - resets = <0x23 0x5f>; - reset-names = "reset"; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x145>; - }; - - dmac@fe530000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfe530000 0x00 0x4000>; - interrupts = <0x00 0x0e 0x04 0x00 0x0d 0x04>; - clocks = <0x23 0x10d>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0x4e>; - }; - - dmac@fe550000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfe550000 0x00 0x4000>; - interrupts = <0x00 0x10 0x04 0x00 0x0f 0x04>; - clocks = <0x23 0x10d>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0xd5>; - }; - - rkscr@fe560000 { - compatible = "rockchip-scr"; - reg = <0x00 0xfe560000 0x00 0x10000>; - interrupts = <0x00 0x61 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xeb>; - clocks = <0x23 0x114>; - clock-names = "g_pclk_sim_card"; - status = "disabled"; - }; - - can@fe570000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe570000 0x00 0x1000>; - interrupts = <0x00 0x01 0x04>; - clocks = <0x23 0x141 0x23 0x140>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x155 0x23 0x154>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - }; - - can@fe580000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe580000 0x00 0x1000>; - interrupts = <0x00 0x02 0x04>; - clocks = <0x23 0x143 0x23 0x142>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x157 0x23 0x156>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "okay"; - assigned-clocks = <0x23 0x143>; - assigned-clock-rates = <0xbebc200>; - pinctrl-names = "default"; - pinctrl-0 = <0xec>; - }; - - can@fe590000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe590000 0x00 0x1000>; - interrupts = <0x00 0x03 0x04>; - clocks = <0x23 0x145 0x23 0x144>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x159 0x23 0x158>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - assigned-clocks = <0x23 0x145>; - assigned-clock-rates = <0xbebc200>; - pinctrl-names = "default"; - pinctrl-0 = <0xed>; - }; - - i2c@fe5a0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5a0000 0x00 0x1000>; - clocks = <0x23 0x148 0x23 0x147>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x2f 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xee>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - clock-frequency = <0x186a0>; - - gpio@21 { - status = "disabled"; - compatible = "nxp,pca9555"; - reg = <0x21>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-group-num = <0xc8>; - phandle = <0x102>; - }; - - gt1x@14 { - status = "disabled"; - compatible = "goodix,gt1x"; - reg = <0x14>; - pinctrl-names = "default"; - pinctrl-0 = <0xef>; - goodix,rst-gpio = <0x3f 0x0e 0x00>; - goodix,irq-gpio = <0x3f 0x0d 0x08>; - }; - }; - - i2c@fe5b0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5b0000 0x00 0x1000>; - clocks = <0x23 0x14a 0x23 0x149>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x30 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf0>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@fe5c0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5c0000 0x00 0x1000>; - clocks = <0x23 0x14c 0x23 0x14b>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x31 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf1>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@fe5d0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5d0000 0x00 0x1000>; - clocks = <0x23 0x14e 0x23 0x14d>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x32 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf2>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - clock-frequency = <0x61a80>; - - gc8034@37 { - compatible = "galaxycore,gc8034"; - status = "disabled"; - reg = <0x37>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x01>; - pwdn-gpios = <0xf4 0x0c 0x01>; - rockchip,grf = <0x3b>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "RK-CMK-8M-2-v1"; - rockchip,camera-module-lens-name = "CK8401"; - - port { - - endpoint { - remote-endpoint = <0xf5>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x130>; - }; - }; - }; - - os04a10@36 { - status = "disabled"; - compatible = "ovti,os04a10"; - reg = <0x36>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x01>; - pwdn-gpios = <0xf4 0x0c 0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT1607-FV1"; - rockchip,camera-module-lens-name = "M12-40IRC-4MP-F16"; - - port { - - endpoint { - remote-endpoint = <0xf6>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x12f>; - }; - }; - }; - - ov5695@36 { - status = "disabled"; - compatible = "ovti,ov5695"; - reg = <0x36>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x00>; - pwdn-gpios = <0xf4 0x0c 0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "TongJu"; - rockchip,camera-module-lens-name = "CHT842-MD"; - - port { - - endpoint { - remote-endpoint = <0xf7>; - data-lanes = <0x01 0x02>; - phandle = <0x131>; - }; - }; - }; - - XC7160b@1b { - status = "okay"; - compatible = "firefly,xc7160"; - reg = <0x1b>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x3f 0x1d 0x00>; - pwdn-gpios = <0xf4 0x0c 0x00>; - firefly,clkout-enabled-index = <0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "NC"; - rockchip,camera-module-lens-name = "NC"; - - port { - - endpoint { - remote-endpoint = <0xf8>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x132>; - }; - }; - }; - - imx415@37 { - status = "okay"; - compatible = "sony,imx415"; - reg = <0x37>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x3f 0x1d 0x01>; - pwdn-gpios = <0xf4 0x0c 0x00>; - firefly,clkout-enabled-index = <0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT2022-PX1"; - rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; - - port { - - endpoint { - remote-endpoint = <0xf9>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x133>; - }; - }; - }; - }; - - i2c@fe5e0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5e0000 0x00 0x1000>; - clocks = <0x23 0x150 0x23 0x14f>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x33 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xfa>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - hym8563@51 { - status = "okay"; - compatible = "haoyu,hym8563"; - reg = <0x51>; - #clock-cells = <0x00>; - rtc-irq-gpio = <0x3f 0x1b 0x02>; - clock-frequency = <0x8000>; - }; - - mc3230sensor@4c { - compatible = "gs_mc3230"; - reg = <0x4c>; - type = <0x02>; - irq_enable = <0x00>; - poll_delay_ms = <0x1e>; - layout = <0x04>; - status = "okay"; - }; - - mxc6655xa@15 { - status = "disabled"; - compatible = "gs_mxc6655xa"; - pinctrl-names = "default"; - pinctrl-0 = <0xfb>; - reg = <0x15>; - irq-gpio = <0x4a 0x11 0x08>; - irq_enable = <0x00>; - poll_delay_ms = <0x1e>; - type = <0x02>; - power-off-in-suspend = <0x01>; - layout = <0x01>; - }; - }; - - timer@fe5f0000 { - compatible = "rockchip,rk3568-timer\0rockchip,rk3288-timer"; - reg = <0x00 0xfe5f0000 0x00 0x1000>; - interrupts = <0x00 0x6d 0x04>; - clocks = <0x23 0x16c 0x23 0x16d>; - clock-names = "pclk\0timer"; - }; - - watchdog@fe600000 { - compatible = "snps,dw-wdt"; - reg = <0x00 0xfe600000 0x00 0x100>; - clocks = <0x23 0x116 0x23 0x115>; - clock-names = "tclk\0pclk"; - interrupts = <0x00 0x95 0x04>; - status = "okay"; - }; - - spi@fe610000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe610000 0x00 0x1000>; - interrupts = <0x00 0x67 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x152 0x23 0x151>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x14 0x4e 0x15>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0xfc 0xfd 0xfe>; - pinctrl-1 = <0xfc 0xfd 0xff>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@fe620000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe620000 0x00 0x1000>; - interrupts = <0x00 0x68 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x154 0x23 0x153>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x16 0x4e 0x17>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x100>; - pinctrl-1 = <0x101>; - num-cs = <0x02>; - status = "disabled"; - max-freq = <0x2dc6c00>; - dev-port = <0x00>; - - spi_wk2xxx@0 { - status = "disabled"; - compatible = "firefly,spi-wk2xxx"; - reg = <0x00>; - spi-max-frequency = <0x989680>; - power-gpio = <0x102 0x0f 0x00>; - reset-gpio = <0x102 0x09 0x00>; - irq-gpio = <0x3f 0x06 0x02>; - cs-gpio = <0x4a 0x01 0x00>; - }; - }; - - spi@fe630000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe630000 0x00 0x1000>; - interrupts = <0x00 0x69 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x156 0x23 0x155>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x18 0x4e 0x19>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x103 0x104 0x105>; - pinctrl-1 = <0x103 0x104 0x106>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@fe640000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe640000 0x00 0x1000>; - interrupts = <0x00 0x6a 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x158 0x23 0x157>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x1a 0x4e 0x1b>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x107 0x108 0x109>; - pinctrl-1 = <0x107 0x108 0x10a>; - num-cs = <0x02>; - status = "disabled"; - }; - - serial@fe650000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe650000 0x00 0x100>; - interrupts = <0x00 0x75 0x04>; - clocks = <0x23 0x11f 0x23 0x11c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x02 0x4e 0x03>; - pinctrl-names = "default"; - pinctrl-0 = <0x10b>; - status = "disabled"; - }; - - serial@fe660000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe660000 0x00 0x100>; - interrupts = <0x00 0x76 0x04>; - clocks = <0x23 0x123 0x23 0x120>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x04 0x4e 0x05>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "disabled"; - }; - - serial@fe670000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe670000 0x00 0x100>; - interrupts = <0x00 0x77 0x04>; - clocks = <0x23 0x127 0x23 0x124>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x06 0x4e 0x07>; - pinctrl-names = "default"; - pinctrl-0 = <0x10d>; - status = "okay"; - }; - - serial@fe680000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe680000 0x00 0x100>; - interrupts = <0x00 0x78 0x04>; - clocks = <0x23 0x12b 0x23 0x128>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x08 0x4e 0x09>; - pinctrl-names = "default"; - pinctrl-0 = <0x10e>; - status = "okay"; - }; - - serial@fe690000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe690000 0x00 0x100>; - interrupts = <0x00 0x79 0x04>; - clocks = <0x23 0x12f 0x23 0x12c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0a 0x4e 0x0b>; - pinctrl-names = "default"; - pinctrl-0 = <0x10f>; - status = "disabled"; - }; - - serial@fe6a0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6a0000 0x00 0x100>; - interrupts = <0x00 0x7a 0x04>; - clocks = <0x23 0x133 0x23 0x130>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0c 0x4e 0x0d>; - pinctrl-names = "default"; - pinctrl-0 = <0x110>; - status = "disabled"; - }; - - serial@fe6b0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6b0000 0x00 0x100>; - interrupts = <0x00 0x7b 0x04>; - clocks = <0x23 0x137 0x23 0x134>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0e 0x4e 0x0f>; - pinctrl-names = "default"; - pinctrl-0 = <0x111>; - status = "okay"; - }; - - serial@fe6c0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6c0000 0x00 0x100>; - interrupts = <0x00 0x7c 0x04>; - clocks = <0x23 0x13b 0x23 0x138>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x10 0x4e 0x11>; - pinctrl-names = "default"; - pinctrl-0 = <0x112 0x113>; - status = "okay"; - }; - - serial@fe6d0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6d0000 0x00 0x100>; - interrupts = <0x00 0x7d 0x04>; - clocks = <0x23 0x13f 0x23 0x13c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x12 0x4e 0x13>; - pinctrl-names = "default"; - pinctrl-0 = <0x114>; - status = "okay"; - }; - - pwm@fe6e0000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0000 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x115>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - }; - - pwm@fe6e0010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0010 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x116>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - }; - - pwm@fe6e0020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0020 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x117>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6e0030 { - compatible = "rockchip,remotectl-pwm"; - reg = <0x00 0xfe6e0030 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "default"; - pinctrl-0 = <0x118>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - remote_pwm_id = <0x03>; - handle_cpu_id = <0x01>; - remote_support_psci = <0x00>; - - ir_key_firefly { - rockchip,usercode = <0xff00>; - rockchip,key_table = <0xeb 0x74 0xec 0x8b 0xfe 0x9e 0xb7 0x66 0xa3 0x96 0xf4 0x73 0xa7 0x72 0xf8 0xe8 0xfc 0x67 0xfd 0x6c 0xf1 0x69 0xe5 0x6a>; - }; - }; - - pwm@fe6f0000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0000 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x119>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0010 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11a>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0020 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11b>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0030 0x00 0x10>; - interrupts = <0x00 0x54 0x04 0x00 0x58 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11c>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - phandle = <0x157>; - }; - - pwm@fe700000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700000 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11d>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700010 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11e>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700020 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11f>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700030 0x00 0x10>; - interrupts = <0x00 0x55 0x04 0x00 0x59 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x120>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - tsadc@fe710000 { - compatible = "rockchip,rk3568-tsadc"; - reg = <0x00 0xfe710000 0x00 0x100>; - interrupts = <0x00 0x73 0x04>; - rockchip,grf = <0x3b>; - clocks = <0x23 0x111 0x23 0x10f>; - clock-names = "tsadc\0apb_pclk"; - assigned-clocks = <0x23 0x110 0x23 0x111>; - assigned-clock-rates = <0x1036640 0xaae60>; - resets = <0x23 0x182 0x23 0x181 0x23 0x1d7>; - reset-names = "tsadc\0tsadc-apb\0tsadc-phy"; - #thermal-sensor-cells = <0x01>; - nvmem-cells = <0x121 0x122>; - nvmem-cell-names = "trim_base\0trim_base_frac"; - rockchip,hw-tshut-temp = <0x1d4c0>; - rockchip,hw-tshut-mode = <0x00>; - rockchip,hw-tshut-polarity = <0x00>; - pinctrl-names = "gpio\0otpout"; - pinctrl-0 = <0x123>; - pinctrl-1 = <0x124>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x1f>; - - tsadc@0 { - reg = <0x00>; - nvmem-cells = <0x125 0x126>; - nvmem-cell-names = "trim_l\0trim_h"; - }; - - tsadc@1 { - reg = <0x01>; - nvmem-cells = <0x127 0x128>; - nvmem-cell-names = "trim_l\0trim_h"; - }; - }; - - saradc@fe720000 { - compatible = "rockchip,rk3568-saradc\0rockchip,rk3399-saradc"; - reg = <0x00 0xfe720000 0x00 0x100>; - interrupts = <0x00 0x5d 0x04>; - #io-channel-cells = <0x01>; - clocks = <0x23 0x113 0x23 0x112>; - clock-names = "saradc\0apb_pclk"; - resets = <0x23 0x180>; - reset-names = "saradc-apb"; - status = "okay"; - vref-supply = <0x129>; - phandle = <0x49>; - }; - - mailbox@fe780000 { - compatible = "rockchip,rk3568-mailbox\0rockchip,rk3368-mailbox"; - reg = <0x00 0xfe780000 0x00 0x1000>; - interrupts = <0x00 0xb7 0x04 0x00 0xb8 0x04 0x00 0xb9 0x04 0x00 0xba 0x04>; - clocks = <0x23 0x11b>; - clock-names = "pclk_mailbox"; - #mbox-cells = <0x01>; - status = "disabled"; - }; - - phy@fe820000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe820000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x1f 0x23 0x17c 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x1f>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c4 0x23 0x1c5>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12b>; - status = "okay"; - phandle = <0x24>; - }; - - phy@fe830000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe830000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x22 0x23 0x17d 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x22>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c6 0x23 0x1c7>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12c>; - status = "okay"; - phandle = <0x26>; - }; - - phy@fe840000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe840000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x25 0x23 0x17e 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x25>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c8 0x23 0x1c9>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12d>; - status = "okay"; - phandle = <0x27>; - }; - - phy@fe850000 { - compatible = "rockchip,rk3568-dsi-dphy\0rockchip,rk3568-video-phy"; - reg = <0x00 0xfe850000 0x00 0x10000 0x00 0xfe060000 0x00 0x10000>; - reg-names = "phy\0host"; - clocks = <0x3a 0x17 0x23 0x17a 0x23 0xe8>; - clock-names = "ref\0pclk\0pclk_host"; - #clock-cells = <0x00>; - resets = <0x23 0x1bb>; - reset-names = "apb"; - power-domains = <0x25 0x09>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x34>; - }; - - phy@fe860000 { - compatible = "rockchip,rk3568-dsi-dphy\0rockchip,rk3568-video-phy"; - reg = <0x00 0xfe860000 0x00 0x10000 0x00 0xfe070000 0x00 0x10000>; - reg-names = "phy\0host"; - clocks = <0x3a 0x19 0x23 0x17b 0x23 0xe9>; - clock-names = "ref\0pclk\0pclk_host"; - #clock-cells = <0x00>; - resets = <0x23 0x1bc>; - reset-names = "apb"; - power-domains = <0x25 0x09>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x36>; - }; - - csi2-dphy-hw@fe870000 { - compatible = "rockchip,rk3568-csi2-dphy-hw"; - reg = <0x00 0xfe870000 0x00 0x1000>; - clocks = <0x23 0x179>; - clock-names = "pclk"; - rockchip,grf = <0x3b>; - status = "okay"; - phandle = <0x12e>; - }; - - csi2-dphy0 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x12f>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf6>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x130>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf5>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0x131>; - data-lanes = <0x01 0x02>; - phandle = <0xf7>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0x132>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf8>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0x133>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf9>; - }; - }; - - port@1 { - reg = <0x01>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x134>; - phandle = <0x8c>; - }; - }; - }; - }; - - csi2-dphy1 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "disabled"; - }; - - csi2-dphy2 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "disabled"; - }; - - usb2-phy@fe8a0000 { - compatible = "rockchip,rk3568-usb2phy"; - reg = <0x00 0xfe8a0000 0x00 0x10000>; - interrupts = <0x00 0x87 0x04>; - clocks = <0x3a 0x13>; - clock-names = "phyclk"; - #clock-cells = <0x00>; - assigned-clocks = <0x23 0x0b>; - assigned-clock-parents = <0x29>; - clock-output-names = "usb480m_phy"; - rockchip,usbgrf = <0x135>; - status = "okay"; - phandle = <0x29>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2b>; - }; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0x28>; - }; - }; - - usb2-phy@fe8b0000 { - compatible = "rockchip,rk3568-usb2phy"; - reg = <0x00 0xfe8b0000 0x00 0x10000>; - interrupts = <0x00 0x88 0x04>; - clocks = <0x3a 0x15>; - clock-names = "phyclk"; - #clock-cells = <0x00>; - rockchip,usbgrf = <0x137>; - status = "okay"; - phandle = <0x2c>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2e>; - }; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2d>; - }; - }; - - phy@fe8c0000 { - compatible = "rockchip,rk3568-pcie3-phy"; - reg = <0x00 0xfe8c0000 0x00 0x20000>; - #phy-cells = <0x00>; - clocks = <0x3a 0x26 0x3a 0x27 0x23 0x177>; - clock-names = "refclk_m\0refclk_n\0pclk"; - resets = <0x23 0x1be>; - reset-names = "phy"; - rockchip,phy-grf = <0x138>; - status = "okay"; - phandle = <0xc0>; - }; - - pinctrl { - compatible = "rockchip,rk3568-pinctrl"; - rockchip,grf = <0x3b>; - rockchip,pmu = <0x3c>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - gpio0@fdd60000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfdd60000 0x00 0x100>; - interrupts = <0x00 0x21 0x04>; - clocks = <0x3a 0x2e 0x3a 0x0c>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x3f>; - }; - - gpio1@fe740000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe740000 0x00 0x100>; - interrupts = <0x00 0x22 0x04>; - clocks = <0x23 0x163 0x23 0x164>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x159>; - }; - - gpio2@fe750000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe750000 0x00 0x100>; - interrupts = <0x00 0x23 0x04>; - clocks = <0x23 0x165 0x23 0x166>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x91>; - }; - - gpio3@fe760000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe760000 0x00 0x100>; - interrupts = <0x00 0x24 0x04>; - clocks = <0x23 0x167 0x23 0x168>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x4a>; - }; - - gpio4@fe770000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe770000 0x00 0x100>; - interrupts = <0x00 0x25 0x04>; - clocks = <0x23 0x169 0x23 0x16a>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0xf4>; - }; - - pcfg-pull-up { - bias-pull-up; - phandle = <0x13b>; - }; - - pcfg-pull-down { - bias-pull-down; - phandle = <0x142>; - }; - - pcfg-pull-none { - bias-disable; - phandle = <0x139>; - }; - - pcfg-pull-none-drv-level-1 { - bias-disable; - drive-strength = <0x01>; - phandle = <0x13d>; - }; - - pcfg-pull-none-drv-level-2 { - bias-disable; - drive-strength = <0x02>; - phandle = <0x13c>; - }; - - pcfg-pull-none-drv-level-3 { - bias-disable; - drive-strength = <0x03>; - phandle = <0x141>; - }; - - pcfg-pull-up-drv-level-1 { - bias-pull-up; - drive-strength = <0x01>; - phandle = <0x140>; - }; - - pcfg-pull-up-drv-level-2 { - bias-pull-up; - drive-strength = <0x02>; - phandle = <0x13a>; - }; - - pcfg-pull-none-smt { - bias-disable; - input-schmitt-enable; - phandle = <0x13e>; - }; - - pcfg-output-low-pull-down { - output-low; - bias-pull-down; - phandle = <0x13f>; - }; - - acodec { - - acodec-pins { - rockchip,pins = <0x01 0x09 0x05 0x139 0x01 0x01 0x05 0x139 0x01 0x00 0x05 0x139 0x01 0x07 0x05 0x139 0x01 0x08 0x05 0x139 0x01 0x03 0x05 0x139 0x01 0x05 0x05 0x139>; - phandle = <0xea>; - }; - }; - - cam { - - vcc-cam { - rockchip,pins = <0x00 0x11 0x00 0x139>; - phandle = <0x158>; - }; - }; - - can1 { - - can1m1-pins { - rockchip,pins = <0x04 0x12 0x03 0x139 0x04 0x13 0x03 0x139>; - phandle = <0xec>; - }; - }; - - can2 { - - can2m0-pins { - rockchip,pins = <0x04 0x0c 0x03 0x139 0x04 0x0d 0x03 0x139>; - phandle = <0xed>; - }; - }; - - cif { - - cif-clk { - rockchip,pins = <0x04 0x10 0x01 0x139>; - phandle = <0xf3>; - }; - }; - - clk32k { - - clk32k-out0 { - rockchip,pins = <0x00 0x08 0x02 0x139>; - phandle = <0x22>; - }; - }; - - ebc { - - ebc-pins { - rockchip,pins = <0x04 0x10 0x02 0x139 0x04 0x0b 0x02 0x139 0x04 0x0c 0x02 0x139 0x04 0x06 0x02 0x139 0x04 0x11 0x02 0x139 0x03 0x16 0x02 0x139 0x03 0x17 0x02 0x139 0x03 0x18 0x02 0x139 0x03 0x19 0x02 0x139 0x03 0x1a 0x02 0x139 0x03 0x1b 0x02 0x139 0x03 0x1c 0x02 0x139 0x03 0x1d 0x02 0x139 0x03 0x1e 0x02 0x139 0x03 0x1f 0x02 0x139 0x04 0x00 0x02 0x139 0x04 0x01 0x02 0x139 0x04 0x02 0x02 0x139 0x04 0x03 0x02 0x139 0x04 0x04 0x02 0x139 0x04 0x05 0x02 0x139 0x04 0x0e 0x02 0x139 0x04 0x0f 0x02 0x139>; - phandle = <0x7c>; - }; - }; - - fspi { - - fspi-pins { - rockchip,pins = <0x01 0x18 0x01 0x139 0x01 0x1b 0x01 0x139 0x01 0x19 0x01 0x139 0x01 0x1a 0x01 0x139 0x01 0x17 0x02 0x139 0x01 0x1c 0x01 0x139>; - phandle = <0xd4>; - }; - }; - - gmac0 { - - gmac0-miim { - rockchip,pins = <0x02 0x13 0x02 0x139 0x02 0x14 0x02 0x139>; - phandle = <0xc8>; - }; - - gmac0-clkinout { - rockchip,pins = <0x02 0x12 0x02 0x139>; - phandle = <0xcd>; - }; - - gmac0-rx-bus2 { - rockchip,pins = <0x02 0x0e 0x01 0x139 0x02 0x0f 0x02 0x139 0x02 0x10 0x02 0x139>; - phandle = <0xca>; - }; - - gmac0-tx-bus2 { - rockchip,pins = <0x02 0x0b 0x01 0x13c 0x02 0x0c 0x01 0x13c 0x02 0x0d 0x01 0x139>; - phandle = <0xc9>; - }; - - gmac0-rgmii-clk { - rockchip,pins = <0x02 0x05 0x02 0x139 0x02 0x08 0x02 0x13d>; - phandle = <0xcb>; - }; - - gmac0-rgmii-bus { - rockchip,pins = <0x02 0x03 0x02 0x139 0x02 0x04 0x02 0x139 0x02 0x06 0x02 0x13c 0x02 0x07 0x02 0x13c>; - phandle = <0xcc>; - }; - }; - - gmac1 { - - gmac1m1-miim { - rockchip,pins = <0x04 0x0e 0x03 0x139 0x04 0x0f 0x03 0x139>; - phandle = <0x93>; - }; - - gmac1m1-clkinout { - rockchip,pins = <0x04 0x11 0x03 0x139>; - phandle = <0x98>; - }; - - gmac1m1-rx-bus2 { - rockchip,pins = <0x04 0x07 0x03 0x139 0x04 0x08 0x03 0x139 0x04 0x09 0x03 0x139>; - phandle = <0x95>; - }; - - gmac1m1-tx-bus2 { - rockchip,pins = <0x04 0x04 0x03 0x13c 0x04 0x05 0x03 0x13c 0x04 0x06 0x03 0x139>; - phandle = <0x94>; - }; - - gmac1m1-rgmii-clk { - rockchip,pins = <0x04 0x03 0x03 0x139 0x04 0x00 0x03 0x13d>; - phandle = <0x96>; - }; - - gmac1m1-rgmii-bus { - rockchip,pins = <0x04 0x01 0x03 0x139 0x04 0x02 0x03 0x139 0x03 0x1e 0x03 0x13c 0x03 0x1f 0x03 0x13c>; - phandle = <0x97>; - }; - }; - - hdmitx { - - hdmitxm0-cec { - rockchip,pins = <0x04 0x19 0x01 0x139>; - phandle = <0xac>; - }; - - hdmitx-scl { - rockchip,pins = <0x04 0x17 0x01 0x139>; - phandle = <0xaa>; - }; - - hdmitx-sda { - rockchip,pins = <0x04 0x18 0x01 0x139>; - phandle = <0xab>; - }; - }; - - i2c0 { - - i2c0-xfer { - rockchip,pins = <0x00 0x09 0x01 0x13e 0x00 0x0a 0x01 0x13e>; - phandle = <0x3d>; - }; - }; - - i2c1 { - - i2c1-xfer { - rockchip,pins = <0x00 0x0b 0x01 0x13e 0x00 0x0c 0x01 0x13e>; - phandle = <0xee>; - }; - }; - - i2c2 { - - i2c2m0-xfer { - rockchip,pins = <0x00 0x0d 0x01 0x13e 0x00 0x0e 0x01 0x13e>; - phandle = <0xf0>; - }; - }; - - i2c3 { - - i2c3m0-xfer { - rockchip,pins = <0x01 0x01 0x01 0x13e 0x01 0x00 0x01 0x13e>; - phandle = <0xf1>; - }; - }; - - i2c4 { - - i2c4m0-xfer { - rockchip,pins = <0x04 0x0b 0x01 0x13e 0x04 0x0a 0x01 0x13e>; - phandle = <0xf2>; - }; - }; - - i2c5 { - - i2c5m0-xfer { - rockchip,pins = <0x03 0x0b 0x04 0x13e 0x03 0x0c 0x04 0x13e>; - phandle = <0xfa>; - }; - }; - - i2s1 { - - i2s1m0-lrcktx { - rockchip,pins = <0x01 0x05 0x01 0x13e>; - phandle = <0xd7>; - }; - - i2s1m0-mclk { - rockchip,pins = <0x01 0x02 0x01 0x13e>; - phandle = <0x47>; - }; - - i2s1m0-sclktx { - rockchip,pins = <0x01 0x03 0x01 0x13e>; - phandle = <0xd6>; - }; - - i2s1m0-sdi0 { - rockchip,pins = <0x01 0x0b 0x01 0x139>; - phandle = <0xd8>; - }; - - i2s1m0-sdo0 { - rockchip,pins = <0x01 0x07 0x01 0x139>; - phandle = <0xd9>; - }; - }; - - i2s2 { - - i2s2m0-lrcktx { - rockchip,pins = <0x02 0x13 0x01 0x13e>; - phandle = <0xdb>; - }; - - i2s2m0-sclktx { - rockchip,pins = <0x02 0x12 0x01 0x13e>; - phandle = <0xda>; - }; - - i2s2m0-sdi { - rockchip,pins = <0x02 0x15 0x01 0x139>; - phandle = <0xdc>; - }; - - i2s2m0-sdo { - rockchip,pins = <0x02 0x14 0x01 0x139>; - phandle = <0xdd>; - }; - }; - - i2s3 { - - i2s3m0-lrck { - rockchip,pins = <0x03 0x04 0x04 0x13e>; - phandle = <0xdf>; - }; - - i2s3m0-sclk { - rockchip,pins = <0x03 0x03 0x04 0x13e>; - phandle = <0xde>; - }; - - i2s3m0-sdi { - rockchip,pins = <0x03 0x06 0x04 0x139>; - phandle = <0xe0>; - }; - - i2s3m0-sdo { - rockchip,pins = <0x03 0x05 0x04 0x139>; - phandle = <0xe1>; - }; - }; - - lcdc { - - lcdc-ctl { - rockchip,pins = <0x03 0x00 0x01 0x139 0x02 0x18 0x01 0x139 0x02 0x19 0x01 0x139 0x02 0x1a 0x01 0x139 0x02 0x1b 0x01 0x139 0x02 0x1c 0x01 0x139 0x02 0x1d 0x01 0x139 0x02 0x1e 0x01 0x139 0x02 0x1f 0x01 0x139 0x03 0x01 0x01 0x139 0x03 0x02 0x01 0x139 0x03 0x03 0x01 0x139 0x03 0x04 0x01 0x139 0x03 0x05 0x01 0x139 0x03 0x06 0x01 0x139 0x03 0x07 0x01 0x139 0x03 0x08 0x01 0x139 0x03 0x09 0x01 0x139 0x03 0x0a 0x01 0x139 0x03 0x0b 0x01 0x139 0x03 0x0c 0x01 0x139 0x03 0x0d 0x01 0x139 0x03 0x0e 0x01 0x139 0x03 0x0f 0x01 0x139 0x03 0x10 0x01 0x139 0x03 0x13 0x01 0x139 0x03 0x11 0x01 0x139 0x03 0x12 0x01 0x139>; - phandle = <0x39>; - }; - }; - - pdm { - - pdmm0-clk { - rockchip,pins = <0x01 0x06 0x03 0x139>; - phandle = <0xe2>; - }; - - pdmm0-clk1 { - rockchip,pins = <0x01 0x04 0x03 0x139>; - phandle = <0xe3>; - }; - - pdmm0-sdi0 { - rockchip,pins = <0x01 0x0b 0x02 0x139>; - phandle = <0xe4>; - }; - - pdmm0-sdi1 { - rockchip,pins = <0x01 0x0a 0x03 0x139>; - phandle = <0xe5>; - }; - - pdmm0-sdi2 { - rockchip,pins = <0x01 0x09 0x03 0x139>; - phandle = <0xe6>; - }; - - pdmm0-sdi3 { - rockchip,pins = <0x01 0x08 0x03 0x139>; - phandle = <0xe7>; - }; - }; - - pmic { - - pmic_int { - rockchip,pins = <0x00 0x03 0x00 0x13b>; - phandle = <0x40>; - }; - - soc_slppin_gpio { - rockchip,pins = <0x00 0x02 0x00 0x13f>; - phandle = <0x43>; - }; - - soc_slppin_slp { - rockchip,pins = <0x00 0x02 0x01 0x13b>; - phandle = <0x41>; - }; - - soc_slppin_rst { - rockchip,pins = <0x00 0x02 0x02 0x139>; - }; - - spk_ctl_gpio { - rockchip,pins = <0x03 0x15 0x00 0x13b>; - phandle = <0x48>; - }; - }; - - pwm0 { - - pwm0m0-pins { - rockchip,pins = <0x00 0x0f 0x01 0x139>; - phandle = <0x50>; - }; - }; - - pwm1 { - - pwm1m0-pins { - rockchip,pins = <0x00 0x10 0x01 0x139>; - phandle = <0x51>; - }; - }; - - pwm2 { - - pwm2m0-pins { - rockchip,pins = <0x00 0x11 0x01 0x139>; - phandle = <0x52>; - }; - }; - - pwm3 { - - pwm3-pins { - rockchip,pins = <0x00 0x12 0x01 0x139>; - phandle = <0x53>; - }; - }; - - pwm4 { - - pwm4-pins { - rockchip,pins = <0x00 0x13 0x01 0x139>; - phandle = <0x115>; - }; - }; - - pwm5 { - - pwm5-pins { - rockchip,pins = <0x00 0x14 0x01 0x139>; - phandle = <0x116>; - }; - }; - - pwm6 { - - pwm6-pins { - rockchip,pins = <0x00 0x15 0x01 0x139>; - phandle = <0x117>; - }; - }; - - pwm7 { - - pwm7-pins { - rockchip,pins = <0x00 0x16 0x01 0x139>; - phandle = <0x118>; - }; - }; - - pwm8 { - - pwm8m0-pins { - rockchip,pins = <0x03 0x09 0x05 0x139>; - phandle = <0x119>; - }; - }; - - pwm9 { - - pwm9m0-pins { - rockchip,pins = <0x03 0x0a 0x05 0x139>; - phandle = <0x11a>; - }; - }; - - pwm10 { - - pwm10m0-pins { - rockchip,pins = <0x03 0x0d 0x05 0x139>; - phandle = <0x11b>; - }; - }; - - pwm11 { - - pwm11m0-pins { - rockchip,pins = <0x03 0x0e 0x05 0x139>; - phandle = <0x11c>; - }; - }; - - pwm12 { - - pwm12m0-pins { - rockchip,pins = <0x03 0x0f 0x02 0x139>; - phandle = <0x11d>; - }; - }; - - pwm13 { - - pwm13m0-pins { - rockchip,pins = <0x03 0x10 0x02 0x139>; - phandle = <0x11e>; - }; - }; - - pwm14 { - - pwm14m0-pins { - rockchip,pins = <0x03 0x14 0x01 0x139>; - phandle = <0x11f>; - }; - }; - - pwm15 { - - pwm15m0-pins { - rockchip,pins = <0x03 0x15 0x01 0x139>; - phandle = <0x120>; - }; - }; - - scr { - - scr-pins { - rockchip,pins = <0x01 0x02 0x03 0x139 0x01 0x07 0x03 0x13b 0x01 0x03 0x03 0x13b 0x01 0x05 0x03 0x139>; - phandle = <0xeb>; - }; - }; - - sdmmc0 { - - sdmmc0-bus4 { - rockchip,pins = <0x01 0x1d 0x01 0x13a 0x01 0x1e 0x01 0x13a 0x01 0x1f 0x01 0x13a 0x02 0x00 0x01 0x13a>; - phandle = <0xd0>; - }; - - sdmmc0-clk { - rockchip,pins = <0x02 0x02 0x01 0x13a>; - phandle = <0xd1>; - }; - - sdmmc0-cmd { - rockchip,pins = <0x02 0x01 0x01 0x13a>; - phandle = <0xd2>; - }; - - sdmmc0-det { - rockchip,pins = <0x00 0x04 0x01 0x13b>; - phandle = <0xd3>; - }; - }; - - sdmmc2 { - - sdmmc2m0-bus4 { - rockchip,pins = <0x03 0x16 0x03 0x13a 0x03 0x17 0x03 0x13a 0x03 0x18 0x03 0x13a 0x03 0x19 0x03 0x13a>; - phandle = <0xb0>; - }; - - sdmmc2m0-clk { - rockchip,pins = <0x03 0x1b 0x03 0x13a>; - phandle = <0xb2>; - }; - - sdmmc2m0-cmd { - rockchip,pins = <0x03 0x1a 0x03 0x13a>; - phandle = <0xb1>; - }; - }; - - spdif { - - spdifm1-tx { - rockchip,pins = <0x03 0x15 0x02 0x139>; - phandle = <0xe9>; - }; - }; - - spi0 { - - spi0m0-pins { - rockchip,pins = <0x00 0x0d 0x02 0x139 0x00 0x15 0x02 0x139 0x00 0x0e 0x02 0x139>; - phandle = <0xfe>; - }; - - spi0m0-cs0 { - rockchip,pins = <0x00 0x16 0x02 0x139>; - phandle = <0xfc>; - }; - - spi0m0-cs1 { - rockchip,pins = <0x00 0x14 0x02 0x139>; - phandle = <0xfd>; - }; - }; - - spi1 { - - spi1m1-pins { - rockchip,pins = <0x03 0x13 0x03 0x139 0x03 0x12 0x03 0x139 0x03 0x11 0x03 0x139>; - phandle = <0x100>; - }; - }; - - spi2 { - - spi2m0-pins { - rockchip,pins = <0x02 0x11 0x04 0x139 0x02 0x12 0x04 0x139 0x02 0x13 0x04 0x139>; - phandle = <0x105>; - }; - - spi2m0-cs0 { - rockchip,pins = <0x02 0x14 0x04 0x139>; - phandle = <0x103>; - }; - - spi2m0-cs1 { - rockchip,pins = <0x02 0x15 0x04 0x139>; - phandle = <0x104>; - }; - }; - - spi3 { - - spi3m0-pins { - rockchip,pins = <0x04 0x0b 0x04 0x139 0x04 0x08 0x04 0x139 0x04 0x0a 0x04 0x139>; - phandle = <0x109>; - }; - - spi3m0-cs0 { - rockchip,pins = <0x04 0x06 0x04 0x139>; - phandle = <0x107>; - }; - - spi3m0-cs1 { - rockchip,pins = <0x04 0x07 0x04 0x139>; - phandle = <0x108>; - }; - }; - - tsadc { - - tsadc-shutorg { - rockchip,pins = <0x00 0x01 0x02 0x139>; - phandle = <0x124>; - }; - }; - - uart0 { - - uart0-xfer { - rockchip,pins = <0x00 0x10 0x03 0x13b 0x00 0x11 0x03 0x13b>; - phandle = <0x4f>; - }; - }; - - uart1 { - - uart1m0-xfer { - rockchip,pins = <0x02 0x0b 0x02 0x13b 0x02 0x0c 0x02 0x13b>; - phandle = <0x10b>; - }; - }; - - uart2 { - - uart2m0-xfer { - rockchip,pins = <0x00 0x18 0x01 0x13b 0x00 0x19 0x01 0x13b>; - phandle = <0x10c>; - }; - }; - - uart3 { - - uart3m1-xfer { - rockchip,pins = <0x03 0x10 0x04 0x13b 0x03 0x0f 0x04 0x13b>; - phandle = <0x10d>; - }; - }; - - uart4 { - - uart4m1-xfer { - rockchip,pins = <0x03 0x09 0x04 0x13b 0x03 0x0a 0x04 0x13b>; - phandle = <0x10e>; - }; - }; - - uart5 { - - uart5m0-xfer { - rockchip,pins = <0x02 0x01 0x03 0x13b 0x02 0x02 0x03 0x13b>; - phandle = <0x10f>; - }; - }; - - uart6 { - - uart6m0-xfer { - rockchip,pins = <0x02 0x03 0x03 0x13b 0x02 0x04 0x03 0x13b>; - phandle = <0x110>; - }; - }; - - uart7 { - - uart7m1-xfer { - rockchip,pins = <0x03 0x15 0x04 0x13b 0x03 0x14 0x04 0x13b>; - phandle = <0x111>; - }; - }; - - uart8 { - - uart8m0-xfer { - rockchip,pins = <0x02 0x16 0x02 0x13b 0x02 0x15 0x03 0x13b>; - phandle = <0x112>; - }; - - uart8m0-ctsn { - rockchip,pins = <0x02 0x0a 0x03 0x139>; - phandle = <0x113>; - }; - - uart8m0-rtsn { - rockchip,pins = <0x02 0x09 0x03 0x139>; - phandle = <0x155>; - }; - }; - - uart9 { - - uart9m1-xfer { - rockchip,pins = <0x04 0x16 0x04 0x13b 0x04 0x15 0x04 0x13b>; - phandle = <0x114>; - }; - }; - - spi0-hs { - - spi0m0-pins { - rockchip,pins = <0x00 0x0d 0x02 0x140 0x00 0x15 0x02 0x140 0x00 0x0e 0x02 0x140>; - phandle = <0xff>; - }; - }; - - spi1-hs { - - spi1m1-pins { - rockchip,pins = <0x03 0x13 0x03 0x140 0x03 0x12 0x03 0x140 0x03 0x11 0x03 0x140>; - phandle = <0x101>; - }; - }; - - spi2-hs { - - spi2m0-pins { - rockchip,pins = <0x02 0x11 0x04 0x140 0x02 0x12 0x04 0x140 0x02 0x13 0x04 0x140>; - phandle = <0x106>; - }; - }; - - spi3-hs { - - spi3m0-pins { - rockchip,pins = <0x04 0x0b 0x04 0x140 0x04 0x08 0x04 0x140 0x04 0x0a 0x04 0x140>; - phandle = <0x10a>; - }; - }; - - gpio-func { - - tsadc-gpio-func { - rockchip,pins = <0x00 0x01 0x00 0x139>; - phandle = <0x123>; - }; - }; - - usb { - - vcc5v0-host-en { - rockchip,pins = <0x00 0x06 0x00 0x139>; - phandle = <0x150>; - }; - - vcc5v0-otg-en { - rockchip,pins = <0x00 0x05 0x00 0x139>; - phandle = <0x151>; - }; - - vcc-hub-reset-en { - rockchip,pins = <0x01 0x04 0x00 0x139>; - phandle = <0x15a>; - }; - }; - - headphone { - - hp-det { - rockchip,pins = <0x03 0x12 0x00 0x142>; - phandle = <0x148>; - }; - }; - - sdio-pwrseq { - - wifi-enable-h { - rockchip,pins = <0x03 0x1d 0x00 0x139>; - phandle = <0x153>; - }; - }; - - wireless-wlan { - - wifi-host-wake-irq { - rockchip,pins = <0x03 0x1c 0x00 0x142>; - phandle = <0x154>; - }; - }; - - wireless-bluetooth { - - uart8-gpios { - rockchip,pins = <0x02 0x09 0x00 0x139>; - phandle = <0x156>; - }; - }; - - touch { - - touch-gpio { - rockchip,pins = <0x00 0x0d 0x00 0x13b 0x00 0x0e 0x00 0x139>; - phandle = <0xef>; - }; - }; - - mxc6655xa { - - mxc6655xa_irq_gpio { - rockchip,pins = <0x03 0x11 0x00 0x139>; - phandle = <0xfb>; - }; - }; - - pcie { - - pcie-pi6c-oe-en { - rockchip,pins = <0x03 0x07 0x00 0x139>; - phandle = <0x15b>; - }; - }; - - leds { - - leds-gpio { - rockchip,pins = <0x01 0x0a 0x00 0x139 0x01 0x09 0x00 0x139 0x01 0x08 0x00 0x139 0x02 0x11 0x00 0x139>; - phandle = <0x15d>; - }; - }; - - 4g { - - vcc-4g-power-en { - rockchip,pins = <0x03 0x03 0x00 0x139>; - phandle = <0x15c>; - }; - }; - - usb-typec { - - usbc0-int { - rockchip,pins = <0x00 0x11 0x00 0x13b>; - phandle = <0x4b>; - }; - - vcc5v0-typec0-en { - rockchip,pins = <0x00 0x05 0x00 0x139>; - }; - }; - }; - - audiopwmout-diff { - status = "disabled"; - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "rockchip,audiopwmout-diff"; - simple-audio-card,mclk-fs = <0x100>; - simple-audio-card,bitclock-master = <0x143>; - simple-audio-card,frame-master = <0x143>; - - simple-audio-card,cpu { - sound-dai = <0x144>; - }; - - simple-audio-card,codec { - sound-dai = <0x145>; - phandle = <0x143>; - }; - }; - - dc-12v { - compatible = "regulator-fixed"; - regulator-name = "dc_12v"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b00>; - regulator-max-microvolt = <0xb71b00>; - phandle = <0x14f>; - }; - - hdmi-sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,mclk-fs = <0x80>; - simple-audio-card,name = "rockchip,hdmi"; - status = "okay"; - - simple-audio-card,cpu { - sound-dai = <0x146>; - }; - - simple-audio-card,codec { - sound-dai = <0x147>; - }; - }; - - rk-headset { - status = "disabled"; - compatible = "rockchip_headset"; - headset_gpio = <0x4a 0x12 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x148>; - }; - - dummy-codec { - status = "disabled"; - compatible = "rockchip,dummy-codec"; - #sound-dai-cells = <0x00>; - phandle = <0x14a>; - }; - - pdm-mic-array { - status = "disabled"; - compatible = "simple-audio-card"; - simple-audio-card,name = "rockchip,pdm-mic-array"; - - simple-audio-card,cpu { - sound-dai = <0x149>; - }; - - simple-audio-card,codec { - sound-dai = <0x14a>; - }; - }; - - rk809-sound { - status = "okay"; - compatible = "rockchip,multicodecs-card"; - rockchip,card-name = "rockchip-rk809"; - rockchip,format = "i2s"; - rockchip,mclk-fs = <0x100>; - rockchip,cpu = <0xe8>; - rockchip,codec = <0x14b>; - }; - - spdif-sound { - status = "okay"; - compatible = "simple-audio-card"; - simple-audio-card,name = "ROCKCHIP,SPDIF"; - - simple-audio-card,cpu { - sound-dai = <0x14c>; - }; - - simple-audio-card,codec { - sound-dai = <0x14d>; - }; - }; - - spdif-out { - status = "okay"; - compatible = "linux,spdif-dit"; - #sound-dai-cells = <0x00>; - phandle = <0x14d>; - }; - - vad-sound { - status = "disabled"; - compatible = "rockchip,multicodecs-card"; - rockchip,card-name = "rockchip,rk3568-vad"; - rockchip,cpu = <0xe8>; - rockchip,codec = <0x14b 0x14e>; - }; - - vcc3v3-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - vin-supply = <0x14f>; - phandle = <0x46>; - }; - - vcc5v0-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - vin-supply = <0x14f>; - phandle = <0x3e>; - }; - - vcc5v0-host-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x3f 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x150>; - regulator-name = "vcc5v0_host"; - regulator-always-on; - regulator-boot-on; - phandle = <0x136>; - }; - - vcc5v0-otg-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x3f 0x05 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x151>; - regulator-name = "vcc5v0_otg"; - phandle = <0x4c>; - }; - - vcc3v3-lcd0-n { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_lcd0_n"; - regulator-boot-on; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc3v3-lcd1-n { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_lcd1_n"; - regulator-boot-on; - status = "disabled"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - test-power { - status = "okay"; - }; - - chosen { - // linux,initrd-end = <0x00 0xaacf15d>; - // linux,initrd-start = <0x00 0xa200000>; - bootargs = "storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal androidboot.verifiedbootstate=orange rw rootwait earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 root=PARTLABEL=rootfs rootfstype=ext4 overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 swiotlb=0x10000 net.ifnames=0 comm-05/20/2025 androidboot.fwver=ddr-v1.21-2d653b3476,spl-v1.14,bl31-v1.44,bl32-v2.12,uboot--boot"; - }; - - fiq-debugger { - compatible = "rockchip,fiq-debugger"; - rockchip,serial-id = <0x02>; - rockchip,wake-irq = <0x00>; - rockchip,irq-mode-enable = <0x01>; - rockchip,baudrate = <0x16e360>; - interrupts = <0x00 0xfc 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "okay"; - }; - - debug@fd904000 { - compatible = "rockchip,debug"; - reg = <0x00 0xfd904000 0x00 0x1000 0x00 0xfd905000 0x00 0x1000 0x00 0xfd906000 0x00 0x1000 0x00 0xfd907000 0x00 0x1000>; - }; - - cspmu@fd90c000 { - compatible = "rockchip,cspmu"; - reg = <0x00 0xfd90c000 0x00 0x1000 0x00 0xfd90d000 0x00 0x1000 0x00 0xfd90e000 0x00 0x1000 0x00 0xfd90f000 0x00 0x1000>; - }; - - adc-keys { - compatible = "adc-keys"; - io-channels = <0x49 0x00>; - io-channel-names = "buttons"; - keyup-threshold-microvolt = <0x1b7740>; - poll-interval = <0x64>; - - recovery-key { - label = "F12"; - linux,code = <0x58>; - press-threshold-microvolt = <0x6d6>; - }; - - vol-down-key { - label = "volume down"; - linux,code = <0x72>; - press-threshold-microvolt = <0x48a1c>; - }; - - menu-key { - label = "menu"; - linux,code = <0x8b>; - press-threshold-microvolt = <0xef420>; - }; - - back-key { - label = "back"; - linux,code = <0x9e>; - press-threshold-microvolt = <0x13eb9c>; - }; - }; - - vcc2v5-ddr { - compatible = "regulator-fixed"; - regulator-name = "vcc2v5-sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x2625a0>; - regulator-max-microvolt = <0x2625a0>; - vin-supply = <0x46>; - }; - - pcie30-avdd0v9 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd0v9"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - vin-supply = <0x46>; - }; - - pcie30-avdd1v8 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd1v8"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - vin-supply = <0x46>; - }; - - gpio-regulator { - compatible = "regulator-gpio"; - regulator-name = "pcie30_3v3"; - regulator-min-microvolt = <0x186a0>; - regulator-max-microvolt = <0x325aa0>; - gpios = <0x3f 0x1c 0x00>; - gpios-states = <0x01>; - states = <0x186a0 0x00 0x325aa0 0x01>; - phandle = <0xc2>; - }; - - vcc3v3-bu { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_bu"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - vin-supply = <0x3e>; - }; - - sdio-pwrseq { - compatible = "mmc-pwrseq-simple"; - clocks = <0x152 0x01>; - clock-names = "ext_clock"; - pinctrl-names = "default"; - pinctrl-0 = <0x153>; - post-power-on-delay-ms = <0x64>; - reset-gpios = <0x4a 0x1d 0x01>; - status = "okay"; - phandle = <0xb3>; - }; - - wireless-wlan { - compatible = "wlan-platdata"; - rockchip,grf = <0x3b>; - wifi_chip_type = "ap6256"; - pinctrl-names = "default"; - pinctrl-0 = <0x154>; - WIFI,host_wake_irq = <0x4a 0x1c 0x00>; - status = "okay"; - }; - - wireless-bluetooth { - compatible = "bluetooth-platdata"; - clocks = <0x152 0x01>; - clock-names = "ext_clock"; - uart_rts_gpios = <0x91 0x09 0x01>; - pinctrl-names = "default\0rts_gpio"; - pinctrl-0 = <0x155>; - pinctrl-1 = <0x156>; - BT,reset_gpio = <0x4a 0x00 0x00>; - BT,wake_gpio = <0x4a 0x02 0x00>; - BT,wake_host_irq = <0x4a 0x01 0x00>; - status = "okay"; - }; - - flash-led { - compatible = "led,rgb13h"; - label = "pwm-flash-led"; - led-max-microamp = <0x4e20>; - flash-max-microamp = <0x4e20>; - flash-max-timeout-us = <0xf4240>; - pwms = <0x157 0x00 0x61a8 0x00>; - rockchip,camera-module-index = <0x01>; - rockchip,camera-module-facing = "front"; - status = "disabled"; - }; - - vcc-camera-regulator { - compatible = "regulator-fixed"; - gpio = <0x102 0x03 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x158>; - regulator-name = "vcc_camera"; - enable-active-high; - status = "disabled"; - }; - - vcc-hub-reset-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x159 0x04 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x15a>; - regulator-name = "vcc_hub_reset_en"; - regulator-always-on; - }; - - pcie-pi6c-oe-regulator { - compatible = "regulator-fixed"; - gpio = <0x4a 0x07 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x15b>; - regulator-name = "pcie_pi6c_oe_en"; - regulator-always-on; - }; - - vcc-4g-power-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x4a 0x03 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x15c>; - regulator-name = "vcc_4g_power_en"; - regulator-always-on; - }; - - leds { - status = "okay"; - compatible = "gpio-leds"; - pinctrl-names = "default"; - pinctrl-0 = <0x15d>; - - power { - label = "firefly:blue:power"; - linux,default-trigger = "ir-power-click"; - default-state = "on"; - gpios = <0x159 0x0a 0x00>; - }; - - user { - label = "firefly:yellow:user"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x159 0x09 0x00>; - }; - - diy1 { - label = "firefly:green:diy"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x159 0x08 0x00>; - }; - - diy2 { - label = "firefly:yellow:diy"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x91 0x11 0x00>; - }; - }; -}; \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.toml b/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.toml deleted file mode 100644 index ed0cf421f..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.toml +++ /dev/null @@ -1,62 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 2 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x00] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8008_0000 -# The location of image: "memory" | "fs". -# Load from memory. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x8008_0000 -## The file path of the kernel image. -kernel_path = "/userdata/rootfs_overlay/guest/linux/roc-rk3568-pc" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x8000_0000 -#dtb_path = "/path/linux-aarch64-rk3568_smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x6000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] -# -# Device specifications -# -[devices] -# The interrupt mode. -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.dts b/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.dts deleted file mode 100644 index 5475dbe6e..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.dts +++ /dev/null @@ -1,6108 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000008300000 0x000000000001c000; -/memreserve/ 0x000000000a200000 0x00000000008cf15d; -/ { - serial-number = "425ca8fc29ade692"; - compatible = "rockchip,rk3568-firefly-roc-pc-se\0rockchip,rk3568"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Firefly RK3568-ROC-PC-SE HDMI (Linux)"; - - memory { - reg = <0x00 0x80000000 0x00 0x60000000>; - device_type = "memory"; - }; - - ddr3-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x12c>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x21>; - phy_ca_drv_odten = <0x21>; - phy_clk_drv_odten = <0x21>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x21>; - phy_ca_drv_odtoff = <0x21>; - phy_clk_drv_odtoff = <0x21>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0xa7>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x14d>; - phy_odt_en_freq = <0x14d>; - phy_dq_sr_odten = <0x0f>; - phy_ca_sr_odten = <0x03>; - phy_clk_sr_odten = <0x00>; - phy_dq_sr_odtoff = <0x0f>; - phy_ca_sr_odtoff = <0x03>; - phy_clk_sr_odtoff = <0x00>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x15>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - phandle = <0xb7>; - }; - - ddr4-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x271>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x25>; - phy_ca_drv_odten = <0x25>; - phy_clk_drv_odten = <0x25>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x25>; - phy_ca_drv_odtoff = <0x25>; - phy_clk_drv_odtoff = <0x25>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0x8b>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x1f4>; - phy_odt_en_freq = <0x1f4>; - phy_dq_sr_odten = <0x0e>; - phy_ca_sr_odten = <0x01>; - phy_clk_sr_odten = <0x01>; - phy_dq_sr_odtoff = <0x0e>; - phy_ca_sr_odtoff = <0x01>; - phy_clk_sr_odtoff = <0x01>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x0c>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x22777788>; - dq_map_cs0_dq_h = <0xd7888877>; - dq_map_cs1_dq_l = <0x22777788>; - dq_map_cs1_dq_h = <0xd7888877>; - phandle = <0xb8>; - }; - - lpddr3-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x25>; - phy_ca_drv_odten = <0x25>; - phy_clk_drv_odten = <0x27>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x25>; - phy_ca_drv_odtoff = <0x25>; - phy_clk_drv_odtoff = <0x27>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0x94>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x14d>; - phy_odt_en_freq = <0x14d>; - phy_dq_sr_odten = <0x0f>; - phy_ca_sr_odten = <0x01>; - phy_clk_sr_odten = <0x0f>; - phy_dq_sr_odtoff = <0x0f>; - phy_ca_sr_odtoff = <0x01>; - phy_clk_sr_odtoff = <0x0f>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0x8d>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - phandle = <0xb9>; - }; - - lpddr4-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x618>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x1e>; - phy_ca_drv_odten = <0x26>; - phy_clk_drv_odten = <0x26>; - dram_dq_drv_odten = <0x28>; - phy_dq_drv_odtoff = <0x1e>; - phy_ca_drv_odtoff = <0x26>; - phy_clk_drv_odtoff = <0x26>; - dram_dq_drv_odtoff = <0x28>; - dram_odt = <0x50>; - phy_odt = <0x3c>; - phy_odt_puup_en = <0x00>; - phy_odt_pudn_en = <0x00>; - dram_dq_odt_en_freq = <0x320>; - phy_odt_en_freq = <0x320>; - phy_dq_sr_odten = <0x00>; - phy_ca_sr_odten = <0x0f>; - phy_clk_sr_odten = <0x0f>; - phy_dq_sr_odtoff = <0x00>; - phy_ca_sr_odtoff = <0x0f>; - phy_clk_sr_odtoff = <0x0f>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - lp4_ca_odt = <0x78>; - lp4_drv_pu_cal_odten = <0x01>; - lp4_drv_pu_cal_odtoff = <0x01>; - phy_lp4_drv_pulldown_en_odten = <0x00>; - phy_lp4_drv_pulldown_en_odtoff = <0x00>; - lp4_ca_odt_en_freq = <0x320>; - phy_lp4_cs_drv_odten = <0x00>; - phy_lp4_cs_drv_odtoff = <0x00>; - lp4_odte_ck_en = <0x01>; - lp4_odte_cs_en = <0x01>; - lp4_odtd_ca_en = <0x00>; - phy_lp4_dq_vref_odten = <0xa6>; - lp4_dq_vref_odten = <0x12c>; - lp4_ca_vref_odten = <0x17c>; - phy_lp4_dq_vref_odtoff = <0x1a4>; - lp4_dq_vref_odtoff = <0x1a4>; - lp4_ca_vref_odtoff = <0x1a4>; - phandle = <0xba>; - }; - - lpddr4x-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x618>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x1d>; - phy_ca_drv_odten = <0x24>; - phy_clk_drv_odten = <0x24>; - dram_dq_drv_odten = <0x28>; - phy_dq_drv_odtoff = <0x1d>; - phy_ca_drv_odtoff = <0x24>; - phy_clk_drv_odtoff = <0x24>; - dram_dq_drv_odtoff = <0x28>; - dram_odt = <0x50>; - phy_odt = <0x3c>; - phy_odt_puup_en = <0x00>; - phy_odt_pudn_en = <0x00>; - dram_dq_odt_en_freq = <0x320>; - phy_odt_en_freq = <0x320>; - phy_dq_sr_odten = <0x00>; - phy_ca_sr_odten = <0x00>; - phy_clk_sr_odten = <0x00>; - phy_dq_sr_odtoff = <0x00>; - phy_ca_sr_odtoff = <0x00>; - phy_clk_sr_odtoff = <0x00>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - lp4_ca_odt = <0x78>; - lp4_drv_pu_cal_odten = <0x00>; - lp4_drv_pu_cal_odtoff = <0x00>; - phy_lp4_drv_pulldown_en_odten = <0x00>; - phy_lp4_drv_pulldown_en_odtoff = <0x00>; - lp4_ca_odt_en_freq = <0x320>; - phy_lp4_cs_drv_odten = <0x00>; - phy_lp4_cs_drv_odtoff = <0x00>; - lp4_odte_ck_en = <0x00>; - lp4_odte_cs_en = <0x00>; - lp4_odtd_ca_en = <0x00>; - phy_lp4_dq_vref_odten = <0xa6>; - lp4_dq_vref_odten = <0xe4>; - lp4_ca_vref_odten = <0x157>; - phy_lp4_dq_vref_odtoff = <0x1a4>; - lp4_dq_vref_odtoff = <0x1a4>; - lp4_ca_vref_odtoff = <0x157>; - phandle = <0xbb>; - }; - - aliases { - csi2dphy0 = "/csi2-dphy0"; - csi2dphy1 = "/csi2-dphy1"; - csi2dphy2 = "/csi2-dphy2"; - dsi0 = "/dsi@fe060000"; - dsi1 = "/dsi@fe070000"; - ethernet0 = "/ethernet@fe2a0000"; - ethernet1 = "/ethernet@fe010000"; - gpio0 = "/pinctrl/gpio0@fdd60000"; - gpio1 = "/pinctrl/gpio1@fe740000"; - gpio2 = "/pinctrl/gpio2@fe750000"; - gpio3 = "/pinctrl/gpio3@fe760000"; - gpio4 = "/pinctrl/gpio4@fe770000"; - i2c0 = "/i2c@fdd40000"; - i2c1 = "/i2c@fe5a0000"; - i2c2 = "/i2c@fe5b0000"; - i2c3 = "/i2c@fe5c0000"; - i2c4 = "/i2c@fe5d0000"; - i2c5 = "/i2c@fe5e0000"; - mmc0 = "/sdhci@fe310000"; - mmc1 = "/dwmmc@fe2b0000"; - mmc2 = "/dwmmc@fe2c0000"; - mmc3 = "/dwmmc@fe000000"; - serial0 = "/serial@fdd50000"; - serial1 = "/serial@fe650000"; - serial2 = "/serial@fe660000"; - serial3 = "/serial@fe670000"; - serial4 = "/serial@fe680000"; - serial5 = "/serial@fe690000"; - serial6 = "/serial@fe6a0000"; - serial7 = "/serial@fe6b0000"; - serial8 = "/serial@fe6c0000"; - serial9 = "/serial@fe6d0000"; - spi0 = "/spi@fe610000"; - spi1 = "/spi@fe620000"; - spi2 = "/spi@fe630000"; - spi3 = "/spi@fe640000"; - lvds0 = "/syscon@fdc60000/lvds"; - lvds1 = "/syscon@fdc60000/lvds1"; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - // cpu@0 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x00 0x00>; - // enable-method = "psci"; - // clocks = <0x02 0x00>; - // operating-points-v2 = <0x03>; - // cpu-idle-states = <0x04>; - // #cooling-cells = <0x02>; - // dynamic-power-coefficient = <0xbb>; - // cpu-supply = <0x05>; - // phandle = <0x0c>; - // }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x00 0x100>; - // enable-method = "psci"; - // clocks = <0x02 0x00>; - // operating-points-v2 = <0x03>; - // cpu-idle-states = <0x04>; - // phandle = <0x0d>; - // }; - - cpu@200 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x200>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - phandle = <0x0e>; - }; - - cpu@300 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x300>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - phandle = <0x0f>; - }; - - idle-states { - entry-method = "psci"; - - cpu-sleep { - compatible = "arm,idle-state"; - local-timer-stop; - arm,psci-suspend-param = <0x10000>; - entry-latency-us = <0x64>; - exit-latency-us = <0x78>; - min-residency-us = <0x3e8>; - phandle = <0x04>; - }; - }; - }; - - cpu0-opp-table { - compatible = "operating-points-v2"; - opp-shared; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x06 0x07 0x08 0x09 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0x118c30>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-freq = <0x639c0>; - rockchip,pvtm-volt = <0xdbba0>; - rockchip,pvtm-ch = <0x00 0x05>; - rockchip,pvtm-sample-time = <0x3e8>; - rockchip,pvtm-number = <0x0a>; - rockchip,pvtm-error = <0x3e8>; - rockchip,pvtm-ref-temp = <0x28>; - rockchip,pvtm-temp-prop = <0x1a 0x1a>; - rockchip,thermal-zone = "soc-thermal"; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x7c8 0x124f8>; - phandle = <0x03>; - - opp-408000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x18519600>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-816000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x30a32c00>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - opp-suspend; - }; - - opp-1104000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x41cdb400>; - opp-microvolt = <0xdbba0 0xdbba0 0x118c30>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0x118c30>; - opp-microvolt-L1 = <0xcf850 0xcf850 0x118c30>; - opp-microvolt-L2 = <0xcf850 0xcf850 0x118c30>; - opp-microvolt-L3 = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1416000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x54667200>; - opp-microvolt = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L0 = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L1 = <0xee098 0xee098 0x118c30>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0x118c30>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1608000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x5fd82200>; - opp-microvolt = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L0 = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L1 = <0x100590 0x100590 0x118c30>; - opp-microvolt-L2 = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L3 = <0xf4240 0xf4240 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1800000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x6b49d200>; - opp-microvolt = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L0 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L1 = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L2 = <0x106738 0x106738 0x118c30>; - opp-microvolt-L3 = <0x100590 0x100590 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1992000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x76bb8200>; - opp-microvolt = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L0 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L1 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L2 = <0x112a88 0x112a88 0x118c30>; - opp-microvolt-L3 = <0x10c8e0 0x10c8e0 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-j-1008000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x3c14dc00>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-j-1416000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x54667200>; - opp-microvolt = <0xdbba0 0xdbba0 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-m-1608000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x5fd82200>; - opp-microvolt = <0xf4240 0xf4240 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - }; - - arm-pmu { - compatible = "arm,cortex-a55-pmu\0arm,armv8-pmuv3"; - interrupts = <0x00 0xe4 0x04 0x00 0xe5 0x04 0x00 0xe6 0x04 0x00 0xe7 0x04>; - interrupt-affinity = <0x0c 0x0d 0x0e 0x0f>; - }; - - cpuinfo { - compatible = "rockchip,cpuinfo"; - nvmem-cells = <0x10 0x11 0x12>; - nvmem-cell-names = "id\0cpu-version\0cpu-code"; - }; - - display-subsystem { - compatible = "rockchip,display-subsystem"; - memory-region = <0x13 0x14>; - memory-region-names = "drm-logo\0drm-cubic-lut"; - ports = <0x15>; - devfreq = <0x16>; - - route { - - route-dsi0 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x17>; - }; - - route-dsi1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x18>; - }; - - route-edp { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x19>; - }; - - route-hdmi { - status = "okay"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1a>; - }; - - route-lvds { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1b>; - }; - - route-rgb { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1c>; - }; - }; - }; - - edac { - compatible = "rockchip,rk3568-edac"; - interrupts = <0x00 0xad 0x04 0x00 0xaf 0x04>; - interrupt-names = "ce\0ue"; - status = "disabled"; - }; - - firmware { - - scmi { - compatible = "arm,scmi-smc"; - shmem = <0x1d>; - arm,smc-id = <0x82000010>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@14 { - reg = <0x14>; - #clock-cells = <0x01>; - rockchip,clk-init = <0x41cdb400>; - phandle = <0x02>; - }; - }; - - sdei { - compatible = "arm,sdei-1.0"; - method = "smc"; - }; - }; - - mipi-csi2 { - compatible = "rockchip,rk3568-mipi-csi2"; - rockchip,hw = <0x1e>; - status = "disabled"; - }; - - mpp-srv { - compatible = "rockchip,mpp-service"; - rockchip,taskqueue-count = <0x06>; - rockchip,resetgroup-count = <0x06>; - status = "okay"; - phandle = <0x7b>; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - }; - - reserved-memory { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - drm-logo@00000000 { - compatible = "rockchip,drm-logo"; - reg = <0x00 0xedf00000 0x00 0x2e0000>; - phandle = <0x13>; - }; - - drm-cubic-lut@00000000 { - compatible = "rockchip,drm-cubic-lut"; - reg = <0x00 0xeff00000 0x00 0x8000>; - phandle = <0x14>; - }; - - ramoops@110000 { - compatible = "ramoops"; - reg = <0x00 0x110000 0x00 0xf0000>; - record-size = <0x20000>; - console-size = <0x80000>; - ftrace-size = <0x00>; - pmsg-size = <0x50000>; - }; - }; - - rockchip-suspend { - compatible = "rockchip,pm-rk3568"; - status = "okay"; - rockchip,sleep-debug-en = <0x01>; - rockchip,sleep-mode-config = <0x5ec>; - rockchip,wakeup-config = <0x10>; - }; - - rockchip-system-monitor { - compatible = "rockchip,system-monitor"; - rockchip,thermal-zone = "soc-thermal"; - }; - - thermal-zones { - - soc-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - sustainable-power = <0x389>; - thermal-sensors = <0x1f 0x00>; - - trips { - - trip-point-0 { - temperature = <0x124f8>; - hysteresis = <0x7d0>; - type = "passive"; - }; - - trip-point-1 { - temperature = <0x14c08>; - hysteresis = <0x7d0>; - type = "passive"; - phandle = <0x20>; - }; - - soc-crit { - temperature = <0x1c138>; - hysteresis = <0x7d0>; - type = "critical"; - }; - }; - - cooling-maps { - - map0 { - trip = <0x20>; - cooling-device = <0x0c 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - - map1 { - trip = <0x20>; - cooling-device = <0x21 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - }; - }; - - gpu-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x1f 0x01>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; - arm,no-tick-in-suspend; - }; - - external-gmac0-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "gmac0_clkin"; - #clock-cells = <0x00>; - phandle = <0xc7>; - }; - - external-gmac1-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "gmac1_clkin"; - #clock-cells = <0x00>; - phandle = <0x92>; - }; - - xpcs-gmac0-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "clk_gmac0_xpcs_mii"; - #clock-cells = <0x00>; - }; - - xpcs-gmac1-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "clk_gmac1_xpcs_mii"; - #clock-cells = <0x00>; - }; - - i2s1-mclkin-rx { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s1_mclkin_rx"; - }; - - i2s1-mclkin-tx { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s1_mclkin_tx"; - }; - - i2s2-mclkin { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s2_mclkin"; - }; - - i2s3-mclkin { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s3_mclkin"; - }; - - mpll { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2faf0800>; - clock-output-names = "mpll"; - }; - - xin24m { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x16e3600>; - clock-output-names = "xin24m"; - }; - - xin32k { - compatible = "fixed-clock"; - clock-frequency = <0x8000>; - clock-output-names = "xin32k"; - #clock-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x22>; - }; - - scmi-shmem@10f000 { - compatible = "arm,scmi-shmem"; - reg = <0x00 0x10f000 0x00 0x100>; - phandle = <0x1d>; - }; - - sata@fc000000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc000000 0x00 0x1000>; - clocks = <0x23 0x96 0x23 0x97 0x23 0x98>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x5e 0x04>; - interrupt-names = "hostc"; - phys = <0x24 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "disabled"; - }; - - sata@fc400000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc400000 0x00 0x1000>; - clocks = <0x23 0x9b 0x23 0x9c 0x23 0x9d>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x5f 0x04>; - interrupt-names = "hostc"; - phys = <0x26 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "disabled"; - }; - - sata@fc800000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc800000 0x00 0x1000>; - clocks = <0x23 0xa0 0x23 0xa1 0x23 0xa2>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x60 0x04>; - interrupt-names = "hostc"; - phys = <0x27 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "okay"; - }; - - usbdrd { - compatible = "rockchip,rk3568-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x23 0xa6 0x23 0xa7 0x23 0xa5 0x23 0x7f>; - clock-names = "ref_clk\0suspend_clk\0bus_clk\0pipe_clk"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - dwc3@fcc00000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfcc00000 0x00 0x400000>; - interrupts = <0x00 0xa9 0x04>; - dr_mode = "otg"; - phys = <0x28 0x24 0x04>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - power-domains = <0x25 0x0f>; - resets = <0x23 0x94>; - reset-names = "usb3-otg"; - snps,dis_enblslpm_quirk; - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,dis_rxdet_inp3_quirk; - snps,parkmode-disable-hs-quirk; - snps,parkmode-disable-ss-quirk; - quirk-skip-phy-init; - status = "okay"; - extcon = <0x29>; - usb-role-switch; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x2a>; - phandle = <0x4d>; - }; - }; - }; - }; - - usbhost { - compatible = "rockchip,rk3568-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x23 0xa9 0x23 0xaa 0x23 0xa8 0x23 0x7f>; - clock-names = "ref_clk\0suspend_clk\0bus_clk\0pipe_clk"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - dwc3@fd000000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfd000000 0x00 0x400000>; - interrupts = <0x00 0xaa 0x04>; - dr_mode = "host"; - phys = <0x2b 0x26 0x04>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - power-domains = <0x25 0x0f>; - resets = <0x23 0x95>; - reset-names = "usb3-host"; - snps,dis_enblslpm_quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,dis_rxdet_inp3_quirk; - snps,parkmode-disable-hs-quirk; - snps,parkmode-disable-ss-quirk; - status = "okay"; - }; - }; - - interrupt-controller@fd400000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0xfd400000 0x00 0x10000 0x00 0xfd460000 0x00 0xc0000>; - interrupts = <0x01 0x09 0x04>; - phandle = <0x01>; - - interrupt-controller@fd440000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <0x01>; - reg = <0x00 0xfd440000 0x00 0x20000>; - status = "okay"; - phandle = <0xbe>; - }; - }; - - usb@fd800000 { - compatible = "generic-ehci"; - reg = <0x00 0xfd800000 0x00 0x40000>; - interrupts = <0x00 0x82 0x04>; - clocks = <0x23 0xbd 0x23 0xbe 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2d>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd840000 { - compatible = "generic-ohci"; - reg = <0x00 0xfd840000 0x00 0x40000>; - interrupts = <0x00 0x83 0x04>; - clocks = <0x23 0xbd 0x23 0xbe 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2d>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd880000 { - compatible = "generic-ehci"; - reg = <0x00 0xfd880000 0x00 0x40000>; - interrupts = <0x00 0x85 0x04>; - clocks = <0x23 0xbf 0x23 0xc0 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2e>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd8c0000 { - compatible = "generic-ohci"; - reg = <0x00 0xfd8c0000 0x00 0x40000>; - interrupts = <0x00 0x86 0x04>; - clocks = <0x23 0xbf 0x23 0xc0 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2e>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - syscon@fda00000 { - compatible = "rockchip,rk3568-xpcs\0syscon"; - reg = <0x00 0xfda00000 0x00 0x200000>; - status = "disabled"; - }; - - syscon@fdc20000 { - compatible = "rockchip,rk3568-pmugrf\0syscon\0simple-mfd"; - reg = <0x00 0xfdc20000 0x00 0x10000>; - phandle = <0x3c>; - - io-domains { - compatible = "rockchip,rk3568-pmu-io-voltage-domain"; - status = "okay"; - pmuio1-supply = <0x2f>; - pmuio2-supply = <0x2f>; - vccio1-supply = <0x30>; - vccio3-supply = <0x31>; - vccio4-supply = <0x32>; - vccio5-supply = <0x33>; - vccio6-supply = <0x32>; - vccio7-supply = <0x33>; - }; - - reboot-mode { - compatible = "syscon-reboot-mode"; - offset = <0x200>; - mode-bootloader = <0x5242c301>; - mode-charge = <0x5242c30b>; - mode-fastboot = <0x5242c309>; - mode-loader = <0x5242c301>; - mode-normal = <0x5242c300>; - mode-recovery = <0x5242c303>; - mode-ums = <0x5242c30c>; - mode-panic = <0x5242c307>; - mode-watchdog = <0x5242c308>; - }; - }; - - syscon@fdc50000 { - compatible = "rockchip,rk3568-pipegrf\0syscon"; - reg = <0x00 0xfdc50000 0x00 0x1000>; - phandle = <0x12a>; - }; - - syscon@fdc60000 { - compatible = "rockchip,rk3568-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfdc60000 0x00 0x10000>; - phandle = <0x3b>; - - io-domains { - compatible = "rockchip,rk3568-io-voltage-domain"; - status = "disabled"; - }; - - lvds { - compatible = "rockchip,rk3568-lvds"; - phys = <0x34>; - phy-names = "phy"; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x1b>; - status = "disabled"; - phandle = <0xa3>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x35>; - status = "disabled"; - phandle = <0xa5>; - }; - }; - }; - }; - - lvds1 { - compatible = "rockchip,rk3568-lvds"; - phys = <0x36>; - phy-names = "phy"; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x37>; - phandle = <0xa4>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x38>; - phandle = <0xa7>; - }; - }; - }; - }; - - rgb { - compatible = "rockchip,rk3568-rgb"; - pinctrl-names = "default"; - pinctrl-0 = <0x39>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x1c>; - status = "disabled"; - phandle = <0xa6>; - }; - }; - }; - }; - }; - - syscon@fdc70000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc70000 0x00 0x1000>; - phandle = <0x12b>; - }; - - syscon@fdc80000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc80000 0x00 0x1000>; - phandle = <0x12c>; - }; - - syscon@fdc90000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc90000 0x00 0x1000>; - phandle = <0x12d>; - }; - - syscon@fdca0000 { - compatible = "rockchip,rk3568-usb2phy-grf\0syscon"; - reg = <0x00 0xfdca0000 0x00 0x8000>; - phandle = <0x135>; - }; - - syscon@fdca8000 { - compatible = "rockchip,rk3568-usb2phy-grf\0syscon"; - reg = <0x00 0xfdca8000 0x00 0x8000>; - phandle = <0x137>; - }; - - syscon@fdcb0000 { - compatible = "rockchip,rk3568-edp-phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfdcb0000 0x00 0x100>; - clocks = <0x23 0x192>; - - edp-phy { - compatible = "rockchip,rk3568-edp-phy"; - clocks = <0x3a 0x29>; - clock-names = "refclk"; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0xae>; - }; - }; - - syscon@fdcb8000 { - compatible = "rockchip,pcie30-phy-grf\0syscon"; - reg = <0x00 0xfdcb8000 0x00 0x10000>; - phandle = <0x138>; - }; - - sram@fdcc0000 { - compatible = "mmio-sram"; - reg = <0x00 0xfdcc0000 0x00 0xb000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0xfdcc0000 0xb000>; - - rkvdec-sram@0 { - reg = <0x00 0xb000>; - phandle = <0x84>; - }; - }; - - clock-controller@fdd00000 { - compatible = "rockchip,rk3568-pmucru"; - reg = <0x00 0xfdd00000 0x00 0x1000>; - rockchip,grf = <0x3b>; - rockchip,pmugrf = <0x3c>; - #clock-cells = <0x01>; - #reset-cells = <0x01>; - assigned-clocks = <0x3a 0x32>; - assigned-clock-parents = <0x3a 0x05>; - phandle = <0x3a>; - }; - - clock-controller@fdd20000 { - compatible = "rockchip,rk3568-cru"; - reg = <0x00 0xfdd20000 0x00 0x1000>; - rockchip,grf = <0x3b>; - #clock-cells = <0x01>; - #reset-cells = <0x01>; - assigned-clocks = <0x3a 0x05 0x23 0x106 0x23 0x10b 0x3a 0x01 0x3a 0x2b 0x23 0x03 0x23 0x19b 0x23 0x09 0x23 0x19c 0x23 0x19d 0x23 0x1a1 0x23 0x19e 0x23 0x19f 0x23 0x1a0 0x23 0x04 0x23 0x10d 0x23 0x10e 0x23 0x173 0x23 0x174 0x23 0x175 0x23 0x176 0x23 0xc9 0x23 0xca 0x23 0x06 0x23 0x7e 0x23 0x7f 0x23 0x3d 0x23 0x41 0x23 0x45 0x23 0x49 0x23 0x4d 0x23 0x4d 0x23 0x55 0x23 0x51 0x23 0x5d 0x23 0xdd>; - assigned-clock-rates = <0x8000 0x11e1a300 0x11e1a300 0xbebc200 0x5f5e100 0x3b9aca00 0x1dcd6500 0x13d92d40 0xee6b280 0x7735940 0x5f5e100 0x3b9aca0 0x2faf080 0x17d7840 0x46cf7100 0x8f0d180 0x5f5e100 0x1dcd6500 0x17d78400 0x8f0d180 0x5f5e100 0x11e1a300 0x8f0d180 0x47868c00 0x17d78400 0x5f5e100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x1dcd6500>; - assigned-clock-parents = <0x3a 0x08 0x23 0x04 0x23 0x04>; - phandle = <0x23>; - }; - - i2c@fdd40000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfdd40000 0x00 0x1000>; - clocks = <0x3a 0x07 0x3a 0x2d>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x2e 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x3d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - tcs4525@1c { - compatible = "tcs,tcs452x"; - reg = <0x1c>; - vin-supply = <0x3e>; - regulator-compatible = "fan53555-reg"; - regulator-name = "vdd_cpu"; - regulator-min-microvolt = <0xadf34>; - regulator-max-microvolt = <0x1535b0>; - regulator-ramp-delay = <0x8fc>; - fcs,suspend-voltage-selector = <0x01>; - regulator-boot-on; - regulator-always-on; - phandle = <0x05>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - pmic@20 { - compatible = "rockchip,rk809"; - reg = <0x20>; - interrupt-parent = <0x3f>; - interrupts = <0x03 0x08>; - pinctrl-names = "default\0pmic-sleep\0pmic-power-off\0pmic-reset"; - pinctrl-0 = <0x40>; - pinctrl-1 = <0x41 0x42>; - pinctrl-2 = <0x43 0x44>; - pinctrl-3 = <0x43 0x45>; - rockchip,system-power-controller; - wakeup-source; - #clock-cells = <0x01>; - clock-output-names = "rk808-clkout1\0rk808-clkout2"; - pmic-reset-func = <0x00>; - not-save-power-en = <0x01>; - vcc1-supply = <0x46>; - vcc2-supply = <0x46>; - vcc3-supply = <0x46>; - vcc4-supply = <0x46>; - vcc5-supply = <0x46>; - vcc6-supply = <0x46>; - vcc7-supply = <0x46>; - vcc8-supply = <0x46>; - vcc9-supply = <0x46>; - phandle = <0x152>; - - pwrkey { - status = "okay"; - }; - - pinctrl_rk8xx { - gpio-controller; - #gpio-cells = <0x02>; - - rk817_slppin_null { - pins = "gpio_slp"; - function = "pin_fun0"; - }; - - rk817_slppin_slp { - pins = "gpio_slp"; - function = "pin_fun1"; - phandle = <0x42>; - }; - - rk817_slppin_pwrdn { - pins = "gpio_slp"; - function = "pin_fun2"; - phandle = <0x44>; - }; - - rk817_slppin_rst { - pins = "gpio_slp"; - function = "pin_fun3"; - phandle = <0x45>; - }; - }; - - regulators { - - DCDC_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_logic"; - phandle = <0x75>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_gpu"; - phandle = <0x77>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-initial-mode = <0x02>; - regulator-name = "vcc_ddr"; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - DCDC_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_npu"; - phandle = <0x71>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG1 { - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda0v9_image"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda_0v9"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda0v9_pmu"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0xdbba0>; - }; - }; - - LDO_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vccio_acodec"; - phandle = <0x30>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vccio_sd"; - phandle = <0x31>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG6 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vcc3v3_pmu"; - phandle = <0x2f>; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x325aa0>; - }; - }; - - LDO_REG7 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca_1v8"; - phandle = <0x129>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG8 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca1v8_pmu"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x1b7740>; - }; - }; - - LDO_REG9 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca1v8_image"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcc_1v8"; - phandle = <0x32>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - SWITCH_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vcc_3v3"; - phandle = <0x33>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - SWITCH_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vcc3v3_sd"; - phandle = <0xcf>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - - codec { - #sound-dai-cells = <0x00>; - compatible = "rockchip,rk809-codec\0rockchip,rk817-codec"; - clocks = <0x23 0x1a3>; - clock-names = "mclk"; - assigned-clocks = <0x23 0x1a3 0x23 0x1a6>; - assigned-clock-rates = <0xbb8000>; - assigned-clock-parents = <0x23 0x48 0x23 0x48>; - pinctrl-names = "default\0spk_gpio"; - pinctrl-0 = <0x47>; - pinctrl-1 = <0x48>; - hp-volume = <0x03>; - spk-volume = <0x03>; - mic-in-differential; - board-spk-from-hp; - capture-volume = <0x00>; - io-channels = <0x49 0x07>; - hp-det-adc-value = <0x3e8>; - status = "okay"; - hp-adc-drift-scope = <0x64>; - phandle = <0x14b>; - }; - - rtc { - status = "disabled"; - }; - }; - - fusb302@22 { - compatible = "fcs,fusb302"; - reg = <0x22>; - interrupt-parent = <0x3f>; - fcs,int_n = <0x3f 0x11 0x08>; - fusb340-switch-gpios = <0x4a 0x12 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4b>; - vbus-supply = <0x4c>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x4d>; - phandle = <0x2a>; - }; - }; - }; - - connector { - compatible = "usb-c-connector"; - label = "USB-C"; - data-role = "dual"; - power-role = "dual"; - try-power-role = "sink"; - op-sink-microwatt = <0xf4240>; - sink-pdos = <0x40190fa>; - source-pdos = <0x4019096>; - }; - }; - }; - - serial@fdd50000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfdd50000 0x00 0x100>; - interrupts = <0x00 0x74 0x04>; - clocks = <0x3a 0x0b 0x3a 0x2c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x00 0x4e 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x4f>; - status = "disabled"; - }; - - pwm@fdd70000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70000 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x50>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70010 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x51>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70020 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x52>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70030 0x00 0x10>; - interrupts = <0x00 0x52 0x04 0x00 0x56 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x53>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - power-management@fdd90000 { - compatible = "rockchip,rk3568-pmu\0syscon\0simple-mfd"; - reg = <0x00 0xfdd90000 0x00 0x1000>; - - power-controller { - compatible = "rockchip,rk3568-power-controller"; - #power-domain-cells = <0x01>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x25>; - - pd_npu@6 { - reg = <0x06>; - clocks = <0x23 0x27 0x23 0x25 0x23 0x26>; - pm_qos = <0x54>; - }; - - pd_gpu@7 { - reg = <0x07>; - clocks = <0x23 0x19 0x23 0x1a>; - pm_qos = <0x55>; - }; - - pd_vi@8 { - reg = <0x08>; - clocks = <0x23 0xcc 0x23 0xcd>; - pm_qos = <0x56 0x57 0x58>; - }; - - pd_vo@9 { - reg = <0x09>; - clocks = <0x23 0xda 0x23 0xdb 0x23 0xdc>; - pm_qos = <0x59 0x5a 0x5b>; - }; - - pd_rga@10 { - reg = <0x0a>; - clocks = <0x23 0xf1 0x23 0xf2>; - pm_qos = <0x5c 0x5d 0x5e 0x5f 0x60 0x61>; - }; - - pd_vpu@11 { - reg = <0x0b>; - clocks = <0x23 0xed>; - pm_qos = <0x62>; - }; - - pd_rkvdec@13 { - clocks = <0x23 0x107>; - reg = <0x0d>; - pm_qos = <0x63>; - }; - - pd_rkvenc@14 { - reg = <0x0e>; - clocks = <0x23 0x102>; - pm_qos = <0x64 0x65 0x66>; - }; - - pd_pipe@15 { - reg = <0x0f>; - clocks = <0x23 0x7f>; - pm_qos = <0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e>; - }; - }; - }; - - pvtm@fde00000 { - compatible = "rockchip,rk3568-core-pvtm"; - reg = <0x00 0xfde00000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@0 { - reg = <0x00>; - clocks = <0x23 0x13 0x23 0x1c2>; - clock-names = "clk\0pclk"; - resets = <0x23 0x1a 0x23 0x19>; - reset-names = "rts\0rst-p"; - thermal-zone = "soc-thermal"; - }; - }; - - npu@fde40000 { - compatible = "rockchip,rk3568-rknpu\0rockchip,rknpu"; - reg = <0x00 0xfde40000 0x00 0x10000>; - interrupts = <0x00 0x97 0x04>; - clocks = <0x02 0x02 0x23 0x23 0x23 0x28 0x23 0x29>; - clock-names = "scmi_clk\0clk\0aclk\0hclk"; - assigned-clocks = <0x23 0x23>; - assigned-clock-rates = <0x23c34600>; - resets = <0x23 0x2b 0x23 0x2c>; - reset-names = "srst_a\0srst_h"; - power-domains = <0x25 0x06>; - operating-points-v2 = <0x6f>; - iommus = <0x70>; - status = "okay"; - rknpu-supply = <0x71>; - }; - - npu-opp-table { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x72 0x07 0x08 0x73 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x3e8 0xc350>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x6f>; - - opp-200000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0xbebc200>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-300000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-400000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-700000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L0 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L1 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L2 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L3 = <0xcf850 0xcf850 0xf4240>; - }; - - opp-800000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L0 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L1 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L2 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L3 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-900000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xee098 0xee098 0xf4240>; - opp-microvolt-L0 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L1 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L2 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L3 = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-1000000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L0 = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L1 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0xf4240>; - status = "disabled"; - }; - - opp-j-600000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-m-900000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xe1d48 0xe1d48 0xf4240>; - }; - }; - - bus-npu { - compatible = "rockchip,rk3568-bus"; - rockchip,busfreq-policy = "clkfreq"; - clocks = <0x02 0x02>; - clock-names = "bus"; - operating-points-v2 = <0x74>; - status = "okay"; - bus-supply = <0x75>; - pvtm-supply = <0x05>; - }; - - bus-npu-opp-table { - compatible = "operating-points-v2"; - opp-shared; - nvmem-cells = <0x07>; - nvmem-cell-names = "pvtm"; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x16378 0x01 0x16379 0x186a0 0x02>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x74>; - - opp-700000000 { - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - opp-microvolt-L2 = <0xd59f8>; - }; - - opp-900000000 { - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xdbba0>; - }; - - opp-1000000000 { - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xe7ef0>; - opp-microvolt-L0 = <0xe7ef0>; - opp-microvolt-L1 = <0xe1d48>; - opp-microvolt-L2 = <0xdbba0>; - }; - }; - - iommu@fde4b000 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfde4b000 0x00 0x40>; - interrupts = <0x00 0x97 0x04>; - interrupt-names = "rknpu_mmu"; - clocks = <0x23 0x28 0x23 0x29>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x06>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x70>; - }; - - gpu@fde60000 { - compatible = "arm,mali-bifrost"; - reg = <0x00 0xfde60000 0x00 0x4000>; - interrupts = <0x00 0x27 0x04 0x00 0x29 0x04 0x00 0x28 0x04>; - interrupt-names = "GPU\0MMU\0JOB"; - upthreshold = <0x28>; - downdifferential = <0x0a>; - clocks = <0x02 0x01 0x23 0x1b>; - clock-names = "clk_mali\0clk_gpu"; - power-domains = <0x25 0x07>; - #cooling-cells = <0x02>; - operating-points-v2 = <0x76>; - status = "okay"; - mali-supply = <0x77>; - phandle = <0x21>; - - power-model { - compatible = "simple-power-model"; - leakage-range = <0x05 0x0f>; - ls = <0xffffa23e 0x5927 0x00>; - static-coefficient = <0x186a0>; - dynamic-coefficient = <0x3b9>; - ts = <0xfffe56a6 0xf87a 0xfffffab5 0x14>; - thermal-zone = "gpu-thermal"; - }; - }; - - opp-table2 { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x78 0x07 0x08 0x79 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x320 0xc350>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x76>; - - opp-200000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0xbebc200>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-300000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x11e1a300>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-400000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L1 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L2 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L3 = <0xcf850 0xcf850 0xf4240>; - }; - - opp-700000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L0 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L1 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L2 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L3 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-800000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L0 = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L1 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0xf4240>; - }; - - opp-j-600000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-m-800000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xe7ef0 0xe7ef0 0xf4240>; - }; - }; - - pvtm@fde80000 { - compatible = "rockchip,rk3568-gpu-pvtm"; - reg = <0x00 0xfde80000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@1 { - reg = <0x01>; - clocks = <0x23 0x1e 0x23 0x1d>; - clock-names = "clk\0pclk"; - resets = <0x23 0x24 0x23 0x23>; - reset-names = "rts\0rst-p"; - thermal-zone = "gpu-thermal"; - }; - }; - - pvtm@fde90000 { - compatible = "rockchip,rk3568-npu-pvtm"; - reg = <0x00 0xfde90000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@2 { - reg = <0x02>; - clocks = <0x23 0x2b 0x23 0x2a 0x23 0x25>; - clock-names = "clk\0pclk\0hclk"; - resets = <0x23 0x2e 0x23 0x2d>; - reset-names = "rts\0rst-p"; - thermal-zone = "soc-thermal"; - }; - }; - - vdpu@fdea0400 { - compatible = "rockchip,vpu-decoder-v2"; - reg = <0x00 0xfdea0400 0x00 0x400>; - interrupts = <0x00 0x8b 0x04>; - interrupt-names = "irq_dec"; - clocks = <0x23 0xee 0x23 0xef>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - resets = <0x23 0x11a 0x23 0x11b>; - reset-names = "video_a\0video_h"; - iommus = <0x7a>; - power-domains = <0x25 0x0b>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x00>; - rockchip,resetgroup-node = <0x00>; - status = "okay"; - }; - - iommu@fdea0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdea0800 0x00 0x40>; - interrupts = <0x00 0x8a 0x04>; - interrupt-names = "vdpu_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xee 0x23 0xef>; - power-domains = <0x25 0x0b>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7a>; - }; - - rk_rga@fdeb0000 { - compatible = "rockchip,rga2"; - reg = <0x00 0xfdeb0000 0x00 0x1000>; - interrupts = <0x00 0x5a 0x04>; - clocks = <0x23 0xf3 0x23 0xf4 0x23 0xf5>; - clock-names = "aclk_rga\0hclk_rga\0clk_rga"; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - ebc@fdec0000 { - compatible = "rockchip,rk3568-ebc-tcon"; - reg = <0x00 0xfdec0000 0x00 0x5000>; - interrupts = <0x00 0x11 0x04>; - clocks = <0x23 0xf9 0x23 0xfa>; - clock-names = "hclk\0dclk"; - power-domains = <0x25 0x0a>; - rockchip,grf = <0x3b>; - pinctrl-names = "default"; - pinctrl-0 = <0x7c>; - status = "disabled"; - }; - - jpegd@fded0000 { - compatible = "rockchip,rkv-jpeg-decoder-v1"; - reg = <0x00 0xfded0000 0x00 0x400>; - interrupts = <0x00 0x3e 0x04>; - clocks = <0x23 0xfb 0x23 0xfc>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,disable-auto-freq; - resets = <0x23 0x12c 0x23 0x12d>; - reset-names = "video_a\0video_h"; - iommus = <0x7d>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x01>; - rockchip,resetgroup-node = <0x01>; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - iommu@fded0480 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfded0480 0x00 0x40>; - interrupts = <0x00 0x3d 0x04>; - interrupt-names = "jpegd_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xfb 0x23 0xfc>; - power-domains = <0x25 0x0a>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7d>; - }; - - vepu@fdee0000 { - compatible = "rockchip,vpu-encoder-v2"; - reg = <0x00 0xfdee0000 0x00 0x400>; - interrupts = <0x00 0x40 0x04>; - clocks = <0x23 0xfd 0x23 0xfe>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,disable-auto-freq; - resets = <0x23 0x12e 0x23 0x12f>; - reset-names = "video_a\0video_h"; - iommus = <0x7e>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x02>; - rockchip,resetgroup-node = <0x02>; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - iommu@fdee0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdee0800 0x00 0x40>; - interrupts = <0x00 0x3f 0x04>; - interrupt-names = "vepu_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xfd 0x23 0xfe>; - power-domains = <0x25 0x0a>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7e>; - }; - - iep@fdef0000 { - compatible = "rockchip,iep-v2"; - reg = <0x00 0xfdef0000 0x00 0x500>; - interrupts = <0x00 0x38 0x04>; - clocks = <0x23 0xf6 0x23 0xf7 0x23 0xf8>; - clock-names = "aclk\0hclk\0sclk"; - resets = <0x23 0x127 0x23 0x128 0x23 0x129>; - reset-names = "rst_a\0rst_h\0rst_s"; - power-domains = <0x25 0x0a>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x05>; - rockchip,resetgroup-node = <0x05>; - iommus = <0x7f>; - status = "okay"; - }; - - iommu@fdef0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdef0800 0x00 0x100>; - interrupts = <0x00 0x38 0x04>; - interrupt-names = "iep_mmu"; - clocks = <0x23 0xf6 0x23 0xf7>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - power-domains = <0x25 0x0a>; - status = "okay"; - phandle = <0x7f>; - }; - - eink@fdf00000 { - compatible = "rockchip,rk3568-eink-tcon"; - reg = <0x00 0xfdf00000 0x00 0x74>; - interrupts = <0x00 0xb2 0x04>; - clocks = <0x23 0xff 0x23 0x100>; - clock-names = "pclk\0hclk"; - status = "disabled"; - }; - - rkvenc@fdf40000 { - compatible = "rockchip,rkv-encoder-v1"; - reg = <0x00 0xfdf40000 0x00 0x400>; - interrupts = <0x00 0x8c 0x04>; - interrupt-names = "irq_enc"; - clocks = <0x23 0x103 0x23 0x104 0x23 0x105>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core"; - rockchip,normal-rates = <0x11b3dc40 0x00 0x11b3dc40>; - resets = <0x23 0x133 0x23 0x134 0x23 0x135>; - reset-names = "video_a\0video_h\0video_core"; - assigned-clocks = <0x23 0x103 0x23 0x105>; - assigned-clock-rates = <0x11b3dc40 0x11b3dc40>; - iommus = <0x80>; - node-name = "rkvenc"; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x03>; - rockchip,resetgroup-node = <0x03>; - power-domains = <0x25 0x0e>; - operating-points-v2 = <0x81>; - status = "okay"; - venc-supply = <0x75>; - }; - - rkvenc-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x07>; - nvmem-cell-names = "pvtm"; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x16378 0x01 0x16379 0x186a0 0x02>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x81>; - - opp-297000000 { - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - opp-microvolt-L2 = <0xd59f8>; - }; - - opp-400000000 { - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xe7ef0>; - opp-microvolt-L0 = <0xe7ef0>; - opp-microvolt-L1 = <0xe1d48>; - opp-microvolt-L2 = <0xdbba0>; - }; - }; - - iommu@fdf40f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdf40f00 0x00 0x40 0x00 0xfdf40f40 0x00 0x40>; - interrupts = <0x00 0x8d 0x04 0x00 0x8e 0x04>; - interrupt-names = "rkvenc_mmu0\0rkvenc_mmu1"; - clocks = <0x23 0x103 0x23 0x104>; - clock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - #iommu-cells = <0x00>; - power-domains = <0x25 0x0e>; - status = "okay"; - phandle = <0x80>; - }; - - rkvdec@fdf80200 { - compatible = "rockchip,rkv-decoder-rk3568\0rockchip,rkv-decoder-v2"; - reg = <0x00 0xfdf80200 0x00 0x400 0x00 0xfdf80100 0x00 0x100>; - reg-names = "regs\0link"; - interrupts = <0x00 0x5b 0x04>; - interrupt-names = "irq_dec"; - clocks = <0x23 0x108 0x23 0x109 0x23 0x10a 0x23 0x10b 0x23 0x10c>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_cabac\0clk_core\0clk_hevc_cabac"; - rockchip,normal-rates = <0x11b3dc40 0x00 0x11b3dc40 0x11b3dc40 0x23c34600>; - rockchip,advanced-rates = <0x179a7b00 0x00 0x179a7b00 0x179a7b00 0x23c34600>; - rockchip,default-max-load = <0x1fe000>; - resets = <0x23 0x142 0x23 0x143 0x23 0x144 0x23 0x145 0x23 0x146>; - assigned-clocks = <0x23 0x108 0x23 0x10a 0x23 0x10b 0x23 0x10c>; - assigned-clock-rates = <0x11b3dc40 0x11b3dc40 0x11b3dc40 0x11b3dc40>; - reset-names = "video_a\0video_h\0video_cabac\0video_core\0video_hevc_cabac"; - power-domains = <0x25 0x0d>; - operating-points-v2 = <0x82>; - vdec-supply = <0x75>; - iommus = <0x83>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x04>; - rockchip,resetgroup-node = <0x04>; - rockchip,sram = <0x84>; - rockchip,rcb-iova = <0x10000000 0x10000>; - rockchip,rcb-min-width = <0x200>; - rockchip,task-capacity = <0x10>; - status = "okay"; - }; - - rkvdec-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x85 0x07>; - nvmem-cell-names = "leakage\0pvtm"; - rockchip,leakage-voltage-sel = <0x01 0x50 0x00 0x51 0xfe 0x01>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x186a0 0x01>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x82>; - - opp-297000000 { - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - }; - - opp-400000000 { - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xdbba0>; - }; - }; - - iommu@fdf80800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdf80800 0x00 0x40 0x00 0xfdf80840 0x00 0x40>; - interrupts = <0x00 0x5c 0x04>; - interrupt-names = "rkvdec_mmu"; - clocks = <0x23 0x108 0x23 0x109>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x0d>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x83>; - }; - - mipi-csi2-hw@fdfb0000 { - compatible = "rockchip,rk3568-mipi-csi2-hw"; - reg = <0x00 0xfdfb0000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x08 0x04 0x00 0x09 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x23 0xd5>; - clock-names = "pclk_csi2host"; - resets = <0x23 0xff>; - reset-names = "srst_csihost_p"; - status = "okay"; - phandle = <0x1e>; - }; - - rkcif@fdfe0000 { - compatible = "rockchip,rk3568-cif"; - reg = <0x00 0xfdfe0000 0x00 0x8000>; - reg-names = "cif_regs"; - interrupts = <0x00 0x92 0x04>; - interrupt-names = "cif-intr"; - clocks = <0x23 0xce 0x23 0xcf 0x23 0xd0 0x23 0xd1>; - clock-names = "aclk_cif\0hclk_cif\0dclk_cif\0iclk_cif_g"; - resets = <0x23 0xf7 0x23 0xf8 0x23 0xf9 0x23 0xfb 0x23 0xfa>; - reset-names = "rst_cif_a\0rst_cif_h\0rst_cif_d\0rst_cif_p\0rst_cif_i"; - assigned-clocks = <0x23 0xd0>; - assigned-clock-rates = <0x11e1a300>; - power-domains = <0x25 0x08>; - rockchip,grf = <0x3b>; - iommus = <0x86>; - status = "disabled"; - phandle = <0x87>; - }; - - iommu@fdfe0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdfe0800 0x00 0x100>; - interrupts = <0x00 0x92 0x04>; - interrupt-names = "cif_mmu"; - clocks = <0x23 0xce 0x23 0xcf>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x08>; - rockchip,disable-mmu-reset; - #iommu-cells = <0x00>; - status = "disabled"; - phandle = <0x86>; - }; - - rkcif_dvp { - compatible = "rockchip,rkcif-dvp"; - rockchip,hw = <0x87>; - status = "disabled"; - phandle = <0x88>; - }; - - rkcif_dvp_sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x88>; - status = "disabled"; - }; - - rkcif_mipi_lvds { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x87>; - status = "disabled"; - phandle = <0x89>; - }; - - rkcif_mipi_lvds_sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x89>; - status = "disabled"; - }; - - rkisp@fdff0000 { - compatible = "rockchip,rk3568-rkisp"; - reg = <0x00 0xfdff0000 0x00 0x10000>; - interrupts = <0x00 0x39 0x04 0x00 0x3a 0x04 0x00 0x3c 0x04>; - interrupt-names = "mipi_irq\0mi_irq\0isp_irq"; - clocks = <0x23 0xd2 0x23 0xd3 0x23 0xd4>; - clock-names = "aclk_isp\0hclk_isp\0clk_isp"; - resets = <0x23 0xfd 0x23 0xfc>; - reset-names = "isp\0isp-h"; - rockchip,grf = <0x3b>; - power-domains = <0x25 0x08>; - iommus = <0x8a>; - rockchip,iq-feature = <0x1bfb 0xfffe67ff>; - status = "okay"; - phandle = <0x8b>; - }; - - iommu@fdff1a00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdff1a00 0x00 0x100>; - interrupts = <0x00 0x3b 0x04>; - interrupt-names = "isp_mmu"; - clocks = <0x23 0xd2 0x23 0xd3>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x08>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "okay"; - phandle = <0x8a>; - }; - - rkisp-vir0 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x8b>; - status = "okay"; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x8c>; - phandle = <0x134>; - }; - }; - }; - - rkisp-vir1 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x8b>; - status = "disabled"; - }; - - uio@fe010000 { - compatible = "rockchip,uio-gmac"; - reg = <0x00 0xfe010000 0x00 0x10000>; - rockchip,ethernet = <0x8d>; - status = "disabled"; - }; - - ethernet@fe010000 { - local-mac-address = [5e 4f fd 70 05 c6]; - compatible = "rockchip,rk3568-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe010000 0x00 0x10000>; - interrupts = <0x00 0x20 0x04 0x00 0x1d 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0x3b>; - clocks = <0x23 0x186 0x23 0x189 0x23 0x189 0x23 0xc7 0x23 0xc3 0x23 0xc4 0x23 0x189 0x23 0xc8 0x23 0xac 0x23 0xab>; - clock-names = "stmmaceth\0mac_clk_rx\0mac_clk_tx\0clk_mac_refout\0aclk_mac\0pclk_mac\0clk_mac_speed\0ptp_ref\0pclk_xpcs\0clk_xpcs_eee"; - resets = <0x23 0xec>; - reset-names = "stmmaceth"; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0x8e>; - snps,mtl-rx-config = <0x8f>; - snps,mtl-tx-config = <0x90>; - status = "okay"; - phy-mode = "rgmii"; - clock_in_out = "input"; - snps,reset-gpio = <0x91 0x19 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - assigned-clocks = <0x23 0x189 0x23 0x186>; - assigned-clock-parents = <0x23 0x187 0x92>; - pinctrl-names = "default"; - pinctrl-0 = <0x93 0x94 0x95 0x96 0x97 0x98>; - tx_delay = <0x3e>; - rx_delay = <0x32>; - phy-handle = <0x99>; - phandle = <0x8d>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x00>; - led_status_value = <0x6940>; - phandle = <0x99>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0x8e>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x01>; - phandle = <0x8f>; - - queue0 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x01>; - phandle = <0x90>; - - queue0 { - }; - }; - }; - - vop@fe040000 { - compatible = "rockchip,rk3568-vop"; - reg = <0x00 0xfe040000 0x00 0x3000 0x00 0xfe044000 0x00 0x1000>; - reg-names = "regs\0gamma_lut"; - rockchip,grf = <0x3b>; - interrupts = <0x00 0x94 0x04>; - clocks = <0x23 0xdd 0x23 0xde 0x23 0xdf 0x23 0xe0 0x23 0xe1>; - clock-names = "aclk_vop\0hclk_vop\0dclk_vp0\0dclk_vp1\0dclk_vp2"; - iommus = <0x9a>; - power-domains = <0x25 0x09>; - status = "okay"; - assigned-clocks = <0x23 0xdf 0x23 0xe0>; - assigned-clock-parents = <0x3a 0x02 0x23 0x05>; - disable-win-move; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - phandle = <0x15>; - - port@0 { - rockchip,primary-plane = <0x04>; - rockchip,plane-mask = <0x15>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x9b>; - phandle = <0x17>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x9c>; - phandle = <0x18>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x9d>; - phandle = <0x19>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0x9e>; - phandle = <0x1a>; - }; - }; - - port@1 { - rockchip,primary-plane = <0x05>; - rockchip,plane-mask = <0x22>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x01>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x9f>; - phandle = <0xa8>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa0>; - phandle = <0xa9>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xa1>; - phandle = <0xaf>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0xa2>; - phandle = <0xad>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0xa3>; - phandle = <0x1b>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0xa4>; - phandle = <0x37>; - }; - }; - - port@2 { - rockchip,primary-plane = <0x03>; - rockchip,plane-mask = <0x08>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x02>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xa5>; - phandle = <0x35>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa6>; - phandle = <0x1c>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xa7>; - phandle = <0x38>; - }; - }; - }; - }; - - iommu@fe043e00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfe043e00 0x00 0x100 0x00 0xfe043f00 0x00 0x100>; - interrupts = <0x00 0x94 0x04>; - interrupt-names = "vop_mmu"; - clocks = <0x23 0xdd 0x23 0xde>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - rockchip,disable-device-link-resume; - status = "okay"; - phandle = <0x9a>; - }; - - dsi@fe060000 { - compatible = "rockchip,rk3568-mipi-dsi"; - reg = <0x00 0xfe060000 0x00 0x10000>; - interrupts = <0x00 0x44 0x04>; - clocks = <0x23 0xe8 0x23 0xda>; - clock-names = "pclk\0hclk"; - resets = <0x23 0x110>; - reset-names = "apb"; - phys = <0x34>; - phy-names = "dphy"; - power-domains = <0x25 0x09>; - rockchip,grf = <0x3b>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x17>; - status = "disabled"; - phandle = <0x9b>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa8>; - status = "disabled"; - phandle = <0x9f>; - }; - }; - }; - }; - - dsi@fe070000 { - compatible = "rockchip,rk3568-mipi-dsi"; - reg = <0x00 0xfe070000 0x00 0x10000>; - interrupts = <0x00 0x45 0x04>; - clocks = <0x23 0xe9 0x23 0xda>; - clock-names = "pclk\0hclk"; - resets = <0x23 0x111>; - reset-names = "apb"; - phys = <0x36>; - phy-names = "dphy"; - power-domains = <0x25 0x09>; - rockchip,grf = <0x3b>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x18>; - status = "disabled"; - phandle = <0x9c>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa9>; - status = "disabled"; - phandle = <0xa0>; - }; - }; - }; - }; - - hdmi@fe0a0000 { - compatible = "rockchip,rk3568-dw-hdmi"; - reg = <0x00 0xfe0a0000 0x00 0x20000>; - interrupts = <0x00 0x2d 0x04>; - clocks = <0x23 0xe6 0x23 0xe7 0x23 0x193 0x3a 0x02 0x23 0xde>; - clock-names = "iahb\0isfr\0cec\0ref\0hclk"; - power-domains = <0x25 0x09>; - reg-io-width = <0x04>; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xaa 0xab 0xac>; - status = "okay"; - phandle = <0x147>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x1a>; - status = "okay"; - phandle = <0x9e>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xad>; - status = "disabled"; - phandle = <0xa2>; - }; - }; - }; - }; - - edp@fe0c0000 { - compatible = "rockchip,rk3568-edp"; - reg = <0x00 0xfe0c0000 0x00 0x10000>; - interrupts = <0x00 0x12 0x04>; - clocks = <0x3a 0x29 0x23 0xea 0x23 0xeb 0x23 0xda>; - clock-names = "dp\0pclk\0spdif\0hclk"; - resets = <0x23 0x113 0x23 0x112>; - reset-names = "dp\0apb"; - phys = <0xae>; - phy-names = "dp"; - power-domains = <0x25 0x09>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x19>; - status = "disabled"; - phandle = <0x9d>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xaf>; - status = "disabled"; - phandle = <0xa1>; - }; - }; - }; - }; - - nocp-cpu@fe102000 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102000 0x00 0x400>; - phandle = <0xb5>; - }; - - nocp-gpu-vpu-rga-venc@fe102400 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102400 0x00 0x400>; - }; - - nocp-vdec@fe102800 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102800 0x00 0x400>; - }; - - nocp-vi-usb-peri-pipe@fe102c00 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102c00 0x00 0x400>; - }; - - nocp-vo@fe103000 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe103000 0x00 0x400>; - }; - - qos@fe128000 { - compatible = "syscon"; - reg = <0x00 0xfe128000 0x00 0x20>; - phandle = <0x55>; - }; - - qos@fe138080 { - compatible = "syscon"; - reg = <0x00 0xfe138080 0x00 0x20>; - phandle = <0x64>; - }; - - qos@fe138100 { - compatible = "syscon"; - reg = <0x00 0xfe138100 0x00 0x20>; - phandle = <0x65>; - }; - - qos@fe138180 { - compatible = "syscon"; - reg = <0x00 0xfe138180 0x00 0x20>; - phandle = <0x66>; - }; - - qos@fe148000 { - compatible = "syscon"; - reg = <0x00 0xfe148000 0x00 0x20>; - phandle = <0x56>; - }; - - qos@fe148080 { - compatible = "syscon"; - reg = <0x00 0xfe148080 0x00 0x20>; - phandle = <0x57>; - }; - - qos@fe148100 { - compatible = "syscon"; - reg = <0x00 0xfe148100 0x00 0x20>; - phandle = <0x58>; - }; - - qos@fe150000 { - compatible = "syscon"; - reg = <0x00 0xfe150000 0x00 0x20>; - phandle = <0x62>; - }; - - qos@fe158000 { - compatible = "syscon"; - reg = <0x00 0xfe158000 0x00 0x20>; - phandle = <0x5c>; - }; - - qos@fe158100 { - compatible = "syscon"; - reg = <0x00 0xfe158100 0x00 0x20>; - phandle = <0x5d>; - }; - - qos@fe158180 { - compatible = "syscon"; - reg = <0x00 0xfe158180 0x00 0x20>; - phandle = <0x5e>; - }; - - qos@fe158200 { - compatible = "syscon"; - reg = <0x00 0xfe158200 0x00 0x20>; - phandle = <0x5f>; - }; - - qos@fe158280 { - compatible = "syscon"; - reg = <0x00 0xfe158280 0x00 0x20>; - phandle = <0x60>; - }; - - qos@fe158300 { - compatible = "syscon"; - reg = <0x00 0xfe158300 0x00 0x20>; - phandle = <0x61>; - }; - - qos@fe180000 { - compatible = "syscon"; - reg = <0x00 0xfe180000 0x00 0x20>; - phandle = <0x54>; - }; - - qos@fe190000 { - compatible = "syscon"; - reg = <0x00 0xfe190000 0x00 0x20>; - phandle = <0x67>; - }; - - qos@fe190080 { - compatible = "syscon"; - reg = <0x00 0xfe190080 0x00 0x20>; - phandle = <0x68>; - }; - - qos@fe190100 { - compatible = "syscon"; - reg = <0x00 0xfe190100 0x00 0x20>; - phandle = <0x69>; - }; - - qos@fe190200 { - compatible = "syscon"; - reg = <0x00 0xfe190200 0x00 0x20>; - phandle = <0x6a>; - }; - - qos@fe190280 { - compatible = "syscon"; - reg = <0x00 0xfe190280 0x00 0x20>; - phandle = <0x6b>; - }; - - qos@fe190300 { - compatible = "syscon"; - reg = <0x00 0xfe190300 0x00 0x20>; - phandle = <0x6c>; - }; - - qos@fe190380 { - compatible = "syscon"; - reg = <0x00 0xfe190380 0x00 0x20>; - phandle = <0x6d>; - }; - - qos@fe190400 { - compatible = "syscon"; - reg = <0x00 0xfe190400 0x00 0x20>; - phandle = <0x6e>; - }; - - qos@fe198000 { - compatible = "syscon"; - reg = <0x00 0xfe198000 0x00 0x20>; - phandle = <0x63>; - }; - - qos@fe1a8000 { - compatible = "syscon"; - reg = <0x00 0xfe1a8000 0x00 0x20>; - phandle = <0x59>; - }; - - qos@fe1a8080 { - compatible = "syscon"; - reg = <0x00 0xfe1a8080 0x00 0x20>; - phandle = <0x5a>; - }; - - qos@fe1a8100 { - compatible = "syscon"; - reg = <0x00 0xfe1a8100 0x00 0x20>; - phandle = <0x5b>; - }; - - dwmmc@fe000000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe000000 0x00 0x4000>; - interrupts = <0x00 0x64 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xc1 0x23 0xc2 0x23 0x18e 0x23 0x18f>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xeb>; - reset-names = "reset"; - status = "okay"; - no-sd; - no-mmc; - bus-width = <0x04>; - disable-wp; - cap-sd-highspeed; - cap-sdio-irq; - keep-power-in-suspend; - pinctrl-names = "default"; - pinctrl-0 = <0xb0 0xb1 0xb2>; - sd-uhs-sdr104; - mmc-pwrseq = <0xb3>; - non-removable; - }; - - dfi@fe230000 { - reg = <0x00 0xfe230000 0x00 0x400>; - compatible = "rockchip,rk3568-dfi"; - rockchip,pmugrf = <0x3c>; - status = "okay"; - phandle = <0xb4>; - }; - - dmc { - compatible = "rockchip,rk3568-dmc"; - interrupts = <0x00 0x0a 0x04>; - interrupt-names = "complete"; - devfreq-events = <0xb4 0xb5>; - clocks = <0x02 0x03>; - clock-names = "dmc_clk"; - operating-points-v2 = <0xb6>; - vop-bw-dmc-freq = <0x00 0x11e 0x4f1a0 0x11f 0x1869f 0x80e80>; - vop-frame-bw-dmc-freq = <0x00 0x26c 0x4f1a0 0x26d 0x1869f 0xbe6e0>; - cpu-bw-dmc-freq = <0x00 0x15e 0x4f1a0 0x15f 0x190 0x80e80 0x191 0x1869f 0xbe6e0>; - upthreshold = <0x28>; - downdifferential = <0x14>; - system-status-level = <0x01 0x04 0x08 0x08 0x02 0x01 0x10 0x04 0x10000 0x04 0x1000 0x08 0x4000 0x08 0x2000 0x08 0xc00 0x08>; - auto-min-freq = <0x4f1a0>; - auto-freq-en = <0x01>; - #cooling-cells = <0x02>; - status = "okay"; - center-supply = <0x75>; - phandle = <0x16>; - }; - - dmc-fsp { - compatible = "rockchip,rk3568-dmc-fsp"; - debug_print_level = <0x00>; - ddr3_params = <0xb7>; - ddr4_params = <0xb8>; - lpddr3_params = <0xb9>; - lpddr4_params = <0xba>; - lpddr4x_params = <0xbb>; - status = "okay"; - }; - - dmc-opp-table { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x85 0x07 0x08 0xbc 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x618 0x124f8>; - rockchip,leakage-voltage-sel = <0x01 0x50 0x00 0x51 0xfe 0x01>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x186a0 0x01>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0xb6>; - - opp-1560000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x5cfbb600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L1 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-j-m-1560000000 { - opp-supported-hw = <0x06 0xffff>; - opp-hz = <0x00 0x5cfbb600>; - opp-microvolt = <0xd59f8 0xd59f8 0xf4240>; - }; - }; - - pcie@fe260000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x00 0x0f>; - clocks = <0x23 0x81 0x23 0x82 0x23 0x83 0x23 0x84 0x23 0x85>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0x4b 0x04 0x00 0x4a 0x04 0x00 0x49 0x04 0x00 0x48 0x04 0x00 0x47 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xbd 0x00 0x00 0x00 0x00 0x02 0xbd 0x01 0x00 0x00 0x00 0x03 0xbd 0x02 0x00 0x00 0x00 0x04 0xbd 0x03>; - linux,pci-domain = <0x00>; - num-ib-windows = <0x06>; - num-viewport = <0x08>; - num-ob-windows = <0x02>; - max-link-speed = <0x02>; - msi-map = <0x00 0xbe 0x00 0x1000>; - num-lanes = <0x01>; - phys = <0x27 0x02>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf4000000 0x00 0xf4000000 0x00 0x100000 0x81000000 0x00 0xf4100000 0x00 0xf4100000 0x00 0x100000 0x82000000 0x00 0xf4200000 0x00 0xf4200000 0x00 0x1e00000 0xc3000000 0x03 0x00 0x03 0x00 0x00 0x40000000>; - reg = <0x03 0xc0000000 0x00 0x400000 0x00 0xfe260000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xa1>; - reset-names = "pipe"; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x48 0x01>; - phandle = <0xbd>; - }; - }; - - pcie@fe270000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x10 0x1f>; - clocks = <0x23 0x88 0x23 0x89 0x23 0x8a 0x23 0x8b 0x23 0x8c>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0xa0 0x04 0x00 0x9f 0x04 0x00 0x9e 0x04 0x00 0x9d 0x04 0x00 0x9c 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xbf 0x00 0x00 0x00 0x00 0x02 0xbf 0x01 0x00 0x00 0x00 0x03 0xbf 0x02 0x00 0x00 0x00 0x04 0xbf 0x03>; - linux,pci-domain = <0x01>; - num-ib-windows = <0x06>; - num-ob-windows = <0x02>; - num-viewport = <0x08>; - max-link-speed = <0x03>; - msi-map = <0x1000 0xbe 0x1000 0x1000>; - num-lanes = <0x01>; - phys = <0xc0>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf2000000 0x00 0xf2000000 0x00 0x100000 0x81000000 0x00 0xf2100000 0x00 0xf2100000 0x00 0x100000 0x82000000 0x00 0xf2200000 0x00 0xf2200000 0x00 0x1e00000 0xc3000000 0x03 0x40000000 0x03 0x40000000 0x00 0x40000000>; - reg = <0x03 0xc0400000 0x00 0x400000 0x00 0xfe270000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xb1>; - reset-names = "pipe"; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9d 0x01>; - phandle = <0xbf>; - }; - }; - - pcie@fe280000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x20 0x2f>; - clocks = <0x23 0x8f 0x23 0x90 0x23 0x91 0x23 0x92 0x23 0x93>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0xa5 0x04 0x00 0xa4 0x04 0x00 0xa3 0x04 0x00 0xa2 0x04 0x00 0xa1 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xc1 0x00 0x00 0x00 0x00 0x02 0xc1 0x01 0x00 0x00 0x00 0x03 0xc1 0x02 0x00 0x00 0x00 0x04 0xc1 0x03>; - linux,pci-domain = <0x02>; - num-ib-windows = <0x06>; - num-viewport = <0x08>; - num-ob-windows = <0x02>; - max-link-speed = <0x03>; - msi-map = <0x2000 0xbe 0x2000 0x1000>; - num-lanes = <0x02>; - phys = <0xc0>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf0000000 0x00 0xf0000000 0x00 0x100000 0x81000000 0x00 0xf0100000 0x00 0xf0100000 0x00 0x100000 0x82000000 0x00 0xf0200000 0x00 0xf0200000 0x00 0x1e00000 0xc3000000 0x03 0x80000000 0x03 0x80000000 0x00 0x40000000>; - reg = <0x03 0xc0800000 0x00 0x400000 0x00 0xfe280000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xc1>; - reset-names = "pipe"; - status = "okay"; - reset-gpios = <0x91 0x1e 0x00>; - vpcie3v3-supply = <0xc2>; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xa2 0x01>; - phandle = <0xc1>; - }; - }; - - uio@fe2a0000 { - compatible = "rockchip,uio-gmac"; - reg = <0x00 0xfe2a0000 0x00 0x10000>; - rockchip,ethernet = <0xc3>; - status = "disabled"; - }; - - ethernet@fe2a0000 { - local-mac-address = [5a 4f fd 70 05 c6]; - compatible = "rockchip,rk3568-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe2a0000 0x00 0x10000>; - interrupts = <0x00 0x1b 0x04 0x00 0x18 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0x3b>; - clocks = <0x23 0x182 0x23 0x185 0x23 0x185 0x23 0xb8 0x23 0xb4 0x23 0xb5 0x23 0x185 0x23 0xb9 0x23 0xac 0x23 0xab>; - clock-names = "stmmaceth\0mac_clk_rx\0mac_clk_tx\0clk_mac_refout\0aclk_mac\0pclk_mac\0clk_mac_speed\0ptp_ref\0pclk_xpcs\0clk_xpcs_eee"; - resets = <0x23 0xd7>; - reset-names = "stmmaceth"; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0xc4>; - snps,mtl-rx-config = <0xc5>; - snps,mtl-tx-config = <0xc6>; - status = "okay"; - phy-mode = "rgmii"; - clock_in_out = "input"; - snps,reset-gpio = <0x91 0x1b 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - assigned-clocks = <0x23 0x185 0x23 0x182>; - assigned-clock-parents = <0x23 0x183 0xc7>; - pinctrl-names = "default"; - pinctrl-0 = <0xc8 0xc9 0xca 0xcb 0xcc 0xcd>; - tx_delay = <0x4a>; - rx_delay = <0x2e>; - phy-handle = <0xce>; - phandle = <0xc3>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x00>; - led_status_value = <0x6940>; - phandle = <0xce>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0xc4>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x01>; - phandle = <0xc5>; - - queue0 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x01>; - phandle = <0xc6>; - - queue0 { - }; - }; - }; - - dwmmc@fe2b0000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2b0000 0x00 0x4000>; - interrupts = <0x00 0x62 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xb0 0x23 0xb1 0x23 0x18a 0x23 0x18b>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xd4>; - reset-names = "reset"; - status = "okay"; - no-sdio; - no-mmc; - bus-width = <0x04>; - cap-mmc-highspeed; - cap-sd-highspeed; - disable-wp; - sd-uhs-sdr104; - vmmc-supply = <0xcf>; - vqmmc-supply = <0x31>; - pinctrl-names = "default"; - pinctrl-0 = <0xd0 0xd1 0xd2 0xd3>; - }; - - dwmmc@fe2c0000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2c0000 0x00 0x4000>; - interrupts = <0x00 0x63 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xb2 0x23 0xb3 0x23 0x18c 0x23 0x18d>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xd6>; - reset-names = "reset"; - status = "disabled"; - }; - - spi@fe300000 { - compatible = "rockchip,sfc"; - reg = <0x00 0xfe300000 0x00 0x4000>; - interrupts = <0x00 0x65 0x04>; - clocks = <0x23 0x78 0x23 0x76>; - clock-names = "clk_sfc\0hclk_sfc"; - assigned-clocks = <0x23 0x78>; - assigned-clock-rates = <0x2faf080>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <0xd4>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x00>; - spi-max-frequency = <0x2faf080>; - spi-rx-bus-width = <0x01>; - spi-tx-bus-width = <0x01>; - }; - }; - - sdhci@fe310000 { - compatible = "rockchip,rk3568-dwcmshc\0rockchip,dwcmshc-sdhci"; - reg = <0x00 0xfe310000 0x00 0x10000>; - interrupts = <0x00 0x13 0x04>; - assigned-clocks = <0x23 0x7b 0x23 0x7d 0x23 0x7c>; - assigned-clock-rates = <0xbebc200 0x16e3600 0xbebc200>; - clocks = <0x23 0x7c 0x23 0x7a 0x23 0x79 0x23 0x7b 0x23 0x7d>; - clock-names = "core\0bus\0axi\0block\0timer"; - resets = <0x23 0x78 0x23 0x76 0x23 0x75 0x23 0x77 0x23 0x79>; - reset-names = "core\0bus\0axi\0block\0timer"; - status = "okay"; - bus-width = <0x08>; - no-sdio; - no-sd; - non-removable; - max-frequency = <0xbebc200>; - full-pwr-cycle-in-suspend; - }; - - nandc@fe330000 { - compatible = "rockchip,rk-nandc-v9"; - reg = <0x00 0xfe330000 0x00 0x4000>; - interrupts = <0x00 0x46 0x04>; - nandc_id = <0x00>; - clocks = <0x23 0x75 0x23 0x74>; - clock-names = "clk_nandc\0hclk_nandc"; - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - nand@0 { - reg = <0x00>; - nand-bus-width = <0x08>; - nand-ecc-mode = "hw"; - nand-ecc-strength = <0x10>; - nand-ecc-step-size = <0x400>; - }; - }; - - crypto@fe380000 { - compatible = "rockchip,rk3568-crypto"; - reg = <0x00 0xfe380000 0x00 0x4000>; - interrupts = <0x00 0x04 0x04>; - clocks = <0x23 0x6a 0x23 0x6b 0x23 0x6c 0x23 0x6d>; - clock-names = "aclk\0hclk\0sclk\0apb_pclk"; - assigned-clocks = <0x23 0x6c>; - assigned-clock-rates = <0xbebc200>; - resets = <0x23 0x69>; - reset-names = "crypto-rst"; - status = "disabled"; - }; - - rng@fe388000 { - compatible = "rockchip,cryptov2-rng"; - reg = <0x00 0xfe388000 0x00 0x2000>; - clocks = <0x23 0x70 0x23 0x6f>; - clock-names = "clk_trng\0hclk_trng"; - resets = <0x23 0x6d>; - reset-names = "reset"; - status = "okay"; - }; - - otp@fe38c000 { - compatible = "rockchip,rk3568-otp"; - reg = <0x00 0xfe38c000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - clocks = <0x23 0x73 0x23 0x72 0x23 0x71 0x23 0x181>; - clock-names = "usr\0sbpi\0apb\0phy"; - resets = <0x23 0x1cf>; - reset-names = "otp_phy"; - - cpu-code@2 { - reg = <0x02 0x02>; - phandle = <0x12>; - }; - - specification-serial-number@7 { - reg = <0x07 0x01>; - bits = <0x00 0x05>; - phandle = <0x0a>; - }; - - cpu-version@8 { - reg = <0x08 0x01>; - bits = <0x03 0x03>; - phandle = <0x11>; - }; - - mbist-vmin@9 { - reg = <0x09 0x01>; - bits = <0x00 0x04>; - phandle = <0x08>; - }; - - id@a { - reg = <0x0a 0x10>; - phandle = <0x10>; - }; - - cpu-leakage@1a { - reg = <0x1a 0x01>; - phandle = <0x06>; - }; - - log-leakage@1b { - reg = <0x1b 0x01>; - phandle = <0x85>; - }; - - npu-leakage@1c { - reg = <0x1c 0x01>; - phandle = <0x72>; - }; - - gpu-leakage@1d { - reg = <0x1d 0x01>; - phandle = <0x78>; - }; - - core-pvtm@2a { - reg = <0x2a 0x02>; - phandle = <0x07>; - }; - - cpu-tsadc-trim-l@2e { - reg = <0x2e 0x01>; - phandle = <0x125>; - }; - - cpu-tsadc-trim-h@2f { - reg = <0x2f 0x01>; - bits = <0x00 0x04>; - phandle = <0x126>; - }; - - npu-tsadc-trim-l@30 { - reg = <0x30 0x01>; - phandle = <0x127>; - }; - - npu-tsadc-trim-h@31 { - reg = <0x31 0x01>; - bits = <0x00 0x04>; - phandle = <0x128>; - }; - - tsadc-trim-base-frac@31 { - reg = <0x31 0x01>; - bits = <0x04 0x04>; - phandle = <0x122>; - }; - - tsadc-trim-base@32 { - reg = <0x32 0x01>; - phandle = <0x121>; - }; - - cpu-opp-info@36 { - reg = <0x36 0x06>; - phandle = <0x09>; - }; - - gpu-opp-info@3c { - reg = <0x3c 0x06>; - phandle = <0x79>; - }; - - npu-opp-info@42 { - reg = <0x42 0x06>; - phandle = <0x73>; - }; - - dmc-opp-info@48 { - reg = <0x48 0x06>; - phandle = <0xbc>; - }; - - remark-spec-serial-number@56 { - reg = <0x56 0x01>; - bits = <0x00 0x05>; - phandle = <0x0b>; - }; - }; - - i2s@fe400000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe400000 0x00 0x1000>; - interrupts = <0x00 0x34 0x04>; - clocks = <0x23 0x3f 0x23 0x43 0x23 0x39>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x00>; - dma-names = "tx"; - resets = <0x23 0x50 0x23 0x51>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x146>; - }; - - i2s@fe410000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe410000 0x00 0x1000>; - interrupts = <0x00 0x35 0x04>; - clocks = <0x23 0x47 0x23 0x4b 0x23 0x3a>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x02 0xd5 0x03>; - dma-names = "tx\0rx"; - resets = <0x23 0x52 0x23 0x53>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xd6 0xd7 0xd8 0xd9>; - status = "okay"; - rockchip,clk-trcm = <0x01>; - phandle = <0xe8>; - }; - - i2s@fe420000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe420000 0x00 0x1000>; - interrupts = <0x00 0x36 0x04>; - clocks = <0x23 0x4f 0x23 0x4f 0x23 0x3b>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x04 0xd5 0x05>; - dma-names = "tx\0rx"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,clk-trcm = <0x01>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xda 0xdb 0xdc 0xdd>; - status = "disabled"; - }; - - i2s@fe430000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe430000 0x00 0x1000>; - interrupts = <0x00 0x37 0x04>; - clocks = <0x23 0x53 0x23 0x57 0x23 0x3c>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x06 0xd5 0x07>; - dma-names = "tx\0rx"; - resets = <0x23 0x55 0x23 0x56>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,clk-trcm = <0x01>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xde 0xdf 0xe0 0xe1>; - status = "disabled"; - phandle = <0x144>; - }; - - pdm@fe440000 { - compatible = "rockchip,rk3568-pdm\0rockchip,pdm"; - reg = <0x00 0xfe440000 0x00 0x1000>; - clocks = <0x23 0x5a 0x23 0x59>; - clock-names = "pdm_clk\0pdm_hclk"; - dmas = <0xd5 0x09>; - dma-names = "rx"; - pinctrl-names = "default"; - pinctrl-0 = <0xe2 0xe3 0xe4 0xe5 0xe6 0xe7>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x149>; - }; - - vad@fe450000 { - compatible = "rockchip,rk3568-vad"; - reg = <0x00 0xfe450000 0x00 0x10000>; - reg-names = "vad"; - clocks = <0x23 0x5b>; - clock-names = "hclk"; - interrupts = <0x00 0x89 0x04>; - rockchip,audio-src = <0xe8>; - rockchip,det-channel = <0x00>; - rockchip,mode = <0x00>; - #sound-dai-cells = <0x00>; - status = "disabled"; - rockchip,buffer-time-ms = <0x80>; - phandle = <0x14e>; - }; - - spdif@fe460000 { - compatible = "rockchip,rk3568-spdif"; - reg = <0x00 0xfe460000 0x00 0x1000>; - interrupts = <0x00 0x66 0x04>; - dmas = <0xd5 0x01>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x23 0x5f 0x23 0x5c>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xe9>; - status = "disabled"; - phandle = <0x14c>; - }; - - audpwm@fe470000 { - compatible = "rockchip,rk3568-audio-pwm\0rockchip,audio-pwm-v1"; - reg = <0x00 0xfe470000 0x00 0x1000>; - clocks = <0x23 0x63 0x23 0x60>; - clock-names = "clk\0hclk"; - dmas = <0xd5 0x08>; - dma-names = "tx"; - #sound-dai-cells = <0x00>; - rockchip,sample-width-bits = <0x0b>; - rockchip,interpolat-points = <0x01>; - status = "disabled"; - }; - - codec-digital@fe478000 { - compatible = "rockchip,rk3568-codec-digital\0rockchip,codec-digital-v1"; - reg = <0x00 0xfe478000 0x00 0x1000>; - clocks = <0x23 0x67 0x23 0x66 0x23 0x65 0x23 0x64>; - clock-names = "adc\0dac\0i2c\0pclk"; - pinctrl-names = "default"; - pinctrl-0 = <0xea>; - resets = <0x23 0x5f>; - reset-names = "reset"; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x145>; - }; - - dmac@fe530000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfe530000 0x00 0x4000>; - interrupts = <0x00 0x0e 0x04 0x00 0x0d 0x04>; - clocks = <0x23 0x10d>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0x4e>; - }; - - dmac@fe550000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfe550000 0x00 0x4000>; - interrupts = <0x00 0x10 0x04 0x00 0x0f 0x04>; - clocks = <0x23 0x10d>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0xd5>; - }; - - rkscr@fe560000 { - compatible = "rockchip-scr"; - reg = <0x00 0xfe560000 0x00 0x10000>; - interrupts = <0x00 0x61 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xeb>; - clocks = <0x23 0x114>; - clock-names = "g_pclk_sim_card"; - status = "disabled"; - }; - - can@fe570000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe570000 0x00 0x1000>; - interrupts = <0x00 0x01 0x04>; - clocks = <0x23 0x141 0x23 0x140>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x155 0x23 0x154>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - }; - - can@fe580000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe580000 0x00 0x1000>; - interrupts = <0x00 0x02 0x04>; - clocks = <0x23 0x143 0x23 0x142>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x157 0x23 0x156>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "okay"; - assigned-clocks = <0x23 0x143>; - assigned-clock-rates = <0xbebc200>; - pinctrl-names = "default"; - pinctrl-0 = <0xec>; - }; - - can@fe590000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe590000 0x00 0x1000>; - interrupts = <0x00 0x03 0x04>; - clocks = <0x23 0x145 0x23 0x144>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x159 0x23 0x158>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - assigned-clocks = <0x23 0x145>; - assigned-clock-rates = <0xbebc200>; - pinctrl-names = "default"; - pinctrl-0 = <0xed>; - }; - - i2c@fe5a0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5a0000 0x00 0x1000>; - clocks = <0x23 0x148 0x23 0x147>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x2f 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xee>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - clock-frequency = <0x186a0>; - - gpio@21 { - status = "disabled"; - compatible = "nxp,pca9555"; - reg = <0x21>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-group-num = <0xc8>; - phandle = <0x102>; - }; - - gt1x@14 { - status = "disabled"; - compatible = "goodix,gt1x"; - reg = <0x14>; - pinctrl-names = "default"; - pinctrl-0 = <0xef>; - goodix,rst-gpio = <0x3f 0x0e 0x00>; - goodix,irq-gpio = <0x3f 0x0d 0x08>; - }; - }; - - i2c@fe5b0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5b0000 0x00 0x1000>; - clocks = <0x23 0x14a 0x23 0x149>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x30 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf0>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@fe5c0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5c0000 0x00 0x1000>; - clocks = <0x23 0x14c 0x23 0x14b>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x31 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf1>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@fe5d0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5d0000 0x00 0x1000>; - clocks = <0x23 0x14e 0x23 0x14d>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x32 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf2>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - clock-frequency = <0x61a80>; - - gc8034@37 { - compatible = "galaxycore,gc8034"; - status = "disabled"; - reg = <0x37>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x01>; - pwdn-gpios = <0xf4 0x0c 0x01>; - rockchip,grf = <0x3b>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "RK-CMK-8M-2-v1"; - rockchip,camera-module-lens-name = "CK8401"; - - port { - - endpoint { - remote-endpoint = <0xf5>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x130>; - }; - }; - }; - - os04a10@36 { - status = "disabled"; - compatible = "ovti,os04a10"; - reg = <0x36>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x01>; - pwdn-gpios = <0xf4 0x0c 0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT1607-FV1"; - rockchip,camera-module-lens-name = "M12-40IRC-4MP-F16"; - - port { - - endpoint { - remote-endpoint = <0xf6>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x12f>; - }; - }; - }; - - ov5695@36 { - status = "disabled"; - compatible = "ovti,ov5695"; - reg = <0x36>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x00>; - pwdn-gpios = <0xf4 0x0c 0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "TongJu"; - rockchip,camera-module-lens-name = "CHT842-MD"; - - port { - - endpoint { - remote-endpoint = <0xf7>; - data-lanes = <0x01 0x02>; - phandle = <0x131>; - }; - }; - }; - - XC7160b@1b { - status = "okay"; - compatible = "firefly,xc7160"; - reg = <0x1b>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x3f 0x1d 0x00>; - pwdn-gpios = <0xf4 0x0c 0x00>; - firefly,clkout-enabled-index = <0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "NC"; - rockchip,camera-module-lens-name = "NC"; - - port { - - endpoint { - remote-endpoint = <0xf8>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x132>; - }; - }; - }; - - imx415@37 { - status = "okay"; - compatible = "sony,imx415"; - reg = <0x37>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x3f 0x1d 0x01>; - pwdn-gpios = <0xf4 0x0c 0x00>; - firefly,clkout-enabled-index = <0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT2022-PX1"; - rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; - - port { - - endpoint { - remote-endpoint = <0xf9>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x133>; - }; - }; - }; - }; - - i2c@fe5e0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5e0000 0x00 0x1000>; - clocks = <0x23 0x150 0x23 0x14f>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x33 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xfa>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - hym8563@51 { - status = "okay"; - compatible = "haoyu,hym8563"; - reg = <0x51>; - #clock-cells = <0x00>; - rtc-irq-gpio = <0x3f 0x1b 0x02>; - clock-frequency = <0x8000>; - }; - - mc3230sensor@4c { - compatible = "gs_mc3230"; - reg = <0x4c>; - type = <0x02>; - irq_enable = <0x00>; - poll_delay_ms = <0x1e>; - layout = <0x04>; - status = "okay"; - }; - - mxc6655xa@15 { - status = "disabled"; - compatible = "gs_mxc6655xa"; - pinctrl-names = "default"; - pinctrl-0 = <0xfb>; - reg = <0x15>; - irq-gpio = <0x4a 0x11 0x08>; - irq_enable = <0x00>; - poll_delay_ms = <0x1e>; - type = <0x02>; - power-off-in-suspend = <0x01>; - layout = <0x01>; - }; - }; - - timer@fe5f0000 { - compatible = "rockchip,rk3568-timer\0rockchip,rk3288-timer"; - reg = <0x00 0xfe5f0000 0x00 0x1000>; - interrupts = <0x00 0x6d 0x04>; - clocks = <0x23 0x16c 0x23 0x16d>; - clock-names = "pclk\0timer"; - }; - - watchdog@fe600000 { - compatible = "snps,dw-wdt"; - reg = <0x00 0xfe600000 0x00 0x100>; - clocks = <0x23 0x116 0x23 0x115>; - clock-names = "tclk\0pclk"; - interrupts = <0x00 0x95 0x04>; - status = "okay"; - }; - - spi@fe610000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe610000 0x00 0x1000>; - interrupts = <0x00 0x67 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x152 0x23 0x151>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x14 0x4e 0x15>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0xfc 0xfd 0xfe>; - pinctrl-1 = <0xfc 0xfd 0xff>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@fe620000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe620000 0x00 0x1000>; - interrupts = <0x00 0x68 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x154 0x23 0x153>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x16 0x4e 0x17>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x100>; - pinctrl-1 = <0x101>; - num-cs = <0x02>; - status = "disabled"; - max-freq = <0x2dc6c00>; - dev-port = <0x00>; - - spi_wk2xxx@0 { - status = "disabled"; - compatible = "firefly,spi-wk2xxx"; - reg = <0x00>; - spi-max-frequency = <0x989680>; - power-gpio = <0x102 0x0f 0x00>; - reset-gpio = <0x102 0x09 0x00>; - irq-gpio = <0x3f 0x06 0x02>; - cs-gpio = <0x4a 0x01 0x00>; - }; - }; - - spi@fe630000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe630000 0x00 0x1000>; - interrupts = <0x00 0x69 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x156 0x23 0x155>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x18 0x4e 0x19>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x103 0x104 0x105>; - pinctrl-1 = <0x103 0x104 0x106>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@fe640000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe640000 0x00 0x1000>; - interrupts = <0x00 0x6a 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x158 0x23 0x157>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x1a 0x4e 0x1b>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x107 0x108 0x109>; - pinctrl-1 = <0x107 0x108 0x10a>; - num-cs = <0x02>; - status = "disabled"; - }; - - serial@fe650000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe650000 0x00 0x100>; - interrupts = <0x00 0x75 0x04>; - clocks = <0x23 0x11f 0x23 0x11c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x02 0x4e 0x03>; - pinctrl-names = "default"; - pinctrl-0 = <0x10b>; - status = "disabled"; - }; - - serial@fe660000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe660000 0x00 0x100>; - interrupts = <0x00 0x76 0x04>; - clocks = <0x23 0x123 0x23 0x120>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x04 0x4e 0x05>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "disabled"; - }; - - serial@fe670000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe670000 0x00 0x100>; - interrupts = <0x00 0x77 0x04>; - clocks = <0x23 0x127 0x23 0x124>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x06 0x4e 0x07>; - pinctrl-names = "default"; - pinctrl-0 = <0x10d>; - status = "okay"; - }; - - serial@fe680000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe680000 0x00 0x100>; - interrupts = <0x00 0x78 0x04>; - clocks = <0x23 0x12b 0x23 0x128>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x08 0x4e 0x09>; - pinctrl-names = "default"; - pinctrl-0 = <0x10e>; - status = "okay"; - }; - - serial@fe690000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe690000 0x00 0x100>; - interrupts = <0x00 0x79 0x04>; - clocks = <0x23 0x12f 0x23 0x12c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0a 0x4e 0x0b>; - pinctrl-names = "default"; - pinctrl-0 = <0x10f>; - status = "disabled"; - }; - - serial@fe6a0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6a0000 0x00 0x100>; - interrupts = <0x00 0x7a 0x04>; - clocks = <0x23 0x133 0x23 0x130>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0c 0x4e 0x0d>; - pinctrl-names = "default"; - pinctrl-0 = <0x110>; - status = "disabled"; - }; - - serial@fe6b0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6b0000 0x00 0x100>; - interrupts = <0x00 0x7b 0x04>; - clocks = <0x23 0x137 0x23 0x134>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0e 0x4e 0x0f>; - pinctrl-names = "default"; - pinctrl-0 = <0x111>; - status = "okay"; - }; - - serial@fe6c0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6c0000 0x00 0x100>; - interrupts = <0x00 0x7c 0x04>; - clocks = <0x23 0x13b 0x23 0x138>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x10 0x4e 0x11>; - pinctrl-names = "default"; - pinctrl-0 = <0x112 0x113>; - status = "okay"; - }; - - serial@fe6d0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6d0000 0x00 0x100>; - interrupts = <0x00 0x7d 0x04>; - clocks = <0x23 0x13f 0x23 0x13c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x12 0x4e 0x13>; - pinctrl-names = "default"; - pinctrl-0 = <0x114>; - status = "okay"; - }; - - pwm@fe6e0000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0000 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x115>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - }; - - pwm@fe6e0010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0010 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x116>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - }; - - pwm@fe6e0020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0020 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x117>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6e0030 { - compatible = "rockchip,remotectl-pwm"; - reg = <0x00 0xfe6e0030 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "default"; - pinctrl-0 = <0x118>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - remote_pwm_id = <0x03>; - handle_cpu_id = <0x01>; - remote_support_psci = <0x00>; - - ir_key_firefly { - rockchip,usercode = <0xff00>; - rockchip,key_table = <0xeb 0x74 0xec 0x8b 0xfe 0x9e 0xb7 0x66 0xa3 0x96 0xf4 0x73 0xa7 0x72 0xf8 0xe8 0xfc 0x67 0xfd 0x6c 0xf1 0x69 0xe5 0x6a>; - }; - }; - - pwm@fe6f0000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0000 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x119>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0010 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11a>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0020 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11b>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0030 0x00 0x10>; - interrupts = <0x00 0x54 0x04 0x00 0x58 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11c>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - phandle = <0x157>; - }; - - pwm@fe700000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700000 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11d>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700010 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11e>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700020 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11f>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700030 0x00 0x10>; - interrupts = <0x00 0x55 0x04 0x00 0x59 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x120>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - tsadc@fe710000 { - compatible = "rockchip,rk3568-tsadc"; - reg = <0x00 0xfe710000 0x00 0x100>; - interrupts = <0x00 0x73 0x04>; - rockchip,grf = <0x3b>; - clocks = <0x23 0x111 0x23 0x10f>; - clock-names = "tsadc\0apb_pclk"; - assigned-clocks = <0x23 0x110 0x23 0x111>; - assigned-clock-rates = <0x1036640 0xaae60>; - resets = <0x23 0x182 0x23 0x181 0x23 0x1d7>; - reset-names = "tsadc\0tsadc-apb\0tsadc-phy"; - #thermal-sensor-cells = <0x01>; - nvmem-cells = <0x121 0x122>; - nvmem-cell-names = "trim_base\0trim_base_frac"; - rockchip,hw-tshut-temp = <0x1d4c0>; - rockchip,hw-tshut-mode = <0x00>; - rockchip,hw-tshut-polarity = <0x00>; - pinctrl-names = "gpio\0otpout"; - pinctrl-0 = <0x123>; - pinctrl-1 = <0x124>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x1f>; - - tsadc@0 { - reg = <0x00>; - nvmem-cells = <0x125 0x126>; - nvmem-cell-names = "trim_l\0trim_h"; - }; - - tsadc@1 { - reg = <0x01>; - nvmem-cells = <0x127 0x128>; - nvmem-cell-names = "trim_l\0trim_h"; - }; - }; - - saradc@fe720000 { - compatible = "rockchip,rk3568-saradc\0rockchip,rk3399-saradc"; - reg = <0x00 0xfe720000 0x00 0x100>; - interrupts = <0x00 0x5d 0x04>; - #io-channel-cells = <0x01>; - clocks = <0x23 0x113 0x23 0x112>; - clock-names = "saradc\0apb_pclk"; - resets = <0x23 0x180>; - reset-names = "saradc-apb"; - status = "okay"; - vref-supply = <0x129>; - phandle = <0x49>; - }; - - mailbox@fe780000 { - compatible = "rockchip,rk3568-mailbox\0rockchip,rk3368-mailbox"; - reg = <0x00 0xfe780000 0x00 0x1000>; - interrupts = <0x00 0xb7 0x04 0x00 0xb8 0x04 0x00 0xb9 0x04 0x00 0xba 0x04>; - clocks = <0x23 0x11b>; - clock-names = "pclk_mailbox"; - #mbox-cells = <0x01>; - status = "disabled"; - }; - - phy@fe820000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe820000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x1f 0x23 0x17c 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x1f>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c4 0x23 0x1c5>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12b>; - status = "okay"; - phandle = <0x24>; - }; - - phy@fe830000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe830000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x22 0x23 0x17d 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x22>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c6 0x23 0x1c7>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12c>; - status = "okay"; - phandle = <0x26>; - }; - - phy@fe840000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe840000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x25 0x23 0x17e 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x25>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c8 0x23 0x1c9>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12d>; - status = "okay"; - phandle = <0x27>; - }; - - phy@fe850000 { - compatible = "rockchip,rk3568-dsi-dphy\0rockchip,rk3568-video-phy"; - reg = <0x00 0xfe850000 0x00 0x10000 0x00 0xfe060000 0x00 0x10000>; - reg-names = "phy\0host"; - clocks = <0x3a 0x17 0x23 0x17a 0x23 0xe8>; - clock-names = "ref\0pclk\0pclk_host"; - #clock-cells = <0x00>; - resets = <0x23 0x1bb>; - reset-names = "apb"; - power-domains = <0x25 0x09>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x34>; - }; - - phy@fe860000 { - compatible = "rockchip,rk3568-dsi-dphy\0rockchip,rk3568-video-phy"; - reg = <0x00 0xfe860000 0x00 0x10000 0x00 0xfe070000 0x00 0x10000>; - reg-names = "phy\0host"; - clocks = <0x3a 0x19 0x23 0x17b 0x23 0xe9>; - clock-names = "ref\0pclk\0pclk_host"; - #clock-cells = <0x00>; - resets = <0x23 0x1bc>; - reset-names = "apb"; - power-domains = <0x25 0x09>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x36>; - }; - - csi2-dphy-hw@fe870000 { - compatible = "rockchip,rk3568-csi2-dphy-hw"; - reg = <0x00 0xfe870000 0x00 0x1000>; - clocks = <0x23 0x179>; - clock-names = "pclk"; - rockchip,grf = <0x3b>; - status = "okay"; - phandle = <0x12e>; - }; - - csi2-dphy0 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x12f>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf6>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x130>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf5>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0x131>; - data-lanes = <0x01 0x02>; - phandle = <0xf7>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0x132>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf8>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0x133>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf9>; - }; - }; - - port@1 { - reg = <0x01>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x134>; - phandle = <0x8c>; - }; - }; - }; - }; - - csi2-dphy1 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "disabled"; - }; - - csi2-dphy2 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "disabled"; - }; - - usb2-phy@fe8a0000 { - compatible = "rockchip,rk3568-usb2phy"; - reg = <0x00 0xfe8a0000 0x00 0x10000>; - interrupts = <0x00 0x87 0x04>; - clocks = <0x3a 0x13>; - clock-names = "phyclk"; - #clock-cells = <0x00>; - assigned-clocks = <0x23 0x0b>; - assigned-clock-parents = <0x29>; - clock-output-names = "usb480m_phy"; - rockchip,usbgrf = <0x135>; - status = "okay"; - phandle = <0x29>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2b>; - }; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0x28>; - }; - }; - - usb2-phy@fe8b0000 { - compatible = "rockchip,rk3568-usb2phy"; - reg = <0x00 0xfe8b0000 0x00 0x10000>; - interrupts = <0x00 0x88 0x04>; - clocks = <0x3a 0x15>; - clock-names = "phyclk"; - #clock-cells = <0x00>; - rockchip,usbgrf = <0x137>; - status = "okay"; - phandle = <0x2c>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2e>; - }; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2d>; - }; - }; - - phy@fe8c0000 { - compatible = "rockchip,rk3568-pcie3-phy"; - reg = <0x00 0xfe8c0000 0x00 0x20000>; - #phy-cells = <0x00>; - clocks = <0x3a 0x26 0x3a 0x27 0x23 0x177>; - clock-names = "refclk_m\0refclk_n\0pclk"; - resets = <0x23 0x1be>; - reset-names = "phy"; - rockchip,phy-grf = <0x138>; - status = "okay"; - phandle = <0xc0>; - }; - - pinctrl { - compatible = "rockchip,rk3568-pinctrl"; - rockchip,grf = <0x3b>; - rockchip,pmu = <0x3c>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - gpio0@fdd60000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfdd60000 0x00 0x100>; - interrupts = <0x00 0x21 0x04>; - clocks = <0x3a 0x2e 0x3a 0x0c>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x3f>; - }; - - gpio1@fe740000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe740000 0x00 0x100>; - interrupts = <0x00 0x22 0x04>; - clocks = <0x23 0x163 0x23 0x164>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x159>; - }; - - gpio2@fe750000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe750000 0x00 0x100>; - interrupts = <0x00 0x23 0x04>; - clocks = <0x23 0x165 0x23 0x166>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x91>; - }; - - gpio3@fe760000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe760000 0x00 0x100>; - interrupts = <0x00 0x24 0x04>; - clocks = <0x23 0x167 0x23 0x168>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x4a>; - }; - - gpio4@fe770000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe770000 0x00 0x100>; - interrupts = <0x00 0x25 0x04>; - clocks = <0x23 0x169 0x23 0x16a>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0xf4>; - }; - - pcfg-pull-up { - bias-pull-up; - phandle = <0x13b>; - }; - - pcfg-pull-down { - bias-pull-down; - phandle = <0x142>; - }; - - pcfg-pull-none { - bias-disable; - phandle = <0x139>; - }; - - pcfg-pull-none-drv-level-1 { - bias-disable; - drive-strength = <0x01>; - phandle = <0x13d>; - }; - - pcfg-pull-none-drv-level-2 { - bias-disable; - drive-strength = <0x02>; - phandle = <0x13c>; - }; - - pcfg-pull-none-drv-level-3 { - bias-disable; - drive-strength = <0x03>; - phandle = <0x141>; - }; - - pcfg-pull-up-drv-level-1 { - bias-pull-up; - drive-strength = <0x01>; - phandle = <0x140>; - }; - - pcfg-pull-up-drv-level-2 { - bias-pull-up; - drive-strength = <0x02>; - phandle = <0x13a>; - }; - - pcfg-pull-none-smt { - bias-disable; - input-schmitt-enable; - phandle = <0x13e>; - }; - - pcfg-output-low-pull-down { - output-low; - bias-pull-down; - phandle = <0x13f>; - }; - - acodec { - - acodec-pins { - rockchip,pins = <0x01 0x09 0x05 0x139 0x01 0x01 0x05 0x139 0x01 0x00 0x05 0x139 0x01 0x07 0x05 0x139 0x01 0x08 0x05 0x139 0x01 0x03 0x05 0x139 0x01 0x05 0x05 0x139>; - phandle = <0xea>; - }; - }; - - cam { - - vcc-cam { - rockchip,pins = <0x00 0x11 0x00 0x139>; - phandle = <0x158>; - }; - }; - - can1 { - - can1m1-pins { - rockchip,pins = <0x04 0x12 0x03 0x139 0x04 0x13 0x03 0x139>; - phandle = <0xec>; - }; - }; - - can2 { - - can2m0-pins { - rockchip,pins = <0x04 0x0c 0x03 0x139 0x04 0x0d 0x03 0x139>; - phandle = <0xed>; - }; - }; - - cif { - - cif-clk { - rockchip,pins = <0x04 0x10 0x01 0x139>; - phandle = <0xf3>; - }; - }; - - clk32k { - - clk32k-out0 { - rockchip,pins = <0x00 0x08 0x02 0x139>; - phandle = <0x22>; - }; - }; - - ebc { - - ebc-pins { - rockchip,pins = <0x04 0x10 0x02 0x139 0x04 0x0b 0x02 0x139 0x04 0x0c 0x02 0x139 0x04 0x06 0x02 0x139 0x04 0x11 0x02 0x139 0x03 0x16 0x02 0x139 0x03 0x17 0x02 0x139 0x03 0x18 0x02 0x139 0x03 0x19 0x02 0x139 0x03 0x1a 0x02 0x139 0x03 0x1b 0x02 0x139 0x03 0x1c 0x02 0x139 0x03 0x1d 0x02 0x139 0x03 0x1e 0x02 0x139 0x03 0x1f 0x02 0x139 0x04 0x00 0x02 0x139 0x04 0x01 0x02 0x139 0x04 0x02 0x02 0x139 0x04 0x03 0x02 0x139 0x04 0x04 0x02 0x139 0x04 0x05 0x02 0x139 0x04 0x0e 0x02 0x139 0x04 0x0f 0x02 0x139>; - phandle = <0x7c>; - }; - }; - - fspi { - - fspi-pins { - rockchip,pins = <0x01 0x18 0x01 0x139 0x01 0x1b 0x01 0x139 0x01 0x19 0x01 0x139 0x01 0x1a 0x01 0x139 0x01 0x17 0x02 0x139 0x01 0x1c 0x01 0x139>; - phandle = <0xd4>; - }; - }; - - gmac0 { - - gmac0-miim { - rockchip,pins = <0x02 0x13 0x02 0x139 0x02 0x14 0x02 0x139>; - phandle = <0xc8>; - }; - - gmac0-clkinout { - rockchip,pins = <0x02 0x12 0x02 0x139>; - phandle = <0xcd>; - }; - - gmac0-rx-bus2 { - rockchip,pins = <0x02 0x0e 0x01 0x139 0x02 0x0f 0x02 0x139 0x02 0x10 0x02 0x139>; - phandle = <0xca>; - }; - - gmac0-tx-bus2 { - rockchip,pins = <0x02 0x0b 0x01 0x13c 0x02 0x0c 0x01 0x13c 0x02 0x0d 0x01 0x139>; - phandle = <0xc9>; - }; - - gmac0-rgmii-clk { - rockchip,pins = <0x02 0x05 0x02 0x139 0x02 0x08 0x02 0x13d>; - phandle = <0xcb>; - }; - - gmac0-rgmii-bus { - rockchip,pins = <0x02 0x03 0x02 0x139 0x02 0x04 0x02 0x139 0x02 0x06 0x02 0x13c 0x02 0x07 0x02 0x13c>; - phandle = <0xcc>; - }; - }; - - gmac1 { - - gmac1m1-miim { - rockchip,pins = <0x04 0x0e 0x03 0x139 0x04 0x0f 0x03 0x139>; - phandle = <0x93>; - }; - - gmac1m1-clkinout { - rockchip,pins = <0x04 0x11 0x03 0x139>; - phandle = <0x98>; - }; - - gmac1m1-rx-bus2 { - rockchip,pins = <0x04 0x07 0x03 0x139 0x04 0x08 0x03 0x139 0x04 0x09 0x03 0x139>; - phandle = <0x95>; - }; - - gmac1m1-tx-bus2 { - rockchip,pins = <0x04 0x04 0x03 0x13c 0x04 0x05 0x03 0x13c 0x04 0x06 0x03 0x139>; - phandle = <0x94>; - }; - - gmac1m1-rgmii-clk { - rockchip,pins = <0x04 0x03 0x03 0x139 0x04 0x00 0x03 0x13d>; - phandle = <0x96>; - }; - - gmac1m1-rgmii-bus { - rockchip,pins = <0x04 0x01 0x03 0x139 0x04 0x02 0x03 0x139 0x03 0x1e 0x03 0x13c 0x03 0x1f 0x03 0x13c>; - phandle = <0x97>; - }; - }; - - hdmitx { - - hdmitxm0-cec { - rockchip,pins = <0x04 0x19 0x01 0x139>; - phandle = <0xac>; - }; - - hdmitx-scl { - rockchip,pins = <0x04 0x17 0x01 0x139>; - phandle = <0xaa>; - }; - - hdmitx-sda { - rockchip,pins = <0x04 0x18 0x01 0x139>; - phandle = <0xab>; - }; - }; - - i2c0 { - - i2c0-xfer { - rockchip,pins = <0x00 0x09 0x01 0x13e 0x00 0x0a 0x01 0x13e>; - phandle = <0x3d>; - }; - }; - - i2c1 { - - i2c1-xfer { - rockchip,pins = <0x00 0x0b 0x01 0x13e 0x00 0x0c 0x01 0x13e>; - phandle = <0xee>; - }; - }; - - i2c2 { - - i2c2m0-xfer { - rockchip,pins = <0x00 0x0d 0x01 0x13e 0x00 0x0e 0x01 0x13e>; - phandle = <0xf0>; - }; - }; - - i2c3 { - - i2c3m0-xfer { - rockchip,pins = <0x01 0x01 0x01 0x13e 0x01 0x00 0x01 0x13e>; - phandle = <0xf1>; - }; - }; - - i2c4 { - - i2c4m0-xfer { - rockchip,pins = <0x04 0x0b 0x01 0x13e 0x04 0x0a 0x01 0x13e>; - phandle = <0xf2>; - }; - }; - - i2c5 { - - i2c5m0-xfer { - rockchip,pins = <0x03 0x0b 0x04 0x13e 0x03 0x0c 0x04 0x13e>; - phandle = <0xfa>; - }; - }; - - i2s1 { - - i2s1m0-lrcktx { - rockchip,pins = <0x01 0x05 0x01 0x13e>; - phandle = <0xd7>; - }; - - i2s1m0-mclk { - rockchip,pins = <0x01 0x02 0x01 0x13e>; - phandle = <0x47>; - }; - - i2s1m0-sclktx { - rockchip,pins = <0x01 0x03 0x01 0x13e>; - phandle = <0xd6>; - }; - - i2s1m0-sdi0 { - rockchip,pins = <0x01 0x0b 0x01 0x139>; - phandle = <0xd8>; - }; - - i2s1m0-sdo0 { - rockchip,pins = <0x01 0x07 0x01 0x139>; - phandle = <0xd9>; - }; - }; - - i2s2 { - - i2s2m0-lrcktx { - rockchip,pins = <0x02 0x13 0x01 0x13e>; - phandle = <0xdb>; - }; - - i2s2m0-sclktx { - rockchip,pins = <0x02 0x12 0x01 0x13e>; - phandle = <0xda>; - }; - - i2s2m0-sdi { - rockchip,pins = <0x02 0x15 0x01 0x139>; - phandle = <0xdc>; - }; - - i2s2m0-sdo { - rockchip,pins = <0x02 0x14 0x01 0x139>; - phandle = <0xdd>; - }; - }; - - i2s3 { - - i2s3m0-lrck { - rockchip,pins = <0x03 0x04 0x04 0x13e>; - phandle = <0xdf>; - }; - - i2s3m0-sclk { - rockchip,pins = <0x03 0x03 0x04 0x13e>; - phandle = <0xde>; - }; - - i2s3m0-sdi { - rockchip,pins = <0x03 0x06 0x04 0x139>; - phandle = <0xe0>; - }; - - i2s3m0-sdo { - rockchip,pins = <0x03 0x05 0x04 0x139>; - phandle = <0xe1>; - }; - }; - - lcdc { - - lcdc-ctl { - rockchip,pins = <0x03 0x00 0x01 0x139 0x02 0x18 0x01 0x139 0x02 0x19 0x01 0x139 0x02 0x1a 0x01 0x139 0x02 0x1b 0x01 0x139 0x02 0x1c 0x01 0x139 0x02 0x1d 0x01 0x139 0x02 0x1e 0x01 0x139 0x02 0x1f 0x01 0x139 0x03 0x01 0x01 0x139 0x03 0x02 0x01 0x139 0x03 0x03 0x01 0x139 0x03 0x04 0x01 0x139 0x03 0x05 0x01 0x139 0x03 0x06 0x01 0x139 0x03 0x07 0x01 0x139 0x03 0x08 0x01 0x139 0x03 0x09 0x01 0x139 0x03 0x0a 0x01 0x139 0x03 0x0b 0x01 0x139 0x03 0x0c 0x01 0x139 0x03 0x0d 0x01 0x139 0x03 0x0e 0x01 0x139 0x03 0x0f 0x01 0x139 0x03 0x10 0x01 0x139 0x03 0x13 0x01 0x139 0x03 0x11 0x01 0x139 0x03 0x12 0x01 0x139>; - phandle = <0x39>; - }; - }; - - pdm { - - pdmm0-clk { - rockchip,pins = <0x01 0x06 0x03 0x139>; - phandle = <0xe2>; - }; - - pdmm0-clk1 { - rockchip,pins = <0x01 0x04 0x03 0x139>; - phandle = <0xe3>; - }; - - pdmm0-sdi0 { - rockchip,pins = <0x01 0x0b 0x02 0x139>; - phandle = <0xe4>; - }; - - pdmm0-sdi1 { - rockchip,pins = <0x01 0x0a 0x03 0x139>; - phandle = <0xe5>; - }; - - pdmm0-sdi2 { - rockchip,pins = <0x01 0x09 0x03 0x139>; - phandle = <0xe6>; - }; - - pdmm0-sdi3 { - rockchip,pins = <0x01 0x08 0x03 0x139>; - phandle = <0xe7>; - }; - }; - - pmic { - - pmic_int { - rockchip,pins = <0x00 0x03 0x00 0x13b>; - phandle = <0x40>; - }; - - soc_slppin_gpio { - rockchip,pins = <0x00 0x02 0x00 0x13f>; - phandle = <0x43>; - }; - - soc_slppin_slp { - rockchip,pins = <0x00 0x02 0x01 0x13b>; - phandle = <0x41>; - }; - - soc_slppin_rst { - rockchip,pins = <0x00 0x02 0x02 0x139>; - }; - - spk_ctl_gpio { - rockchip,pins = <0x03 0x15 0x00 0x13b>; - phandle = <0x48>; - }; - }; - - pwm0 { - - pwm0m0-pins { - rockchip,pins = <0x00 0x0f 0x01 0x139>; - phandle = <0x50>; - }; - }; - - pwm1 { - - pwm1m0-pins { - rockchip,pins = <0x00 0x10 0x01 0x139>; - phandle = <0x51>; - }; - }; - - pwm2 { - - pwm2m0-pins { - rockchip,pins = <0x00 0x11 0x01 0x139>; - phandle = <0x52>; - }; - }; - - pwm3 { - - pwm3-pins { - rockchip,pins = <0x00 0x12 0x01 0x139>; - phandle = <0x53>; - }; - }; - - pwm4 { - - pwm4-pins { - rockchip,pins = <0x00 0x13 0x01 0x139>; - phandle = <0x115>; - }; - }; - - pwm5 { - - pwm5-pins { - rockchip,pins = <0x00 0x14 0x01 0x139>; - phandle = <0x116>; - }; - }; - - pwm6 { - - pwm6-pins { - rockchip,pins = <0x00 0x15 0x01 0x139>; - phandle = <0x117>; - }; - }; - - pwm7 { - - pwm7-pins { - rockchip,pins = <0x00 0x16 0x01 0x139>; - phandle = <0x118>; - }; - }; - - pwm8 { - - pwm8m0-pins { - rockchip,pins = <0x03 0x09 0x05 0x139>; - phandle = <0x119>; - }; - }; - - pwm9 { - - pwm9m0-pins { - rockchip,pins = <0x03 0x0a 0x05 0x139>; - phandle = <0x11a>; - }; - }; - - pwm10 { - - pwm10m0-pins { - rockchip,pins = <0x03 0x0d 0x05 0x139>; - phandle = <0x11b>; - }; - }; - - pwm11 { - - pwm11m0-pins { - rockchip,pins = <0x03 0x0e 0x05 0x139>; - phandle = <0x11c>; - }; - }; - - pwm12 { - - pwm12m0-pins { - rockchip,pins = <0x03 0x0f 0x02 0x139>; - phandle = <0x11d>; - }; - }; - - pwm13 { - - pwm13m0-pins { - rockchip,pins = <0x03 0x10 0x02 0x139>; - phandle = <0x11e>; - }; - }; - - pwm14 { - - pwm14m0-pins { - rockchip,pins = <0x03 0x14 0x01 0x139>; - phandle = <0x11f>; - }; - }; - - pwm15 { - - pwm15m0-pins { - rockchip,pins = <0x03 0x15 0x01 0x139>; - phandle = <0x120>; - }; - }; - - scr { - - scr-pins { - rockchip,pins = <0x01 0x02 0x03 0x139 0x01 0x07 0x03 0x13b 0x01 0x03 0x03 0x13b 0x01 0x05 0x03 0x139>; - phandle = <0xeb>; - }; - }; - - sdmmc0 { - - sdmmc0-bus4 { - rockchip,pins = <0x01 0x1d 0x01 0x13a 0x01 0x1e 0x01 0x13a 0x01 0x1f 0x01 0x13a 0x02 0x00 0x01 0x13a>; - phandle = <0xd0>; - }; - - sdmmc0-clk { - rockchip,pins = <0x02 0x02 0x01 0x13a>; - phandle = <0xd1>; - }; - - sdmmc0-cmd { - rockchip,pins = <0x02 0x01 0x01 0x13a>; - phandle = <0xd2>; - }; - - sdmmc0-det { - rockchip,pins = <0x00 0x04 0x01 0x13b>; - phandle = <0xd3>; - }; - }; - - sdmmc2 { - - sdmmc2m0-bus4 { - rockchip,pins = <0x03 0x16 0x03 0x13a 0x03 0x17 0x03 0x13a 0x03 0x18 0x03 0x13a 0x03 0x19 0x03 0x13a>; - phandle = <0xb0>; - }; - - sdmmc2m0-clk { - rockchip,pins = <0x03 0x1b 0x03 0x13a>; - phandle = <0xb2>; - }; - - sdmmc2m0-cmd { - rockchip,pins = <0x03 0x1a 0x03 0x13a>; - phandle = <0xb1>; - }; - }; - - spdif { - - spdifm1-tx { - rockchip,pins = <0x03 0x15 0x02 0x139>; - phandle = <0xe9>; - }; - }; - - spi0 { - - spi0m0-pins { - rockchip,pins = <0x00 0x0d 0x02 0x139 0x00 0x15 0x02 0x139 0x00 0x0e 0x02 0x139>; - phandle = <0xfe>; - }; - - spi0m0-cs0 { - rockchip,pins = <0x00 0x16 0x02 0x139>; - phandle = <0xfc>; - }; - - spi0m0-cs1 { - rockchip,pins = <0x00 0x14 0x02 0x139>; - phandle = <0xfd>; - }; - }; - - spi1 { - - spi1m1-pins { - rockchip,pins = <0x03 0x13 0x03 0x139 0x03 0x12 0x03 0x139 0x03 0x11 0x03 0x139>; - phandle = <0x100>; - }; - }; - - spi2 { - - spi2m0-pins { - rockchip,pins = <0x02 0x11 0x04 0x139 0x02 0x12 0x04 0x139 0x02 0x13 0x04 0x139>; - phandle = <0x105>; - }; - - spi2m0-cs0 { - rockchip,pins = <0x02 0x14 0x04 0x139>; - phandle = <0x103>; - }; - - spi2m0-cs1 { - rockchip,pins = <0x02 0x15 0x04 0x139>; - phandle = <0x104>; - }; - }; - - spi3 { - - spi3m0-pins { - rockchip,pins = <0x04 0x0b 0x04 0x139 0x04 0x08 0x04 0x139 0x04 0x0a 0x04 0x139>; - phandle = <0x109>; - }; - - spi3m0-cs0 { - rockchip,pins = <0x04 0x06 0x04 0x139>; - phandle = <0x107>; - }; - - spi3m0-cs1 { - rockchip,pins = <0x04 0x07 0x04 0x139>; - phandle = <0x108>; - }; - }; - - tsadc { - - tsadc-shutorg { - rockchip,pins = <0x00 0x01 0x02 0x139>; - phandle = <0x124>; - }; - }; - - uart0 { - - uart0-xfer { - rockchip,pins = <0x00 0x10 0x03 0x13b 0x00 0x11 0x03 0x13b>; - phandle = <0x4f>; - }; - }; - - uart1 { - - uart1m0-xfer { - rockchip,pins = <0x02 0x0b 0x02 0x13b 0x02 0x0c 0x02 0x13b>; - phandle = <0x10b>; - }; - }; - - uart2 { - - uart2m0-xfer { - rockchip,pins = <0x00 0x18 0x01 0x13b 0x00 0x19 0x01 0x13b>; - phandle = <0x10c>; - }; - }; - - uart3 { - - uart3m1-xfer { - rockchip,pins = <0x03 0x10 0x04 0x13b 0x03 0x0f 0x04 0x13b>; - phandle = <0x10d>; - }; - }; - - uart4 { - - uart4m1-xfer { - rockchip,pins = <0x03 0x09 0x04 0x13b 0x03 0x0a 0x04 0x13b>; - phandle = <0x10e>; - }; - }; - - uart5 { - - uart5m0-xfer { - rockchip,pins = <0x02 0x01 0x03 0x13b 0x02 0x02 0x03 0x13b>; - phandle = <0x10f>; - }; - }; - - uart6 { - - uart6m0-xfer { - rockchip,pins = <0x02 0x03 0x03 0x13b 0x02 0x04 0x03 0x13b>; - phandle = <0x110>; - }; - }; - - uart7 { - - uart7m1-xfer { - rockchip,pins = <0x03 0x15 0x04 0x13b 0x03 0x14 0x04 0x13b>; - phandle = <0x111>; - }; - }; - - uart8 { - - uart8m0-xfer { - rockchip,pins = <0x02 0x16 0x02 0x13b 0x02 0x15 0x03 0x13b>; - phandle = <0x112>; - }; - - uart8m0-ctsn { - rockchip,pins = <0x02 0x0a 0x03 0x139>; - phandle = <0x113>; - }; - - uart8m0-rtsn { - rockchip,pins = <0x02 0x09 0x03 0x139>; - phandle = <0x155>; - }; - }; - - uart9 { - - uart9m1-xfer { - rockchip,pins = <0x04 0x16 0x04 0x13b 0x04 0x15 0x04 0x13b>; - phandle = <0x114>; - }; - }; - - spi0-hs { - - spi0m0-pins { - rockchip,pins = <0x00 0x0d 0x02 0x140 0x00 0x15 0x02 0x140 0x00 0x0e 0x02 0x140>; - phandle = <0xff>; - }; - }; - - spi1-hs { - - spi1m1-pins { - rockchip,pins = <0x03 0x13 0x03 0x140 0x03 0x12 0x03 0x140 0x03 0x11 0x03 0x140>; - phandle = <0x101>; - }; - }; - - spi2-hs { - - spi2m0-pins { - rockchip,pins = <0x02 0x11 0x04 0x140 0x02 0x12 0x04 0x140 0x02 0x13 0x04 0x140>; - phandle = <0x106>; - }; - }; - - spi3-hs { - - spi3m0-pins { - rockchip,pins = <0x04 0x0b 0x04 0x140 0x04 0x08 0x04 0x140 0x04 0x0a 0x04 0x140>; - phandle = <0x10a>; - }; - }; - - gpio-func { - - tsadc-gpio-func { - rockchip,pins = <0x00 0x01 0x00 0x139>; - phandle = <0x123>; - }; - }; - - usb { - - vcc5v0-host-en { - rockchip,pins = <0x00 0x06 0x00 0x139>; - phandle = <0x150>; - }; - - vcc5v0-otg-en { - rockchip,pins = <0x00 0x05 0x00 0x139>; - phandle = <0x151>; - }; - - vcc-hub-reset-en { - rockchip,pins = <0x01 0x04 0x00 0x139>; - phandle = <0x15a>; - }; - }; - - headphone { - - hp-det { - rockchip,pins = <0x03 0x12 0x00 0x142>; - phandle = <0x148>; - }; - }; - - sdio-pwrseq { - - wifi-enable-h { - rockchip,pins = <0x03 0x1d 0x00 0x139>; - phandle = <0x153>; - }; - }; - - wireless-wlan { - - wifi-host-wake-irq { - rockchip,pins = <0x03 0x1c 0x00 0x142>; - phandle = <0x154>; - }; - }; - - wireless-bluetooth { - - uart8-gpios { - rockchip,pins = <0x02 0x09 0x00 0x139>; - phandle = <0x156>; - }; - }; - - touch { - - touch-gpio { - rockchip,pins = <0x00 0x0d 0x00 0x13b 0x00 0x0e 0x00 0x139>; - phandle = <0xef>; - }; - }; - - mxc6655xa { - - mxc6655xa_irq_gpio { - rockchip,pins = <0x03 0x11 0x00 0x139>; - phandle = <0xfb>; - }; - }; - - pcie { - - pcie-pi6c-oe-en { - rockchip,pins = <0x03 0x07 0x00 0x139>; - phandle = <0x15b>; - }; - }; - - leds { - - leds-gpio { - rockchip,pins = <0x01 0x0a 0x00 0x139 0x01 0x09 0x00 0x139 0x01 0x08 0x00 0x139 0x02 0x11 0x00 0x139>; - phandle = <0x15d>; - }; - }; - - 4g { - - vcc-4g-power-en { - rockchip,pins = <0x03 0x03 0x00 0x139>; - phandle = <0x15c>; - }; - }; - - usb-typec { - - usbc0-int { - rockchip,pins = <0x00 0x11 0x00 0x13b>; - phandle = <0x4b>; - }; - - vcc5v0-typec0-en { - rockchip,pins = <0x00 0x05 0x00 0x139>; - }; - }; - }; - - audiopwmout-diff { - status = "disabled"; - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "rockchip,audiopwmout-diff"; - simple-audio-card,mclk-fs = <0x100>; - simple-audio-card,bitclock-master = <0x143>; - simple-audio-card,frame-master = <0x143>; - - simple-audio-card,cpu { - sound-dai = <0x144>; - }; - - simple-audio-card,codec { - sound-dai = <0x145>; - phandle = <0x143>; - }; - }; - - dc-12v { - compatible = "regulator-fixed"; - regulator-name = "dc_12v"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b00>; - regulator-max-microvolt = <0xb71b00>; - phandle = <0x14f>; - }; - - hdmi-sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,mclk-fs = <0x80>; - simple-audio-card,name = "rockchip,hdmi"; - status = "okay"; - - simple-audio-card,cpu { - sound-dai = <0x146>; - }; - - simple-audio-card,codec { - sound-dai = <0x147>; - }; - }; - - rk-headset { - status = "disabled"; - compatible = "rockchip_headset"; - headset_gpio = <0x4a 0x12 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x148>; - }; - - dummy-codec { - status = "disabled"; - compatible = "rockchip,dummy-codec"; - #sound-dai-cells = <0x00>; - phandle = <0x14a>; - }; - - pdm-mic-array { - status = "disabled"; - compatible = "simple-audio-card"; - simple-audio-card,name = "rockchip,pdm-mic-array"; - - simple-audio-card,cpu { - sound-dai = <0x149>; - }; - - simple-audio-card,codec { - sound-dai = <0x14a>; - }; - }; - - rk809-sound { - status = "okay"; - compatible = "rockchip,multicodecs-card"; - rockchip,card-name = "rockchip-rk809"; - rockchip,format = "i2s"; - rockchip,mclk-fs = <0x100>; - rockchip,cpu = <0xe8>; - rockchip,codec = <0x14b>; - }; - - spdif-sound { - status = "okay"; - compatible = "simple-audio-card"; - simple-audio-card,name = "ROCKCHIP,SPDIF"; - - simple-audio-card,cpu { - sound-dai = <0x14c>; - }; - - simple-audio-card,codec { - sound-dai = <0x14d>; - }; - }; - - spdif-out { - status = "okay"; - compatible = "linux,spdif-dit"; - #sound-dai-cells = <0x00>; - phandle = <0x14d>; - }; - - vad-sound { - status = "disabled"; - compatible = "rockchip,multicodecs-card"; - rockchip,card-name = "rockchip,rk3568-vad"; - rockchip,cpu = <0xe8>; - rockchip,codec = <0x14b 0x14e>; - }; - - vcc3v3-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - vin-supply = <0x14f>; - phandle = <0x46>; - }; - - vcc5v0-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - vin-supply = <0x14f>; - phandle = <0x3e>; - }; - - vcc5v0-host-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x3f 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x150>; - regulator-name = "vcc5v0_host"; - regulator-always-on; - regulator-boot-on; - phandle = <0x136>; - }; - - vcc5v0-otg-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x3f 0x05 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x151>; - regulator-name = "vcc5v0_otg"; - phandle = <0x4c>; - }; - - vcc3v3-lcd0-n { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_lcd0_n"; - regulator-boot-on; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc3v3-lcd1-n { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_lcd1_n"; - regulator-boot-on; - status = "disabled"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - test-power { - status = "okay"; - }; - - chosen { - // linux,initrd-end = <0x00 0xaacf15d>; - // linux,initrd-start = <0x00 0xa200000>; - bootargs = "storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal androidboot.verifiedbootstate=orange rw rootwait earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 root=PARTLABEL=rootfs rootfstype=ext4 overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 swiotlb=0x10000 net.ifnames=0 comm-05/20/2025 androidboot.fwver=ddr-v1.21-2d653b3476,spl-v1.14,bl31-v1.44,bl32-v2.12,uboot--boot"; - }; - - fiq-debugger { - compatible = "rockchip,fiq-debugger"; - rockchip,serial-id = <0x02>; - rockchip,wake-irq = <0x00>; - rockchip,irq-mode-enable = <0x01>; - rockchip,baudrate = <0x16e360>; - interrupts = <0x00 0xfc 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "okay"; - }; - - debug@fd904000 { - compatible = "rockchip,debug"; - reg = <0x00 0xfd904000 0x00 0x1000 0x00 0xfd905000 0x00 0x1000 0x00 0xfd906000 0x00 0x1000 0x00 0xfd907000 0x00 0x1000>; - }; - - cspmu@fd90c000 { - compatible = "rockchip,cspmu"; - reg = <0x00 0xfd90c000 0x00 0x1000 0x00 0xfd90d000 0x00 0x1000 0x00 0xfd90e000 0x00 0x1000 0x00 0xfd90f000 0x00 0x1000>; - }; - - adc-keys { - compatible = "adc-keys"; - io-channels = <0x49 0x00>; - io-channel-names = "buttons"; - keyup-threshold-microvolt = <0x1b7740>; - poll-interval = <0x64>; - - recovery-key { - label = "F12"; - linux,code = <0x58>; - press-threshold-microvolt = <0x6d6>; - }; - - vol-down-key { - label = "volume down"; - linux,code = <0x72>; - press-threshold-microvolt = <0x48a1c>; - }; - - menu-key { - label = "menu"; - linux,code = <0x8b>; - press-threshold-microvolt = <0xef420>; - }; - - back-key { - label = "back"; - linux,code = <0x9e>; - press-threshold-microvolt = <0x13eb9c>; - }; - }; - - vcc2v5-ddr { - compatible = "regulator-fixed"; - regulator-name = "vcc2v5-sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x2625a0>; - regulator-max-microvolt = <0x2625a0>; - vin-supply = <0x46>; - }; - - pcie30-avdd0v9 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd0v9"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - vin-supply = <0x46>; - }; - - pcie30-avdd1v8 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd1v8"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - vin-supply = <0x46>; - }; - - gpio-regulator { - compatible = "regulator-gpio"; - regulator-name = "pcie30_3v3"; - regulator-min-microvolt = <0x186a0>; - regulator-max-microvolt = <0x325aa0>; - gpios = <0x3f 0x1c 0x00>; - gpios-states = <0x01>; - states = <0x186a0 0x00 0x325aa0 0x01>; - phandle = <0xc2>; - }; - - vcc3v3-bu { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_bu"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - vin-supply = <0x3e>; - }; - - sdio-pwrseq { - compatible = "mmc-pwrseq-simple"; - clocks = <0x152 0x01>; - clock-names = "ext_clock"; - pinctrl-names = "default"; - pinctrl-0 = <0x153>; - post-power-on-delay-ms = <0x64>; - reset-gpios = <0x4a 0x1d 0x01>; - status = "okay"; - phandle = <0xb3>; - }; - - wireless-wlan { - compatible = "wlan-platdata"; - rockchip,grf = <0x3b>; - wifi_chip_type = "ap6256"; - pinctrl-names = "default"; - pinctrl-0 = <0x154>; - WIFI,host_wake_irq = <0x4a 0x1c 0x00>; - status = "okay"; - }; - - wireless-bluetooth { - compatible = "bluetooth-platdata"; - clocks = <0x152 0x01>; - clock-names = "ext_clock"; - uart_rts_gpios = <0x91 0x09 0x01>; - pinctrl-names = "default\0rts_gpio"; - pinctrl-0 = <0x155>; - pinctrl-1 = <0x156>; - BT,reset_gpio = <0x4a 0x00 0x00>; - BT,wake_gpio = <0x4a 0x02 0x00>; - BT,wake_host_irq = <0x4a 0x01 0x00>; - status = "okay"; - }; - - flash-led { - compatible = "led,rgb13h"; - label = "pwm-flash-led"; - led-max-microamp = <0x4e20>; - flash-max-microamp = <0x4e20>; - flash-max-timeout-us = <0xf4240>; - pwms = <0x157 0x00 0x61a8 0x00>; - rockchip,camera-module-index = <0x01>; - rockchip,camera-module-facing = "front"; - status = "disabled"; - }; - - vcc-camera-regulator { - compatible = "regulator-fixed"; - gpio = <0x102 0x03 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x158>; - regulator-name = "vcc_camera"; - enable-active-high; - status = "disabled"; - }; - - vcc-hub-reset-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x159 0x04 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x15a>; - regulator-name = "vcc_hub_reset_en"; - regulator-always-on; - }; - - pcie-pi6c-oe-regulator { - compatible = "regulator-fixed"; - gpio = <0x4a 0x07 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x15b>; - regulator-name = "pcie_pi6c_oe_en"; - regulator-always-on; - }; - - vcc-4g-power-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x4a 0x03 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x15c>; - regulator-name = "vcc_4g_power_en"; - regulator-always-on; - }; - - leds { - status = "okay"; - compatible = "gpio-leds"; - pinctrl-names = "default"; - pinctrl-0 = <0x15d>; - - power { - label = "firefly:blue:power"; - linux,default-trigger = "ir-power-click"; - default-state = "on"; - gpios = <0x159 0x0a 0x00>; - }; - - user { - label = "firefly:yellow:user"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x159 0x09 0x00>; - }; - - diy1 { - label = "firefly:green:diy"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x159 0x08 0x00>; - }; - - diy2 { - label = "firefly:yellow:diy"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x91 0x11 0x00>; - }; - }; -}; \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.toml b/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.toml deleted file mode 100644 index 36313e6cd..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.toml +++ /dev/null @@ -1,62 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 2 -# The physical CPU ids. -phys_cpu_ids = [0x200, 0x300] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8008_0000 -# The location of image: "memory" | "fs". -# Load from memory. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x8008_0000 -## The file path of the kernel image. -kernel_path = "/userdata/rootfs_overlay/guest/linux/roc-rk3568-pc" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x8000_0000 -#dtb_path = "/code/axvisor/configs/vms/linux-aarch64-rk3568_smp2.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x6000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] -# -# Device specifications -# -[devices] -# The interrupt mode. -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.dts b/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.dts deleted file mode 100644 index 20bc1c3d4..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.dts +++ /dev/null @@ -1,8099 +0,0 @@ -/dts-v1/; - -/ { - compatible = "rockchip,rk3588-firefly-itx-3588j\0rockchip,rk3588"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Firefly ITX-3588J HDMI(Linux)"; - - aliases { - csi2dcphy0 = "/csi2-dcphy0"; - csi2dcphy1 = "/csi2-dcphy1"; - csi2dphy0 = "/csi2-dphy0"; - csi2dphy1 = "/csi2-dphy1"; - csi2dphy2 = "/csi2-dphy2"; - dsi0 = "/dsi@fde20000"; - dsi1 = "/dsi@fde30000"; - ethernet0 = "/ethernet@fe1b0000"; - ethernet1 = "/ethernet@fe1c0000"; - gpio0 = "/pinctrl/gpio@fd8a0000"; - gpio1 = "/pinctrl/gpio@fec20000"; - gpio2 = "/pinctrl/gpio@fec30000"; - gpio3 = "/pinctrl/gpio@fec40000"; - gpio4 = "/pinctrl/gpio@fec50000"; - i2c0 = "/i2c@fd880000"; - i2c1 = "/i2c@fea90000"; - i2c2 = "/i2c@feaa0000"; - i2c3 = "/i2c@feab0000"; - i2c4 = "/i2c@feac0000"; - i2c5 = "/i2c@fead0000"; - i2c6 = "/i2c@fec80000"; - i2c7 = "/i2c@fec90000"; - i2c8 = "/i2c@feca0000"; - rkcif_mipi_lvds0 = "/rkcif-mipi-lvds"; - rkcif_mipi_lvds1 = "/rkcif-mipi-lvds1"; - rkcif_mipi_lvds2 = "/rkcif-mipi-lvds2"; - rkcif_mipi_lvds3 = "/rkcif-mipi-lvds3"; - rkvenc0 = "/rkvenc-core@fdbd0000"; - rkvenc1 = "/rkvenc-core@fdbe0000"; - jpege0 = "/jpege-core@fdba0000"; - jpege1 = "/jpege-core@fdba4000"; - jpege2 = "/jpege-core@fdba8000"; - jpege3 = "/jpege-core@fdbac000"; - serial0 = "/serial@fd890000"; - serial1 = "/serial@feb40000"; - serial2 = "/serial@feb50000"; - serial3 = "/serial@feb60000"; - serial4 = "/serial@feb70000"; - serial5 = "/serial@feb80000"; - serial6 = "/serial@feb90000"; - serial7 = "/serial@feba0000"; - serial8 = "/serial@febb0000"; - serial9 = "/serial@febc0000"; - spi0 = "/spi@feb00000"; - spi1 = "/spi@feb10000"; - spi2 = "/spi@feb20000"; - spi3 = "/spi@feb30000"; - spi4 = "/spi@fecb0000"; - spi5 = "/spi@fe2b0000"; - hdcp0 = "/hdcp@fde40000"; - hdcp1 = "/hdcp@fde70000"; - pwm0 = "/pwm@fd8b0000"; - pwm1 = "/pwm@fd8b0010"; - pwm2 = "/pwm@fd8b0020"; - pwm3 = "/pwm@fd8b0030"; - pwm4 = "/pwm@febd0000"; - pwm5 = "/pwm@febd0010"; - pwm6 = "/pwm@febd0020"; - pwm7 = "/pwm@febd0030"; - pwm8 = "/pwm@febe0000"; - pwm9 = "/pwm@febe0010"; - pwm10 = "/pwm@febe0020"; - pwm11 = "/pwm@febe0030"; - pwm12 = "/pwm@febf0000"; - pwm13 = "/pwm@febf0010"; - pwm14 = "/pwm@febf0020"; - pwm15 = "/pwm@febf0030"; - csi2dphy3 = "/csi2-dphy3"; - csi2dphy4 = "/csi2-dphy4"; - csi2dphy5 = "/csi2-dphy5"; - dp0 = "/dp@fde50000"; - dp1 = "/dp@fde60000"; - edp0 = "/edp@fdec0000"; - edp1 = "/edp@fded0000"; - hdptx0 = "/phy@fed60000"; - hdptx1 = "/phy@fed70000"; - hdptxhdmi0 = "/hdmiphy@fed60000"; - hdptxhdmi1 = "/hdmiphy@fed70000"; - hdmi0 = "/hdmi@fde80000"; - hdmi1 = "/hdmi@fdea0000"; - hdmirx0 = "/hdmirx-controller@fdee0000"; - rkcif_mipi_lvds4 = "/rkcif-mipi-lvds4"; - rkcif_mipi_lvds5 = "/rkcif-mipi-lvds5"; - usbdp0 = "/phy@fed80000"; - usbdp1 = "/phy@fed90000"; - mmc0 = "/mmc@fe2e0000"; - mmc1 = "/mmc@fe2c0000"; - mmc2 = "/mmc@fe2d0000"; - }; - - clocks { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - spll { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x29d7ab80>; - clock-output-names = "spll"; - }; - - xin32k { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x8000>; - clock-output-names = "xin32k"; - }; - - xin24m { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x16e3600>; - clock-output-names = "xin24m"; - }; - - hclk_vo1@fd7c08ec { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08ec 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x264>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - phandle = <0x05>; - }; - - aclk_vdpu_low_pre@fd7c08b0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08b0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1bc>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_vo0@fd7c08dc { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08dc 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x26d>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - phandle = <0x04>; - }; - - hclk_usb@fd7c08a8 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a8 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x264>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_nvm@fd7c087c { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c087c 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x141>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - phandle = <0x03>; - }; - - aclk_usb@fd7c08a8 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a8 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x263>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_isp1_pre@fd7c0868 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c0868 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1e1>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_isp1_pre@fd7c0868 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c0868 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1e0>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_rkvdec0_pre@fd7c08a0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1bc>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_rkvdec0_pre@fd7c08a0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1be>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_rkvdec1_pre@fd7c08a4 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a4 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1bc>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_rkvdec1_pre@fd7c08a4 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a4 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1be>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_jpeg_decoder_pre@fd7c08b0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08b0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1bc>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_rkvenc1_pre@fd7c08c0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08c0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1c5>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_rkvenc1_pre@fd7c08c0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08c0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1c4>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_hdcp0_pre@fd7c08dc { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08dc 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x26c>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_hdcp1_pre@fd7c08ec { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08ec 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x263>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - pclk_av1_pre@fd7c0910 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c0910 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1be>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_av1_pre@fd7c0910 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c0910 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1bc>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_sdio_pre@fd7c092c { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c092c 0x00 0x10>; - clock-names = "link"; - clocks = <0x03>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - pclk_vo0_grf@fd7c08dc { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08dc 0x00 0x04>; - clocks = <0x04>; - clock-names = "link"; - #clock-cells = <0x00>; - phandle = <0x5d>; - }; - - pclk_vo1_grf@fd7c08ec { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08ec 0x00 0x04>; - clocks = <0x05>; - clock-names = "link"; - #clock-cells = <0x00>; - phandle = <0x5e>; - }; - }; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - - cpu-map { - - cluster0 { - - core0 { - cpu = <0x06>; - }; - - core1 { - cpu = <0x07>; - }; - - core2 { - cpu = <0x08>; - }; - - core3 { - cpu = <0x09>; - }; - }; - - cluster1 { - - core0 { - cpu = <0x0a>; - }; - - core1 { - cpu = <0x0b>; - }; - }; - - cluster2 { - - core0 { - cpu = <0x0c>; - }; - - core1 { - cpu = <0x0d>; - }; - }; - }; - - cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00>; - enable-method = "psci"; - capacity-dmips-mhz = <0x212>; - clocks = <0x0e 0x00>; - operating-points-v2 = <0x0f>; - cpu-idle-states = <0x10>; - i-cache-size = <0x8000>; - i-cache-line-size = <0x40>; - i-cache-sets = <0x80>; - d-cache-size = <0x8000>; - d-cache-line-size = <0x40>; - d-cache-sets = <0x80>; - next-level-cache = <0x11>; - #cooling-cells = <0x02>; - dynamic-power-coefficient = <0x64>; - cpu-supply = <0x12>; - mem-supply = <0x12>; - phandle = <0x06>; - }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x100>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x212>; - // clocks = <0x0e 0x00>; - // operating-points-v2 = <0x0f>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x8000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x80>; - // d-cache-size = <0x8000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x80>; - // next-level-cache = <0x13>; - // phandle = <0x07>; - // }; - - // cpu@200 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x200>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x212>; - // clocks = <0x0e 0x00>; - // operating-points-v2 = <0x0f>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x8000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x80>; - // d-cache-size = <0x8000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x80>; - // next-level-cache = <0x14>; - // phandle = <0x08>; - // }; - - // cpu@300 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x300>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x212>; - // clocks = <0x0e 0x00>; - // operating-points-v2 = <0x0f>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x8000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x80>; - // d-cache-size = <0x8000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x80>; - // next-level-cache = <0x15>; - // phandle = <0x09>; - // }; - - // cpu@400 { - // device_type = "cpu"; - // compatible = "arm,cortex-a76"; - // reg = <0x400>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x400>; - // clocks = <0x0e 0x02>; - // operating-points-v2 = <0x16>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x10000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x100>; - // d-cache-size = <0x10000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x100>; - // next-level-cache = <0x17>; - // #cooling-cells = <0x02>; - // dynamic-power-coefficient = <0x12c>; - // cpu-supply = <0x18>; - // mem-supply = <0x18>; - // phandle = <0x0a>; - // }; - - // cpu@500 { - // device_type = "cpu"; - // compatible = "arm,cortex-a76"; - // reg = <0x500>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x400>; - // clocks = <0x0e 0x02>; - // operating-points-v2 = <0x16>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x10000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x100>; - // d-cache-size = <0x10000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x100>; - // next-level-cache = <0x19>; - // phandle = <0x0b>; - // }; - - // cpu@600 { - // device_type = "cpu"; - // compatible = "arm,cortex-a76"; - // reg = <0x600>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x400>; - // clocks = <0x0e 0x03>; - // operating-points-v2 = <0x1a>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x10000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x100>; - // d-cache-size = <0x10000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x100>; - // next-level-cache = <0x1b>; - // #cooling-cells = <0x02>; - // dynamic-power-coefficient = <0x12c>; - // cpu-supply = <0x1c>; - // mem-supply = <0x1c>; - // phandle = <0x0c>; - // }; - - // cpu@700 { - // device_type = "cpu"; - // compatible = "arm,cortex-a76"; - // reg = <0x700>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x400>; - // clocks = <0x0e 0x03>; - // operating-points-v2 = <0x1a>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x10000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x100>; - // d-cache-size = <0x10000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x100>; - // next-level-cache = <0x1d>; - // phandle = <0x0d>; - // }; - - l2-cache-l0 { - compatible = "cache"; - cache-size = <0x20000>; - cache-line-size = <0x40>; - cache-sets = <0x200>; - next-level-cache = <0x1e>; - phandle = <0x11>; - }; - - l2-cache-l1 { - compatible = "cache"; - cache-size = <0x20000>; - cache-line-size = <0x40>; - cache-sets = <0x200>; - next-level-cache = <0x1e>; - phandle = <0x13>; - }; - - l2-cache-l2 { - compatible = "cache"; - cache-size = <0x20000>; - cache-line-size = <0x40>; - cache-sets = <0x200>; - next-level-cache = <0x1e>; - phandle = <0x14>; - }; - - l2-cache-l3 { - compatible = "cache"; - cache-size = <0x20000>; - cache-line-size = <0x40>; - cache-sets = <0x200>; - next-level-cache = <0x1e>; - phandle = <0x15>; - }; - - l2-cache-b0 { - compatible = "cache"; - cache-size = <0x80000>; - cache-line-size = <0x40>; - cache-sets = <0x400>; - next-level-cache = <0x1e>; - phandle = <0x17>; - }; - - l2-cache-b1 { - compatible = "cache"; - cache-size = <0x80000>; - cache-line-size = <0x40>; - cache-sets = <0x400>; - next-level-cache = <0x1e>; - phandle = <0x19>; - }; - - l2-cache-b2 { - compatible = "cache"; - cache-size = <0x80000>; - cache-line-size = <0x40>; - cache-sets = <0x400>; - next-level-cache = <0x1e>; - phandle = <0x1b>; - }; - - l2-cache-b3 { - compatible = "cache"; - cache-size = <0x80000>; - cache-line-size = <0x40>; - cache-sets = <0x400>; - next-level-cache = <0x1e>; - phandle = <0x1d>; - }; - - l3-cache { - compatible = "cache"; - cache-size = <0x300000>; - cache-line-size = <0x40>; - cache-sets = <0x1000>; - phandle = <0x1e>; - }; - }; - - cluster0-opp-table { - compatible = "operating-points-v2"; - opp-shared; - nvmem-cells = <0x1f 0x20>; - nvmem-cell-names = "leakage\0specification_serial_number"; - rockchip,supported-hw; - rockchip,opp-shared-dsu; - rockchip,pvtm-voltage-sel = <0x00 0x582 0x00 0x583 0x59a 0x01 0x59b 0x5b2 0x02 0x5b3 0x5ca 0x03 0x5cb 0x5e2 0x04 0x5e3 0x5fa 0x05 0x5fb 0x270f 0x06>; - rockchip,pvtm-pvtpll; - rockchip,pvtm-offset = <0x64>; - rockchip,pvtm-sample-time = <0x44c>; - rockchip,pvtm-freq = <0x159b40>; - rockchip,pvtm-volt = <0xb71b0>; - rockchip,pvtm-ref-temp = <0x19>; - rockchip,pvtm-temp-prop = <0xf4 0xf4>; - rockchip,pvtm-thermal-zone = "soc-thermal"; - rockchip,grf = <0x21>; - rockchip,dsu-grf = <0x22>; - volt-mem-read-margin = <0xd0bd8 0x01 0xbac48 0x02 0xa4cb8 0x03 0x78d98 0x04>; - low-volt-mem-read-margin = <0x04>; - intermediate-threshold-freq = <0xf6180>; - rockchip,reboot-freq = <0x159b40>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x2710>; - rockchip,low-temp-min-volt = <0xb71b0>; - rockchip,high-temp = <0x14c08>; - rockchip,high-temp-max-freq = <0x188940>; - phandle = <0x0f>; - - opp-408000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x18519600>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-600000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-816000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x30a32c00>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-1008000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x3c14dc00>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-1200000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x47868c00>; - opp-microvolt = <0xadf34 0xadf34 0xe7ef0 0xadf34 0xadf34 0xe7ef0>; - opp-microvolt-L1 = <0xaae60 0xaae60 0xe7ef0 0xaae60 0xaae60 0xe7ef0>; - opp-microvolt-L2 = <0xaae60 0xaae60 0xe7ef0 0xaae60 0xaae60 0xe7ef0>; - opp-microvolt-L3 = <0xa7d8c 0xa7d8c 0xe7ef0 0xa7d8c 0xa7d8c 0xe7ef0>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - opp-microvolt-L6 = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-1416000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x54667200>; - opp-microvolt = <0xba284 0xba284 0xe7ef0 0xba284 0xba284 0xe7ef0>; - opp-microvolt-L1 = <0xb71b0 0xb71b0 0xe7ef0 0xb71b0 0xb71b0 0xe7ef0>; - opp-microvolt-L2 = <0xb40dc 0xb40dc 0xe7ef0 0xb40dc 0xb40dc 0xe7ef0>; - opp-microvolt-L3 = <0xb1008 0xb1008 0xe7ef0 0xb1008 0xb1008 0xe7ef0>; - opp-microvolt-L4 = <0xb1008 0xb1008 0xe7ef0 0xb1008 0xb1008 0xe7ef0>; - opp-microvolt-L5 = <0xadf34 0xadf34 0xe7ef0 0xadf34 0xadf34 0xe7ef0>; - opp-microvolt-L6 = <0xadf34 0xadf34 0xe7ef0 0xadf34 0xadf34 0xe7ef0>; - clock-latency-ns = <0x9c40>; - opp-suspend; - }; - - opp-1608000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x5fd82200>; - opp-microvolt = <0xcf850 0xcf850 0xe7ef0 0xcf850 0xcf850 0xe7ef0>; - opp-microvolt-L1 = <0xcc77c 0xcc77c 0xe7ef0 0xcc77c 0xcc77c 0xe7ef0>; - opp-microvolt-L2 = <0xc96a8 0xc96a8 0xe7ef0 0xc96a8 0xc96a8 0xe7ef0>; - opp-microvolt-L3 = <0xc65d4 0xc65d4 0xe7ef0 0xc65d4 0xc65d4 0xe7ef0>; - opp-microvolt-L4 = <0xc3500 0xc3500 0xe7ef0 0xc3500 0xc3500 0xe7ef0>; - opp-microvolt-L5 = <0xc3500 0xc3500 0xe7ef0 0xc3500 0xc3500 0xe7ef0>; - opp-microvolt-L6 = <0xc042c 0xc042c 0xe7ef0 0xc042c 0xc042c 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-1704000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x6590fa00>; - opp-microvolt = <0xdbba0 0xdbba0 0xe7ef0 0xdbba0 0xdbba0 0xe7ef0>; - opp-microvolt-L1 = <0xd8acc 0xd8acc 0xe7ef0 0xd8acc 0xd8acc 0xe7ef0>; - opp-microvolt-L2 = <0xd59f8 0xd59f8 0xe7ef0 0xd59f8 0xd59f8 0xe7ef0>; - opp-microvolt-L3 = <0xd2924 0xd2924 0xe7ef0 0xd2924 0xd2924 0xe7ef0>; - opp-microvolt-L4 = <0xcf850 0xcf850 0xe7ef0 0xcf850 0xcf850 0xe7ef0>; - opp-microvolt-L5 = <0xcc77c 0xcc77c 0xe7ef0 0xcc77c 0xcc77c 0xe7ef0>; - opp-microvolt-L6 = <0xc96a8 0xc96a8 0xe7ef0 0xc96a8 0xc96a8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-1800000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x6b49d200>; - opp-microvolt = <0xe7ef0 0xe7ef0 0xe7ef0 0xe7ef0 0xe7ef0 0xe7ef0>; - opp-microvolt-L1 = <0xe4e1c 0xe4e1c 0xe7ef0 0xe4e1c 0xe4e1c 0xe7ef0>; - opp-microvolt-L2 = <0xe1d48 0xe1d48 0xe7ef0 0xe1d48 0xe1d48 0xe7ef0>; - opp-microvolt-L3 = <0xdec74 0xdec74 0xe7ef0 0xdec74 0xdec74 0xe7ef0>; - opp-microvolt-L4 = <0xdbba0 0xdbba0 0xe7ef0 0xdbba0 0xdbba0 0xe7ef0>; - opp-microvolt-L5 = <0xd8acc 0xd8acc 0xe7ef0 0xd8acc 0xd8acc 0xe7ef0>; - opp-microvolt-L6 = <0xd59f8 0xd59f8 0xe7ef0 0xd59f8 0xd59f8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - }; - - arm-pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0x01 0x07 0x08>; - interrupt-affinity = <0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d>; - }; - - cpuinfo { - compatible = "rockchip,cpuinfo"; - nvmem-cells = <0x27 0x28 0x29>; - nvmem-cell-names = "id\0cpu-version\0cpu-code"; - }; - - csi2-dcphy0 { - compatible = "rockchip,rk3588-csi2-dcphy"; - phys = <0x2a>; - phy-names = "dcphy"; - status = "disabled"; - }; - - csi2-dcphy1 { - compatible = "rockchip,rk3588-csi2-dcphy"; - phys = <0x2b>; - phy-names = "dcphy"; - status = "disabled"; - }; - - csi2-dphy0 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x2c>; - status = "disabled"; - }; - - csi2-dphy1 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x2c>; - status = "disabled"; - }; - - csi2-dphy2 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x2c>; - status = "disabled"; - }; - - display-subsystem { - compatible = "rockchip,display-subsystem"; - ports = <0x2d>; - clocks = <0x2e 0x2f>; - clock-names = "hdmi0_phy_pll\0hdmi1_phy_pll"; - memory-region = <0x30>; - memory-region-names = "drm-logo"; - - route { - - route-dp0 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x31>; - }; - - route-dsi0 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x32>; - }; - - route-dsi1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x33>; - }; - - route-edp0 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x34>; - }; - - route-edp1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - }; - - route-hdmi0 { - status = "okay"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x35>; - }; - - route-rgb { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x36>; - }; - - route-dp1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x37>; - }; - - route-hdmi1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x38>; - }; - }; - }; - - dmc { - compatible = "rockchip,rk3588-dmc"; - interrupts = <0x00 0x49 0x04>; - interrupt-names = "complete"; - devfreq-events = <0x39>; - clocks = <0x0e 0x04>; - clock-names = "dmc_clk"; - operating-points-v2 = <0x3a>; - upthreshold = <0x28>; - downdifferential = <0x14>; - system-status-level = <0x01 0x04 0x08 0x08 0x02 0x01 0x10 0x04 0x10000 0x04 0x80000 0x04 0x1000 0x08 0x4000 0x08 0x2000 0x08 0xc00 0x08 0x40000 0x08>; - auto-freq-en = <0x01>; - status = "okay"; - center-supply = <0x3b>; - mem-supply = <0x3c>; - }; - - dmc-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x3d>; - nvmem-cell-names = "leakage"; - rockchip,leakage-voltage-sel = <0x01 0x1f 0x00 0x20 0x2c 0x01 0x2d 0x39 0x02 0x3a 0xfe 0x03>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x2710>; - rockchip,low-temp-min-volt = <0xb71b0>; - phandle = <0x3a>; - - opp-528000000 { - opp-hz = <0x00 0x1f78a400>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xd59f8 0xb1008 0xb1008 0xb71b0>; - opp-microvolt-L1 = <0xa4cb8 0xa4cb8 0xd59f8 0xaae60 0xaae60 0xb71b0>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xd59f8 0xa7d8c 0xa7d8c 0xb71b0>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xd59f8 0xa4cb8 0xa4cb8 0xb71b0>; - }; - - opp-1068000000 { - opp-hz = <0x00 0x3fa86300>; - opp-microvolt = <0xb1008 0xb1008 0xd59f8 0xb40dc 0xb40dc 0xb71b0>; - opp-microvolt-L1 = <0xaae60 0xaae60 0xd59f8 0xadf34 0xadf34 0xb71b0>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xd59f8 0xaae60 0xaae60 0xb71b0>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xd59f8 0xa7d8c 0xa7d8c 0xb71b0>; - }; - - opp-1560000000 { - opp-hz = <0x00 0x5cfbb600>; - opp-microvolt = <0xc3500 0xc3500 0xd59f8 0xb71b0 0xb71b0 0xb71b0>; - opp-microvolt-L1 = <0xbd358 0xbd358 0xd59f8 0xb1008 0xb1008 0xb71b0>; - opp-microvolt-L2 = <0xb71b0 0xb71b0 0xd59f8 0xadf34 0xadf34 0xb71b0>; - opp-microvolt-L3 = <0xb1008 0xb1008 0xd59f8 0xaae60 0xaae60 0xb71b0>; - }; - - opp-2750000000 { - opp-hz = <0x00 0xa3e9ab80>; - opp-microvolt = <0xd59f8 0xd59f8 0xd59f8 0xb71b0 0xb71b0 0xb71b0>; - opp-microvolt-L1 = <0xcf850 0xcf850 0xd59f8 0xb71b0 0xb71b0 0xb71b0>; - opp-microvolt-L2 = <0xcc77c 0xcc77c 0xd59f8 0xb1008 0xb1008 0xb71b0>; - opp-microvolt-L3 = <0xc96a8 0xc8320 0xd59f8 0xaae60 0xaae60 0xb71b0>; - }; - }; - - firmware { - - scmi { - compatible = "arm,scmi-smc"; - shmem = <0x3e>; - arm,smc-id = <0x82000010>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@14 { - reg = <0x14>; - #clock-cells = <0x01>; - assigned-clocks = <0x0e 0x00 0x0e 0x02 0x0e 0x03>; - assigned-clock-rates = <0x30a32c00 0x30a32c00 0x30a32c00>; - phandle = <0x0e>; - }; - - protocol@16 { - reg = <0x16>; - #reset-cells = <0x01>; - phandle = <0x104>; - }; - }; - - sdei { - compatible = "arm,sdei-1.0"; - method = "smc"; - }; - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - }; - }; - - jpege-ccu { - compatible = "rockchip,vpu-jpege-ccu"; - status = "okay"; - phandle = <0xa4>; - }; - - mpp-srv { - compatible = "rockchip,mpp-service"; - rockchip,taskqueue-count = <0x0c>; - rockchip,resetgroup-count = <0x01>; - status = "okay"; - phandle = <0x9f>; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - }; - - rkcif-dvp { - compatible = "rockchip,rkcif-dvp"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x41>; - }; - - rkcif-dvp-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x41>; - status = "disabled"; - }; - - rkcif-mipi-lvds { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x42>; - }; - - rkcif-mipi-lvds-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x42>; - status = "disabled"; - }; - - rkcif-mipi-lvds-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x42>; - status = "disabled"; - }; - - rkcif-mipi-lvds-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x42>; - status = "disabled"; - }; - - rkcif-mipi-lvds-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x42>; - status = "disabled"; - }; - - rkcif-mipi-lvds1 { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x43>; - }; - - rkcif-mipi-lvds1-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x43>; - status = "disabled"; - }; - - rkcif-mipi-lvds1-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x43>; - status = "disabled"; - }; - - rkcif-mipi-lvds1-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x43>; - status = "disabled"; - }; - - rkcif-mipi-lvds1-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x43>; - status = "disabled"; - }; - - rkcif-mipi-lvds2 { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x44>; - }; - - rkcif-mipi-lvds2-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x44>; - status = "disabled"; - }; - - rkcif-mipi-lvds2-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x44>; - status = "disabled"; - }; - - rkcif-mipi-lvds2-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x44>; - status = "disabled"; - }; - - rkcif-mipi-lvds2-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x44>; - status = "disabled"; - }; - - rkcif-mipi-lvds3 { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x45>; - }; - - rkcif-mipi-lvds3-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x45>; - status = "disabled"; - }; - - rkcif-mipi-lvds3-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x45>; - status = "disabled"; - }; - - rkcif-mipi-lvds3-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x45>; - status = "disabled"; - }; - - rkcif-mipi-lvds3-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x45>; - status = "disabled"; - }; - - rkisp0-vir0 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x46>; - status = "disabled"; - }; - - rkisp0-vir1 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x46>; - status = "disabled"; - }; - - rkisp0-vir2 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x46>; - status = "disabled"; - }; - - rkisp0-vir3 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x46>; - status = "disabled"; - }; - - rkisp1-vir0 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x47>; - status = "disabled"; - }; - - rkisp1-vir1 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x47>; - status = "disabled"; - }; - - rkisp1-vir2 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x47>; - status = "disabled"; - }; - - rkisp1-vir3 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x47>; - status = "disabled"; - }; - - rkispp0-vir0 { - compatible = "rockchip,rk3588-rkispp-vir"; - rockchip,hw = <0x48>; - status = "disabled"; - }; - - rkispp1-vir0 { - compatible = "rockchip,rk3588-rkispp-vir"; - rockchip,hw = <0x49>; - status = "disabled"; - }; - - rkvenc-ccu { - compatible = "rockchip,rkv-encoder-v2-ccu"; - status = "okay"; - phandle = <0xaa>; - }; - - rockchip-suspend { - compatible = "rockchip,pm-rk3588"; - status = "okay"; - rockchip,sleep-debug-en = <0x01>; - rockchip,sleep-mode-config = <0x04>; - rockchip,wakeup-config = <0x900>; - }; - - rockchip-system-monitor { - compatible = "rockchip,system-monitor"; - rockchip,thermal-zone = "soc-thermal"; - }; - - thermal-zones { - - soc-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - sustainable-power = <0x834>; - thermal-sensors = <0x4a 0x00>; - - trips { - - trip-point-0 { - temperature = <0x124f8>; - hysteresis = <0x7d0>; - type = "passive"; - }; - - trip-point-1 { - temperature = <0x14c08>; - hysteresis = <0x7d0>; - type = "passive"; - phandle = <0x4b>; - }; - - soc-crit { - temperature = <0x1c138>; - hysteresis = <0x7d0>; - type = "critical"; - }; - }; - - cooling-maps { - - map0 { - trip = <0x4b>; - cooling-device = <0x06 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - - map3 { - trip = <0x4b>; - cooling-device = <0x4c 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - }; - }; - - bigcore0-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x01>; - }; - - bigcore1-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x02>; - }; - - littlecore-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x03>; - }; - - center-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x04>; - }; - - gpu-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x05>; - }; - - npu-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x06>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; - }; - - sram@10f000 { - compatible = "mmio-sram"; - reg = <0x00 0x10f000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x10f000 0x100>; - - sram@0 { - compatible = "arm,scmi-shmem"; - reg = <0x00 0x100>; - phandle = <0x3e>; - }; - }; - - gpu@fb000000 { - compatible = "arm,mali-bifrost"; - reg = <0x00 0xfb000000 0x00 0x200000>; - interrupts = <0x00 0x5e 0x04 0x00 0x5d 0x04 0x00 0x5c 0x04>; - interrupt-names = "GPU\0MMU\0JOB"; - clocks = <0x0e 0x05 0x02 0x115 0x02 0x116 0x02 0x114>; - clock-names = "clk_mali\0clk_gpu_coregroup\0clk_gpu_stacks\0clk_gpu"; - assigned-clocks = <0x0e 0x05>; - assigned-clock-rates = <0xbebc200>; - power-domains = <0x4d 0x0c>; - operating-points-v2 = <0x4e>; - #cooling-cells = <0x02>; - dynamic-power-coefficient = <0xba6>; - upthreshold = <0x1e>; - downdifferential = <0x0a>; - status = "okay"; - mali-supply = <0x4f>; - mem-supply = <0x4f>; - phandle = <0x4c>; - }; - - gpu-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x50 0x20>; - nvmem-cell-names = "leakage\0specification_serial_number"; - rockchip,supported-hw; - rockchip,pvtm-voltage-sel = <0x00 0x32f 0x00 0x330 0x343 0x01 0x344 0x35c 0x02 0x35d 0x375 0x03 0x376 0x38e 0x04 0x38f 0x270f 0x05>; - rockchip,pvtm-pvtpll; - rockchip,pvtm-offset = <0x1c>; - rockchip,pvtm-freq = <0xc3500>; - rockchip,pvtm-sample-time = <0x44c>; - rockchip,pvtm-volt = <0xb71b0>; - rockchip,pvtm-ref-temp = <0x19>; - rockchip,pvtm-temp-prop = <0xffffff79 0xffffff79>; - rockchip,pvtm-thermal-zone = "gpu-thermal"; - clocks = <0x02 0x114>; - clock-names = "clk"; - rockchip,grf = <0x51>; - volt-mem-read-margin = <0xd0bd8 0x01 0xbac48 0x02 0xa4cb8 0x03 0x78d98 0x04>; - low-volt-mem-read-margin = <0x04>; - intermediate-threshold-freq = <0x61a80>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x2710>; - rockchip,low-temp-min-volt = <0xb71b0>; - rockchip,high-temp = <0x14c08>; - rockchip,high-temp-max-freq = <0xc3500>; - phandle = <0x4e>; - - opp-300000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x11e1a300>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-400000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-500000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x1dcd6500>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-600000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-700000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L1 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-800000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xb71b0 0xb71b0 0xcf850 0xb71b0 0xb71b0 0xcf850>; - opp-microvolt-L1 = <0xb40dc 0xb40dc 0xcf850 0xb40dc 0xb40dc 0xcf850>; - opp-microvolt-L2 = <0xb1008 0xb1008 0xcf850 0xb1008 0xb1008 0xcf850>; - opp-microvolt-L3 = <0xadf34 0xadf34 0xcf850 0xadf34 0xadf34 0xcf850>; - opp-microvolt-L4 = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - }; - - opp-900000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xc3500 0xc3500 0xcf850 0xc3500 0xc3500 0xcf850>; - opp-microvolt-L1 = <0xc042c 0xc042c 0xcf850 0xc042c 0xc042c 0xcf850>; - opp-microvolt-L2 = <0xbd358 0xbd358 0xcf850 0xbd358 0xbd358 0xcf850>; - opp-microvolt-L3 = <0xba284 0xba284 0xcf850 0xba284 0xba284 0xcf850>; - opp-microvolt-L4 = <0xb71b0 0xb71b0 0xcf850 0xb71b0 0xb71b0 0xcf850>; - }; - - opp-1000000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xcf850 0xcf850 0xcf850 0xcf850 0xcf850 0xcf850>; - opp-microvolt-L1 = <0xcc77c 0xcc77c 0xcf850 0xcc77c 0xcc77c 0xcf850>; - opp-microvolt-L2 = <0xc96a8 0xc96a8 0xcf850 0xc96a8 0xc96a8 0xcf850>; - opp-microvolt-L3 = <0xc65d4 0xc65d4 0xcf850 0xc65d4 0xc65d4 0xcf850>; - opp-microvolt-L4 = <0xc3500 0xc3500 0xcf850 0xc3500 0xc3500 0xcf850>; - }; - }; - - usbdrd3_0 { - compatible = "rockchip,rk3588-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x02 0x1a3 0x02 0x1a2 0x02 0x1a1>; - clock-names = "ref\0suspend\0bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - usb@fc000000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfc000000 0x00 0x400000>; - interrupts = <0x00 0xdc 0x04>; - power-domains = <0x4d 0x1f>; - resets = <0x02 0x2a4>; - reset-names = "usb3-otg"; - dr_mode = "host"; - phys = <0x52 0x53>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - snps,dis_enblslpm_quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,parkmode-disable-ss-quirk; - status = "okay"; - usb-role-switch; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x54>; - phandle = <0x163>; - }; - }; - }; - }; - - usb@fc800000 { - compatible = "rockchip,rk3588-ehci\0generic-ehci"; - reg = <0x00 0xfc800000 0x00 0x40000>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x02 0x19d 0x02 0x19e 0x55>; - clock-names = "usbhost\0arbiter\0utmi"; - companion = <0x56>; - phys = <0x57>; - phy-names = "usb2-phy"; - power-domains = <0x4d 0x1f>; - status = "okay"; - }; - - usb@fc840000 { - compatible = "generic-ohci"; - reg = <0x00 0xfc840000 0x00 0x40000>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x02 0x19d 0x02 0x19e 0x55>; - clock-names = "usbhost\0arbiter\0utmi"; - phys = <0x57>; - phy-names = "usb2-phy"; - power-domains = <0x4d 0x1f>; - status = "okay"; - phandle = <0x56>; - }; - - usb@fc880000 { - compatible = "rockchip,rk3588-ehci\0generic-ehci"; - reg = <0x00 0xfc880000 0x00 0x40000>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x02 0x19f 0x02 0x1a0 0x58>; - clock-names = "usbhost\0arbiter\0utmi"; - companion = <0x59>; - phys = <0x5a>; - phy-names = "usb2-phy"; - power-domains = <0x4d 0x1f>; - status = "okay"; - }; - - usb@fc8c0000 { - compatible = "generic-ohci"; - reg = <0x00 0xfc8c0000 0x00 0x40000>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x02 0x19f 0x02 0x1a0 0x58>; - clock-names = "usbhost\0arbiter\0utmi"; - phys = <0x5a>; - phy-names = "usb2-phy"; - power-domains = <0x4d 0x1f>; - status = "okay"; - phandle = <0x59>; - }; - - iommu@fc900000 { - compatible = "arm,smmu-v3"; - reg = <0x00 0xfc900000 0x00 0x200000>; - interrupts = <0x00 0x171 0x04 0x00 0x173 0x04 0x00 0x176 0x04 0x00 0x16f 0x04>; - interrupt-names = "eventq\0gerror\0priq\0cmdq-sync"; - #iommu-cells = <0x01>; - status = "disabled"; - }; - - iommu@fcb00000 { - compatible = "arm,smmu-v3"; - reg = <0x00 0xfcb00000 0x00 0x200000>; - interrupts = <0x00 0x17d 0x04 0x00 0x17f 0x04 0x00 0x182 0x04 0x00 0x17b 0x04>; - interrupt-names = "eventq\0gerror\0priq\0cmdq-sync"; - #iommu-cells = <0x01>; - status = "disabled"; - }; - - usbhost3_0 { - compatible = "rockchip,rk3588-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x02 0x179 0x02 0x178 0x02 0x177 0x02 0x17a 0x02 0x166 0x02 0x181>; - clock-names = "ref\0suspend\0bus\0utmi\0php\0pipe"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "disabled"; - - usb@fcd00000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfcd00000 0x00 0x400000>; - interrupts = <0x00 0xde 0x04>; - resets = <0x02 0x237>; - reset-names = "usb3-host"; - dr_mode = "host"; - phys = <0x5b 0x04>; - phy-names = "usb3-phy"; - phy_type = "utmi_wide"; - snps,dis_enblslpm_quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,dis_rxdet_inp3_quirk; - snps,parkmode-disable-ss-quirk; - status = "disabled"; - }; - }; - - syscon@fd588000 { - compatible = "rockchip,rk3588-pmu0-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd588000 0x00 0x2000>; - - reboot-mode { - compatible = "syscon-reboot-mode"; - offset = <0x80>; - mode-bootloader = <0x5242c301>; - mode-charge = <0x5242c30b>; - mode-fastboot = <0x5242c309>; - mode-loader = <0x5242c301>; - mode-normal = <0x5242c300>; - mode-recovery = <0x5242c303>; - mode-ums = <0x5242c30c>; - mode-panic = <0x5242c307>; - mode-watchdog = <0x5242c308>; - }; - }; - - syscon@fd58a000 { - compatible = "rockchip,rk3588-pmu1-grf\0syscon"; - reg = <0x00 0xfd58a000 0x00 0x2000>; - phandle = <0xe6>; - }; - - syscon@fd58c000 { - compatible = "rockchip,rk3588-sys-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd58c000 0x00 0x1000>; - phandle = <0xb7>; - - rgb { - compatible = "rockchip,rk3588-rgb"; - pinctrl-names = "default"; - pinctrl-0 = <0x5c>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x36>; - status = "disabled"; - phandle = <0xd2>; - }; - }; - }; - }; - }; - - syscon@fd590000 { - compatible = "rockchip,rk3588-bigcore0-grf\0syscon"; - reg = <0x00 0xfd590000 0x00 0x100>; - phandle = <0x24>; - }; - - syscon@fd592000 { - compatible = "rockchip,rk3588-bigcore1-grf\0syscon"; - reg = <0x00 0xfd592000 0x00 0x100>; - phandle = <0x26>; - }; - - syscon@fd594000 { - compatible = "rockchip,rk3588-litcore-grf\0syscon"; - reg = <0x00 0xfd594000 0x00 0x100>; - phandle = <0x21>; - }; - - syscon@fd598000 { - compatible = "rockchip,rk3588-dsu-grf\0syscon"; - reg = <0x00 0xfd598000 0x00 0x100>; - phandle = <0x22>; - }; - - syscon@fd5a0000 { - compatible = "rockchip,rk3588-gpu-grf\0syscon"; - reg = <0x00 0xfd5a0000 0x00 0x100>; - phandle = <0x51>; - }; - - syscon@fd5a2000 { - compatible = "rockchip,rk3588-npu-grf\0syscon"; - reg = <0x00 0xfd5a2000 0x00 0x100>; - phandle = <0x9d>; - }; - - syscon@fd5a4000 { - compatible = "rockchip,rk3588-vop-grf\0syscon"; - reg = <0x00 0xfd5a4000 0x00 0x2000>; - phandle = <0xb9>; - }; - - syscon@fd5a6000 { - compatible = "rockchip,rk3588-vo-grf\0syscon"; - reg = <0x00 0xfd5a6000 0x00 0x2000>; - clocks = <0x5d>; - phandle = <0xd7>; - }; - - syscon@fd5a8000 { - compatible = "rockchip,rk3588-vo-grf\0syscon"; - reg = <0x00 0xfd5a8000 0x00 0x100>; - clocks = <0x5e>; - phandle = <0xba>; - }; - - syscon@fd5ac000 { - compatible = "rockchip,rk3588-usb-grf\0syscon"; - reg = <0x00 0xfd5ac000 0x00 0x4000>; - phandle = <0x5f>; - }; - - syscon@fd5b0000 { - compatible = "rockchip,rk3588-php-grf\0syscon"; - reg = <0x00 0xfd5b0000 0x00 0x1000>; - phandle = <0x61>; - }; - - syscon@fd5b4000 { - compatible = "rockchip,mipi-dphy-grf\0syscon"; - reg = <0x00 0xfd5b4000 0x00 0x1000>; - phandle = <0x173>; - }; - - syscon@fd5b5000 { - compatible = "rockchip,mipi-dphy-grf\0syscon"; - reg = <0x00 0xfd5b5000 0x00 0x1000>; - phandle = <0x1a7>; - }; - - syscon@fd5bc000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfd5bc000 0x00 0x100>; - phandle = <0x174>; - }; - - syscon@fd5c4000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfd5c4000 0x00 0x100>; - phandle = <0x175>; - }; - - syscon@fd5c8000 { - compatible = "rockchip,rk3588-usbdpphy-grf\0syscon"; - reg = <0x00 0xfd5c8000 0x00 0x4000>; - phandle = <0x16d>; - }; - - syscon@fd5d0000 { - compatible = "rockchip,rk3588-usb2phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd5d0000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - phandle = <0x16c>; - - usb2-phy@0 { - compatible = "rockchip,rk3588-usb2phy"; - reg = <0x00 0x10>; - interrupts = <0x00 0x189 0x04>; - resets = <0x02 0xc0047 0x02 0x488>; - reset-names = "phy\0apb"; - clocks = <0x02 0x2b5>; - clock-names = "phyclk"; - clock-output-names = "usb480m_phy0"; - #clock-cells = <0x00>; - rockchip,usbctrl-grf = <0x5f>; - status = "okay"; - phandle = <0x16e>; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - rockchip,typec-vbus-det; - phandle = <0x52>; - }; - }; - }; - - syscon@fd5d8000 { - compatible = "rockchip,rk3588-usb2phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd5d8000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - - usb2-phy@8000 { - compatible = "rockchip,rk3588-usb2phy"; - reg = <0x8000 0x10>; - interrupts = <0x00 0x187 0x04>; - resets = <0x02 0xc0049 0x02 0x48a>; - reset-names = "phy\0apb"; - clocks = <0x02 0x2b5>; - clock-names = "phyclk"; - clock-output-names = "usb480m_phy2"; - #clock-cells = <0x00>; - status = "okay"; - phandle = <0x55>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x60>; - phandle = <0x57>; - }; - }; - }; - - syscon@fd5dc000 { - compatible = "rockchip,rk3588-usb2phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd5dc000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - - usb2-phy@c000 { - compatible = "rockchip,rk3588-usb2phy"; - reg = <0xc000 0x10>; - interrupts = <0x00 0x188 0x04>; - resets = <0x02 0xc004a 0x02 0x48b>; - reset-names = "phy\0apb"; - clocks = <0x02 0x2b5>; - clock-names = "phyclk"; - clock-output-names = "usb480m_phy3"; - #clock-cells = <0x00>; - status = "okay"; - phandle = <0x58>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x60>; - phandle = <0x5a>; - }; - }; - }; - - syscon@fd5e0000 { - compatible = "rockchip,rk3588-hdptxphy-grf\0syscon"; - reg = <0x00 0xfd5e0000 0x00 0x100>; - phandle = <0x16b>; - }; - - syscon@fd5e8000 { - compatible = "rockchip,mipi-dcphy-grf\0syscon"; - reg = <0x00 0xfd5e8000 0x00 0x4000>; - phandle = <0x171>; - }; - - syscon@fd5ec000 { - compatible = "rockchip,mipi-dcphy-grf\0syscon"; - reg = <0x00 0xfd5ec000 0x00 0x4000>; - phandle = <0x172>; - }; - - syscon@fd5f0000 { - compatible = "rockchip,rk3588-ioc\0syscon"; - reg = <0x00 0xfd5f0000 0x00 0x10000>; - phandle = <0x176>; - }; - - clock-controller@fd7c0000 { - compatible = "rockchip,rk3588-cru"; - rockchip,grf = <0x61>; - reg = <0x00 0xfd7c0000 0x00 0x5c000>; - #clock-cells = <0x01>; - #reset-cells = <0x01>; - assigned-clocks = <0x02 0x09 0x02 0x05 0x02 0x08 0x02 0x07 0x02 0xd8 0x02 0xda 0x02 0xd9 0x02 0x10e 0x02 0x10f 0x02 0x110 0x02 0x299 0x02 0x29a 0x02 0x7b 0x02 0xec 0x02 0x114 0x02 0x208 0x02 0x20e>; - assigned-clock-rates = <0x4190ab00 0x2ee00000 0x32a9f880 0x46cf7100 0x29d7ab80 0x17d78400 0x1dcd6500 0x2faf0800 0x5f5e100 0x17d78400 0x5f5e100 0xbebc200 0x165a0bc0 0x8f0d180 0xbebc200 0xb71b00 0xb71b00>; - phandle = <0x02>; - }; - - i2c@fd880000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfd880000 0x00 0x1000>; - clocks = <0x02 0x287 0x02 0x286>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x13d 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x62>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - rk8602@42 { - compatible = "rockchip,rk8602"; - reg = <0x42>; - vin-supply = <0x63>; - regulator-compatible = "rk860x-reg"; - regulator-name = "vdd_cpu_big0_s0"; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0x100590>; - regulator-ramp-delay = <0x8fc>; - rockchip,suspend-voltage-selector = <0x01>; - regulator-boot-on; - regulator-always-on; - phandle = <0x18>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - rk8603@43 { - compatible = "rockchip,rk8603"; - reg = <0x43>; - vin-supply = <0x63>; - regulator-compatible = "rk860x-reg"; - regulator-name = "vdd_cpu_big1_s0"; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0x100590>; - regulator-ramp-delay = <0x8fc>; - rockchip,suspend-voltage-selector = <0x01>; - regulator-boot-on; - regulator-always-on; - phandle = <0x1c>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - - serial@fd890000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfd890000 0x00 0x100>; - interrupts = <0x00 0x14b 0x04>; - clocks = <0x02 0x2ae 0x02 0x2af>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x64 0x06 0x64 0x07>; - pinctrl-names = "default"; - pinctrl-0 = <0x65>; - status = "disabled"; - }; - - pwm@fd8b0000 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfd8b0000 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x66>; - clocks = <0x02 0x2a5 0x02 0x2a4>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fd8b0010 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfd8b0010 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x67>; - clocks = <0x02 0x2a5 0x02 0x2a4>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fd8b0020 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfd8b0020 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x68>; - clocks = <0x02 0x2a5 0x02 0x2a4>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fd8b0030 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfd8b0030 0x00 0x10>; - interrupts = <0x00 0x158 0x04 0x00 0x159 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x69>; - clocks = <0x02 0x2a5 0x02 0x2a4>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - power-management@fd8d8000 { - compatible = "rockchip,rk3588-pmu\0syscon\0simple-mfd"; - reg = <0x00 0xfd8d8000 0x00 0x400>; - phandle = <0xbb>; - - power-controller { - compatible = "rockchip,rk3588-power-controller"; - #power-domain-cells = <0x01>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x4d>; - - power-domain@8 { - reg = <0x08>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - power-domain@9 { - reg = <0x09>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0x12f 0x02 0x131 0x02 0x130 0x02 0x126>; - pm_qos = <0x6a 0x6b 0x6c>; - - power-domain@10 { - reg = <0x0a>; - clocks = <0x02 0x12f 0x02 0x131 0x02 0x130>; - pm_qos = <0x6d>; - }; - - power-domain@11 { - reg = <0x0b>; - clocks = <0x02 0x12f 0x02 0x131 0x02 0x130>; - pm_qos = <0x6e>; - }; - }; - }; - - power-domain@12 { - reg = <0x0c>; - clocks = <0x02 0x114 0x02 0x115 0x02 0x116>; - pm_qos = <0x6f 0x70 0x71 0x72>; - }; - - power-domain@13 { - reg = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - power-domain@14 { - reg = <0x0e>; - clocks = <0x02 0x18f 0x02 0x1be 0x02 0x1bc 0x02 0x190 0x02 0x18e>; - pm_qos = <0x73>; - }; - - power-domain@15 { - reg = <0x0f>; - clocks = <0x02 0x194 0x02 0x1be 0x02 0x1bc 0x02 0x195>; - pm_qos = <0x74>; - }; - - power-domain@16 { - reg = <0x10>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0x1c4 0x02 0x1c5>; - pm_qos = <0x75 0x76 0x77>; - - power-domain@17 { - reg = <0x11>; - clocks = <0x02 0x1c9 0x02 0x1c4 0x02 0x1c5 0x02 0x1ca>; - pm_qos = <0x78 0x79 0x7a>; - }; - }; - }; - - power-domain@21 { - reg = <0x15>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0x1be 0x02 0x1bd 0x02 0x1bc 0x02 0x1bf 0x02 0x1aa 0x02 0x1a9 0x02 0x1ac 0x02 0x1ad 0x02 0x1ae 0x02 0x1af 0x02 0x1b0 0x02 0x1b1 0x02 0x1b2 0x02 0x1b3 0x02 0x1b4 0x02 0x1b5 0x02 0x1b7 0x02 0x1b6>; - pm_qos = <0x7b 0x7c 0x7d 0x7e 0x7f 0x80 0x81 0x82>; - - power-domain@23 { - reg = <0x17>; - clocks = <0x02 0x4b 0x02 0x49 0x02 0x1be>; - pm_qos = <0x83>; - }; - - power-domain@14 { - reg = <0x0e>; - clocks = <0x02 0x18f 0x02 0x1be 0x02 0x1bc 0x02 0x190>; - pm_qos = <0x73>; - }; - - power-domain@15 { - reg = <0x0f>; - clocks = <0x02 0x194 0x02 0x1be 0x02 0x1bc>; - pm_qos = <0x74>; - }; - - power-domain@22 { - reg = <0x16>; - clocks = <0x02 0x1ba 0x02 0x1b9>; - pm_qos = <0x84>; - }; - }; - - power-domain@24 { - reg = <0x18>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0x26e 0x02 0x26d 0x02 0x270>; - pm_qos = <0x85 0x86>; - - power-domain@25 { - reg = <0x19>; - clocks = <0x02 0x1f6 0x02 0x1f7 0x02 0x1f5 0x02 0x1f3 0x02 0x1ee 0x02 0x1ed 0x02 0x26d>; - pm_qos = <0x87>; - }; - }; - - power-domain@26 { - reg = <0x1a>; - clocks = <0x02 0x22e 0x02 0x22f 0x02 0x22d 0x02 0x218 0x02 0x217 0x02 0x22b 0x02 0x264>; - pm_qos = <0x88 0x89>; - }; - - power-domain@27 { - reg = <0x1b>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0x1e1 0x02 0x1e2 0x02 0x1df 0x02 0x1de 0x02 0x1e5 0x02 0x1e4>; - pm_qos = <0x8a 0x8b 0x8c 0x8d>; - - power-domain@28 { - reg = <0x1c>; - clocks = <0x02 0x121 0x02 0x120 0x02 0x1e1 0x02 0x1e2>; - pm_qos = <0x8e 0x8f>; - }; - - power-domain@29 { - reg = <0x1d>; - clocks = <0x02 0x1d6 0x02 0x1d5 0x02 0x1d9 0x02 0x1d8 0x02 0x1e2>; - pm_qos = <0x90 0x91>; - }; - }; - - power-domain@30 { - reg = <0x1e>; - clocks = <0x02 0x189 0x02 0x18a>; - pm_qos = <0x92>; - }; - - power-domain@31 { - reg = <0x1f>; - clocks = <0x02 0x166 0x02 0x19b 0x02 0x19c 0x02 0x19d 0x02 0x19e 0x02 0x19f 0x02 0x1a0>; - pm_qos = <0x93 0x94 0x95 0x96>; - }; - - power-domain@33 { - reg = <0x21>; - clocks = <0x02 0x166 0x02 0x169 0x02 0x16a>; - }; - - power-domain@34 { - reg = <0x22>; - clocks = <0x02 0x166 0x02 0x169 0x02 0x16a>; - }; - - power-domain@37 { - reg = <0x25>; - clocks = <0x02 0x199 0x02 0x140>; - pm_qos = <0x97>; - }; - - power-domain@38 { - reg = <0x26>; - clocks = <0x02 0x3c 0x02 0x3d>; - }; - - power-domain@40 { - reg = <0x28>; - pm_qos = <0x98>; - }; - }; - }; - - pvtm@fda40000 { - compatible = "rockchip,rk3588-bigcore0-pvtm"; - reg = <0x00 0xfda40000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@0 { - reg = <0x00>; - clocks = <0x02 0x2c6 0x02 0x15>; - clock-names = "clk\0pclk"; - }; - }; - - pvtm@fda50000 { - compatible = "rockchip,rk3588-bigcore1-pvtm"; - reg = <0x00 0xfda50000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@1 { - reg = <0x01>; - clocks = <0x02 0x2c8 0x02 0x17>; - clock-names = "clk\0pclk"; - }; - }; - - pvtm@fda60000 { - compatible = "rockchip,rk3588-litcore-pvtm"; - reg = <0x00 0xfda60000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@2 { - reg = <0x02>; - clocks = <0x02 0x2ca 0x02 0x1b>; - clock-names = "clk\0pclk"; - }; - }; - - pvtm@fdaf0000 { - compatible = "rockchip,rk3588-npu-pvtm"; - reg = <0x00 0xfdaf0000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@3 { - reg = <0x03>; - clocks = <0x02 0x12b 0x02 0x129>; - clock-names = "clk\0pclk"; - resets = <0x02 0x1de 0x02 0x1dc>; - reset-names = "rts\0rst-p"; - }; - }; - - pvtm@fdb30000 { - compatible = "rockchip,rk3588-gpu-pvtm"; - reg = <0x00 0xfdb30000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@4 { - reg = <0x04>; - clocks = <0x02 0x118>; - clock-names = "clk"; - resets = <0x02 0x430 0x02 0x42f>; - reset-names = "rts\0rst-p"; - }; - }; - - npu@fdab0000 { - compatible = "rockchip,rk3588-rknpu"; - reg = <0x00 0xfdab0000 0x00 0x10000 0x00 0xfdac0000 0x00 0x10000 0x00 0xfdad0000 0x00 0x10000>; - interrupts = <0x00 0x6e 0x04 0x00 0x6f 0x04 0x00 0x70 0x04>; - interrupt-names = "npu0_irq\0npu1_irq\0npu2_irq"; - clocks = <0x0e 0x06 0x02 0x12d 0x02 0x122 0x02 0x124 0x02 0x12e 0x02 0x123 0x02 0x125 0x02 0x131>; - clock-names = "clk_npu\0aclk0\0aclk1\0aclk2\0hclk0\0hclk1\0hclk2\0pclk"; - assigned-clocks = <0x0e 0x06>; - assigned-clock-rates = <0xbebc200>; - resets = <0x02 0x1e6 0x02 0x1b0 0x02 0x1c0 0x02 0x1e8 0x02 0x1b2 0x02 0x1c2>; - reset-names = "srst_a0\0srst_a1\0srst_a2\0srst_h0\0srst_h1\0srst_h2"; - power-domains = <0x4d 0x09 0x4d 0x0a 0x4d 0x0b>; - power-domain-names = "npu0\0npu1\0npu2"; - operating-points-v2 = <0x99>; - iommus = <0x9a>; - status = "okay"; - rknpu-supply = <0x9b>; - mem-supply = <0x9b>; - }; - - npu-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x9c 0x20>; - nvmem-cell-names = "leakage\0specification_serial_number"; - rockchip,supported-hw; - rockchip,pvtm-voltage-sel = <0x00 0x32f 0x00 0x330 0x343 0x01 0x344 0x35c 0x02 0x35d 0x375 0x03 0x376 0x38e 0x04 0x38f 0x270f 0x05>; - rockchip,pvtm-pvtpll; - rockchip,pvtm-offset = <0x50>; - rockchip,pvtm-sample-time = <0x44c>; - rockchip,pvtm-freq = <0xc3500>; - rockchip,pvtm-volt = <0xb71b0>; - rockchip,pvtm-ref-temp = <0x19>; - rockchip,pvtm-temp-prop = <0xffffff8f 0xffffff8f>; - rockchip,pvtm-thermal-zone = "npu-thermal"; - clocks = <0x02 0x12a>; - clock-names = "pclk"; - rockchip,grf = <0x9d>; - volt-mem-read-margin = <0xd0bd8 0x01 0xbac48 0x02 0xa4cb8 0x03 0x78d98 0x04>; - low-volt-read-margin = <0x04>; - intermediate-threshold-freq = <0x7a120>; - rockchip,init-freq = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x2710>; - rockchip,low-temp-min-volt = <0xb71b0>; - rockchip,high-temp = <0x14c08>; - rockchip,high-temp-max-freq = <0xc3500>; - phandle = <0x99>; - - opp-300000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x11e1a300>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L1 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-400000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L1 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-500000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x1dcd6500>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L1 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-600000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L1 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-700000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L3 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-800000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xb71b0 0xb71b0 0xcf850 0xb71b0 0xb71b0 0xcf850>; - opp-microvolt-L2 = <0xb40dc 0xb40dc 0xcf850 0xb40dc 0xb40dc 0xcf850>; - opp-microvolt-L3 = <0xb1008 0xb1008 0xcf850 0xb1008 0xb1008 0xcf850>; - opp-microvolt-L4 = <0xadf34 0xadf34 0xcf850 0xadf34 0xadf34 0xcf850>; - opp-microvolt-L5 = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - }; - - opp-900000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xc3500 0xc3500 0xcf850 0xc3500 0xc3500 0xcf850>; - opp-microvolt-L1 = <0xc042c 0xc042c 0xcf850 0xc042c 0xc042c 0xcf850>; - opp-microvolt-L2 = <0xbd358 0xbd358 0xcf850 0xbd358 0xbd358 0xcf850>; - opp-microvolt-L3 = <0xba284 0xba284 0xcf850 0xba284 0xba284 0xcf850>; - opp-microvolt-L4 = <0xb71b0 0xb71b0 0xcf850 0xb71b0 0xb71b0 0xcf850>; - opp-microvolt-L5 = <0xb40dc 0xb40dc 0xcf850 0xb40dc 0xb40dc 0xcf850>; - }; - - opp-1000000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xcf850 0xcf850 0xcf850 0xcf850 0xcf850 0xcf850>; - opp-microvolt-L1 = <0xcc77c 0xcc77c 0xcf850 0xcc77c 0xcc77c 0xcf850>; - opp-microvolt-L2 = <0xc96a8 0xc96a8 0xcf850 0xc96a8 0xc96a8 0xcf850>; - opp-microvolt-L3 = <0xc65d4 0xc65d4 0xcf850 0xc65d4 0xc65d4 0xcf850>; - opp-microvolt-L4 = <0xc3500 0xc3500 0xcf850 0xc3500 0xc3500 0xcf850>; - opp-microvolt-L5 = <0xc042c 0xc042c 0xcf850 0xc042c 0xc042c 0xcf850>; - }; - }; - - iommu@fdab9000 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdab9000 0x00 0x100 0x00 0xfdaba000 0x00 0x100 0x00 0xfdaca000 0x00 0x100 0x00 0xfdada000 0x00 0x100>; - interrupts = <0x00 0x6e 0x04 0x00 0x6f 0x04 0x00 0x70 0x04>; - interrupt-names = "npu0_mmu\0npu1_mmu\0npu2_mmu"; - clocks = <0x02 0x12d 0x02 0x122 0x02 0x124 0x02 0x12e 0x02 0x123 0x02 0x125>; - clock-names = "aclk0\0aclk1\0aclk2\0iface0\0iface1\0iface2"; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x9a>; - }; - - vepu@fdb50000 { - compatible = "rockchip,vpu-encoder-v2"; - reg = <0x00 0xfdb50000 0x00 0x400>; - interrupts = <0x00 0x78 0x04>; - interrupt-names = "irq_vepu"; - clocks = <0x02 0x1c0 0x02 0x1c1>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1c0>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2c8 0x02 0x2c9>; - reset-names = "shared_video_a\0shared_video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0x9e>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x00>; - rockchip,resetgroup-node = <0x00>; - power-domains = <0x4d 0x15>; - status = "disabled"; - }; - - vdpu@fdb50400 { - compatible = "rockchip,vpu-decoder-v2"; - reg = <0x00 0xfdb50400 0x00 0x400>; - interrupts = <0x00 0x77 0x04>; - interrupt-names = "irq_vdpu"; - clocks = <0x02 0x1c0 0x02 0x1c1>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1c0>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2c8 0x02 0x2c9>; - reset-names = "shared_video_a\0shared_video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0x9e>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x00>; - rockchip,resetgroup-node = <0x00>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdb50800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdb50800 0x00 0x40>; - interrupts = <0x00 0x76 0x04>; - interrupt-names = "irq_vdpu_mmu"; - clocks = <0x02 0x1c0 0x02 0x1c1>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x9e>; - }; - - avsd-plus@fdb51000 { - compatible = "rockchip,avs-plus-decoder"; - reg = <0x00 0xfdb51000 0x00 0x200>; - interrupts = <0x00 0x77 0x04>; - interrupt-names = "irq_avsd"; - clocks = <0x02 0x1c0 0x02 0x1c1>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - resets = <0x02 0x2c8 0x02 0x2c9>; - reset-names = "shared_video_a\0shared_video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0x9e>; - power-domains = <0x4d 0x15>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x00>; - rockchip,resetgroup-node = <0x00>; - status = "disabled"; - }; - - rga@fdb60000 { - compatible = "rockchip,rga3_core0"; - reg = <0x00 0xfdb60000 0x00 0x1000>; - interrupts = <0x00 0x72 0x04>; - interrupt-names = "rga3_core0_irq"; - clocks = <0x02 0x1ba 0x02 0x1b9 0x02 0x1bb>; - clock-names = "aclk_rga3_0\0hclk_rga3_0\0clk_rga3_0"; - power-domains = <0x4d 0x16>; - iommus = <0xa0>; - status = "okay"; - }; - - iommu@fdb60f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdb60f00 0x00 0x100>; - interrupts = <0x00 0x72 0x04>; - interrupt-names = "rga3_0_mmu"; - clocks = <0x02 0x1ba 0x02 0x1b9>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x16>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa0>; - }; - - rga@fdb70000 { - compatible = "rockchip,rga3_core1"; - reg = <0x00 0xfdb70000 0x00 0x1000>; - interrupts = <0x00 0x73 0x04>; - interrupt-names = "rga3_core1_irq"; - clocks = <0x02 0x18a 0x02 0x189 0x02 0x18b>; - clock-names = "aclk_rga3_1\0hclk_rga3_1\0clk_rga3_1"; - power-domains = <0x4d 0x1e>; - iommus = <0xa1>; - status = "okay"; - }; - - iommu@fdb70f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdb70f00 0x00 0x100>; - interrupts = <0x00 0x73 0x04>; - interrupt-names = "rga3_1_mmu"; - clocks = <0x02 0x18a 0x02 0x189>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x1e>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa1>; - }; - - rga@fdb80000 { - compatible = "rockchip,rga2_core0"; - reg = <0x00 0xfdb80000 0x00 0x1000>; - interrupts = <0x00 0x74 0x04>; - interrupt-names = "rga2_irq"; - clocks = <0x02 0x1b7 0x02 0x1b6 0x02 0x1b8>; - clock-names = "aclk_rga2\0hclk_rga2\0clk_rga2"; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - jpegd@fdb90000 { - compatible = "rockchip,rkv-jpeg-decoder-v1"; - reg = <0x00 0xfdb90000 0x00 0x400>; - interrupts = <0x00 0x81 0x04>; - interrupt-names = "irq_jpegd"; - clocks = <0x02 0x1b4 0x02 0x1b5>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x23c34600 0x00>; - assigned-clocks = <0x02 0x1b4>; - assigned-clock-rates = <0x23c34600>; - resets = <0x02 0x2d2 0x02 0x2d3>; - reset-names = "video_a\0video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0xa2>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x01>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdb90480 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdb90480 0x00 0x40>; - interrupts = <0x00 0x82 0x04>; - interrupt-names = "irq_jpegd_mmu"; - clocks = <0x02 0x1b4 0x02 0x1b5>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa2>; - }; - - jpege-core@fdba0000 { - compatible = "rockchip,vpu-jpege-core"; - reg = <0x00 0xfdba0000 0x00 0x400>; - interrupts = <0x00 0x7a 0x04>; - interrupt-names = "irq_jpege0"; - clocks = <0x02 0x1ac 0x02 0x1ad>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1ac>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2ca 0x02 0x2cb>; - reset-names = "video_a\0video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0xa3>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x02>; - rockchip,ccu = <0xa4>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdba0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdba0800 0x00 0x40>; - interrupts = <0x00 0x79 0x04>; - interrupt-names = "irq_jpege0_mmu"; - clocks = <0x02 0x1ac 0x02 0x1ad>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa3>; - }; - - jpege-core@fdba4000 { - compatible = "rockchip,vpu-jpege-core"; - reg = <0x00 0xfdba4000 0x00 0x400>; - interrupts = <0x00 0x7c 0x04>; - interrupt-names = "irq_jpege1"; - clocks = <0x02 0x1ae 0x02 0x1af>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1ae>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2cc 0x02 0x2cd>; - reset-names = "video_a\0video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0xa5>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x02>; - rockchip,ccu = <0xa4>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdba4800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdba4800 0x00 0x40>; - interrupts = <0x00 0x7b 0x04>; - interrupt-names = "irq_jpege1_mmu"; - clocks = <0x02 0x1ae 0x02 0x1af>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa5>; - }; - - jpege-core@fdba8000 { - compatible = "rockchip,vpu-jpege-core"; - reg = <0x00 0xfdba8000 0x00 0x400>; - interrupts = <0x00 0x7e 0x04>; - interrupt-names = "irq_jpege2"; - clocks = <0x02 0x1b0 0x02 0x1b1>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1b0>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2ce 0x02 0x2cf>; - reset-names = "video_a\0video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0xa6>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x02>; - rockchip,ccu = <0xa4>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdba8800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdba8800 0x00 0x40>; - interrupts = <0x00 0x7d 0x04>; - interrupt-names = "irq_jpege2_mmu"; - clocks = <0x02 0x1b0 0x02 0x1b1>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa6>; - }; - - jpege-core@fdbac000 { - compatible = "rockchip,vpu-jpege-core"; - reg = <0x00 0xfdbac000 0x00 0x400>; - interrupts = <0x00 0x80 0x04>; - interrupt-names = "irq_jpege3"; - clocks = <0x02 0x1b2 0x02 0x1b3>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1b2>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2d0 0x02 0x2d1>; - reset-names = "video_a\0video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0xa7>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x02>; - rockchip,ccu = <0xa4>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdbac800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdbac800 0x00 0x40>; - interrupts = <0x00 0x7f 0x04>; - interrupt-names = "irq_jpege3_mmu"; - clocks = <0x02 0x1b2 0x02 0x1b3>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa7>; - }; - - iep@fdbb0000 { - compatible = "rockchip,iep-v2"; - reg = <0x00 0xfdbb0000 0x00 0x500>; - interrupts = <0x00 0x75 0x04>; - interrupt-names = "irq_iep"; - clocks = <0x02 0x1aa 0x02 0x1a9 0x02 0x1ab>; - clock-names = "aclk\0hclk\0sclk"; - resets = <0x02 0x2d5 0x02 0x2d4 0x02 0x2d6>; - reset-names = "rst_a\0rst_h\0rst_s"; - rockchip,skip-pmu-idle-request; - power-domains = <0x4d 0x15>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x06>; - iommus = <0xa8>; - status = "okay"; - }; - - iommu@fdbb0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdbb0800 0x00 0x100>; - interrupts = <0x00 0x75 0x04>; - interrupt-names = "irq_iep_mmu"; - clocks = <0x02 0x1aa 0x02 0x1a9>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x15>; - status = "okay"; - phandle = <0xa8>; - }; - - rkvenc-core@fdbd0000 { - compatible = "rockchip,rkv-encoder-v2-core"; - reg = <0x00 0xfdbd0000 0x00 0x6000>; - interrupts = <0x00 0x65 0x04>; - interrupt-names = "irq_rkvenc0"; - clocks = <0x02 0x1c5 0x02 0x1c4 0x02 0x1c6>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core"; - rockchip,normal-rates = <0x23c34600 0x00 0x2faf0800>; - assigned-clocks = <0x02 0x1c5 0x02 0x1c6>; - assigned-clock-rates = <0x23c34600 0x2faf0800>; - resets = <0x02 0x2f5 0x02 0x2f4 0x02 0x2f6>; - reset-names = "video_a\0video_h\0video_core"; - rockchip,skip-pmu-idle-request; - iommus = <0xa9>; - rockchip,srv = <0x9f>; - rockchip,ccu = <0xaa>; - rockchip,taskqueue-node = <0x07>; - rockchip,task-capacity = <0x08>; - power-domains = <0x4d 0x10>; - status = "okay"; - }; - - iommu@fdbdf000 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdbdf000 0x00 0x40 0x00 0xfdbdf040 0x00 0x40>; - interrupts = <0x00 0x63 0x04 0x00 0x64 0x04>; - interrupt-names = "irq_rkvenc0_mmu0\0irq_rkvenc0_mmu1"; - clocks = <0x02 0x1c5 0x02 0x1c4>; - clock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - rockchip,shootdown-entire; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x10>; - status = "okay"; - phandle = <0xa9>; - }; - - rkvenc-core@fdbe0000 { - compatible = "rockchip,rkv-encoder-v2-core"; - reg = <0x00 0xfdbe0000 0x00 0x6000>; - interrupts = <0x00 0x68 0x04>; - interrupt-names = "irq_rkvenc1"; - clocks = <0x02 0x1ca 0x02 0x1c9 0x02 0x1cb>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core"; - rockchip,normal-rates = <0x23c34600 0x00 0x2faf0800>; - assigned-clocks = <0x02 0x1ca 0x02 0x1cb>; - assigned-clock-rates = <0x23c34600 0x2faf0800>; - resets = <0x02 0x305 0x02 0x304 0x02 0x306>; - reset-names = "video_a\0video_h\0video_core"; - rockchip,skip-pmu-idle-request; - iommus = <0xab>; - rockchip,srv = <0x9f>; - rockchip,ccu = <0xaa>; - rockchip,taskqueue-node = <0x07>; - rockchip,task-capacity = <0x08>; - power-domains = <0x4d 0x11>; - status = "okay"; - }; - - iommu@fdbef000 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdbef000 0x00 0x40 0x00 0xfdbef040 0x00 0x40>; - interrupts = <0x00 0x66 0x04 0x00 0x67 0x04>; - interrupt-names = "irq_rkvenc1_mmu0\0irq_rkvenc1_mmu1"; - clocks = <0x02 0x1ca 0x02 0x1c9>; - lock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - rockchip,shootdown-entire; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x11>; - status = "okay"; - phandle = <0xab>; - }; - - rkvdec-ccu@fdc30000 { - compatible = "rockchip,rkv-decoder-v2-ccu"; - reg = <0x00 0xfdc30000 0x00 0x100>; - reg-names = "ccu"; - clocks = <0x02 0x18e>; - clock-names = "aclk_ccu"; - assigned-clocks = <0x02 0x18e>; - assigned-clock-rates = <0x23c34600>; - resets = <0x02 0x282>; - reset-names = "video_ccu"; - rockchip,skip-pmu-idle-request; - power-domains = <0x4d 0x0e>; - status = "okay"; - phandle = <0xad>; - }; - - rkvdec-core@fdc38000 { - compatible = "rockchip,rkv-decoder-v2"; - reg = <0x00 0xfdc38100 0x00 0x400 0x00 0xfdc38000 0x00 0x100>; - reg-names = "regs\0link"; - interrupts = <0x00 0x5f 0x04>; - interrupt-names = "irq_rkvdec0"; - clocks = <0x02 0x190 0x02 0x18f 0x02 0x193 0x02 0x191 0x02 0x192>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core\0clk_cabac\0clk_hevc_cabac"; - rockchip,normal-rates = <0x2faf0800 0x00 0x23c34600 0x23c34600 0x3b9aca00>; - assigned-clocks = <0x02 0x190 0x02 0x193 0x02 0x191 0x02 0x192>; - assigned-clock-rates = <0x2faf0800 0x23c34600 0x23c34600 0x3b9aca00>; - resets = <0x02 0x284 0x02 0x283 0x02 0x289 0x02 0x287 0x02 0x288>; - reset-names = "video_a\0video_h\0video_core\0video_cabac\0video_hevc_cabac"; - rockchip,skip-pmu-idle-request; - iommus = <0xac>; - rockchip,srv = <0x9f>; - rockchip,ccu = <0xad>; - rockchip,core-mask = <0x10001>; - rockchip,taskqueue-node = <0x09>; - rockchip,sram = <0xae>; - rockchip,rcb-iova = <0xfff00000 0x100000>; - rockchip,rcb-min-width = <0x200>; - power-domains = <0x4d 0x0e>; - status = "okay"; - }; - - iommu@fdc38700 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdc38700 0x00 0x40 0x00 0xfdc38740 0x00 0x40>; - interrupts = <0x00 0x60 0x04>; - interrupt-names = "irq_rkvdec0_mmu"; - clocks = <0x02 0x190 0x02 0x18f>; - clock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - rockchip,shootdown-entire; - rockchip,master-handle-irq; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x0e>; - status = "okay"; - phandle = <0xac>; - }; - - rkvdec-core@fdc48000 { - compatible = "rockchip,rkv-decoder-v2"; - reg = <0x00 0xfdc48100 0x00 0x400 0x00 0xfdc48000 0x00 0x100>; - reg-names = "regs\0link"; - interrupts = <0x00 0x61 0x04>; - interrupt-names = "irq_rkvdec1"; - clocks = <0x02 0x195 0x02 0x194 0x02 0x198 0x02 0x196 0x02 0x197>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core\0clk_cabac\0clk_hevc_cabac"; - rockchip,normal-rates = <0x2faf0800 0x00 0x23c34600 0x23c34600 0x3b9aca00>; - assigned-clocks = <0x02 0x195 0x02 0x198 0x02 0x196 0x02 0x197>; - assigned-clock-rates = <0x2faf0800 0x23c34600 0x23c34600 0x3b9aca00>; - resets = <0x02 0x293 0x02 0x292 0x02 0x298 0x02 0x296 0x02 0x297>; - reset-names = "video_a\0video_h\0video_core\0video_cabac\0video_hevc_cabac"; - rockchip,skip-pmu-idle-request; - iommus = <0xaf>; - rockchip,srv = <0x9f>; - rockchip,ccu = <0xad>; - rockchip,core-mask = <0x20002>; - rockchip,taskqueue-node = <0x09>; - rockchip,sram = <0xb0>; - rockchip,rcb-iova = <0xffe00000 0x100000>; - rockchip,rcb-min-width = <0x200>; - power-domains = <0x4d 0x0f>; - status = "okay"; - }; - - iommu@fdc48700 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdc48700 0x00 0x40 0x00 0xfdc48740 0x00 0x40>; - interrupts = <0x00 0x62 0x04>; - interrupt-names = "irq_rkvdec1_mmu"; - clocks = <0x02 0x195 0x02 0x194>; - clock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - rockchip,shootdown-entire; - rockchip,master-handle-irq; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x0f>; - status = "okay"; - phandle = <0xaf>; - }; - - av1d@fdc70000 { - compatible = "rockchip,av1-decoder"; - reg = <0x00 0xfdc70000 0x00 0x800 0x00 0xfdc80000 0x00 0x400 0x00 0xfdc90000 0x00 0x400>; - reg-names = "vcd\0cache\0afbc"; - interrupts = <0x00 0x6c 0x04 0x00 0x6b 0x04 0x00 0x6a 0x04>; - interrupt-names = "irq_av1d\0irq_cache\0irq_afbc"; - clocks = <0x02 0x49 0x02 0x4b>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x17d78400 0x17d78400>; - assigned-clocks = <0x02 0x49 0x02 0x4b>; - assigned-clock-rates = <0x17d78400 0x17d78400>; - resets = <0x02 0x442 0x02 0x445>; - reset-names = "video_a\0video_h"; - iommus = <0xb1>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x0b>; - power-domains = <0x4d 0x17>; - status = "okay"; - }; - - iommu@fdca0000 { - compatible = "rockchip,iommu-av1"; - reg = <0x00 0xfdca0000 0x00 0x600>; - interrupts = <0x00 0x6d 0x04>; - interrupt-names = "irq_av1d_mmu"; - clocks = <0x02 0x49 0x02 0x4b>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x17>; - status = "okay"; - phandle = <0xb1>; - }; - - rkisp-unite@fdcb0000 { - compatible = "rockchip,rk3588-rkisp-unite"; - reg = <0x00 0xfdcb0000 0x00 0x10000 0x00 0xfdcc0000 0x00 0x10000>; - interrupts = <0x00 0x87 0x04 0x00 0x89 0x04 0x00 0x8a 0x04>; - interrupt-names = "isp_irq\0mi_irq\0mipi_irq"; - clocks = <0x02 0x1de 0x02 0x1df 0x02 0x1db 0x02 0x1dc 0x02 0x1dd 0x02 0x120 0x02 0x121 0x02 0x11d 0x02 0x11e 0x02 0x11f>; - clock-names = "aclk_isp0\0hclk_isp0\0clk_isp_core0\0clk_isp_core_marvin0\0clk_isp_core_vicap0\0aclk_isp1\0hclk_isp1\0clk_isp_core1\0clk_isp_core_marvin1\0clk_isp_core_vicap1"; - power-domains = <0x4d 0x1c>; - iommus = <0xb2>; - status = "disabled"; - }; - - rkisp@fdcb0000 { - compatible = "rockchip,rk3588-rkisp"; - reg = <0x00 0xfdcb0000 0x00 0x7f00>; - interrupts = <0x00 0x83 0x04 0x00 0x85 0x04 0x00 0x86 0x04>; - interrupt-names = "isp_irq\0mi_irq\0mipi_irq"; - clocks = <0x02 0x1de 0x02 0x1df 0x02 0x1db 0x02 0x1dc 0x02 0x1dd>; - clock-names = "aclk_isp\0hclk_isp\0clk_isp_core\0clk_isp_core_marvin\0clk_isp_core_vicap"; - power-domains = <0x4d 0x1b>; - iommus = <0xb3>; - status = "disabled"; - phandle = <0x46>; - }; - - rkisp-unite-mmu@fdcb7f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdcb7f00 0x00 0x100 0x00 0xfdcc7f00 0x00 0x100>; - interrupts = <0x00 0x84 0x04 0x00 0x88 0x04>; - interrupt-names = "isp0_mmu\0isp1_mmu"; - clocks = <0x02 0x1de 0x02 0x1df 0x02 0x120 0x02 0x121>; - clock-names = "aclk0\0iface0\0aclk1\0iface1"; - power-domains = <0x4d 0x1c>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "disabled"; - phandle = <0xb2>; - }; - - iommu@fdcb7f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdcb7f00 0x00 0x100>; - interrupts = <0x00 0x84 0x04>; - interrupt-names = "isp0_mmu"; - clocks = <0x02 0x1de 0x02 0x1df>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x1b>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "disabled"; - phandle = <0xb3>; - }; - - rkisp@fdcc0000 { - compatible = "rockchip,rk3588-rkisp"; - reg = <0x00 0xfdcc0000 0x00 0x7f00>; - interrupts = <0x00 0x87 0x04 0x00 0x89 0x04 0x00 0x8a 0x04>; - interrupt-names = "isp_irq\0mi_irq\0mipi_irq"; - clocks = <0x02 0x120 0x02 0x121 0x02 0x11d 0x02 0x11e 0x02 0x11f>; - clock-names = "aclk_isp\0hclk_isp\0clk_isp_core\0clk_isp_core_marvin\0clk_isp_core_vicap"; - power-domains = <0x4d 0x1c>; - iommus = <0xb4>; - status = "disabled"; - phandle = <0x47>; - }; - - iommu@fdcc7f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdcc7f00 0x00 0x100>; - interrupts = <0x00 0x88 0x04>; - interrupt-names = "isp1_mmu"; - clocks = <0x02 0x120 0x02 0x121>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x1c>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "disabled"; - phandle = <0xb4>; - }; - - rkispp@fdcd0000 { - compatible = "rockchip,rk3588-rkispp"; - reg = <0x00 0xfdcd0000 0x00 0xf00>; - interrupts = <0x00 0x8b 0x04>; - interrupt-names = "fec_irq"; - clocks = <0x02 0x1d5 0x02 0x1d6 0x02 0x1d7>; - clock-names = "aclk_ispp\0hclk_ispp\0clk_ispp"; - assigned-clocks = <0x02 0x1d6>; - assigned-clock-rates = <0x5f5e100>; - power-domains = <0x4d 0x1d>; - iommus = <0xb5>; - status = "disabled"; - phandle = <0x48>; - }; - - iommu@fdcd0f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdcd0f00 0x00 0x100>; - interrupts = <0x00 0x8c 0x04>; - interrupt-names = "fec0_mmu"; - clocks = <0x02 0x1d5 0x02 0x1d6 0x02 0x1d7>; - clock-names = "aclk\0iface\0pclk"; - power-domains = <0x4d 0x1d>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "disabled"; - phandle = <0xb5>; - }; - - rkispp@fdcd8000 { - compatible = "rockchip,rk3588-rkispp"; - reg = <0x00 0xfdcd8000 0x00 0xf00>; - interrupts = <0x00 0x8d 0x04>; - interrupt-names = "fec_irq"; - clocks = <0x02 0x1d8 0x02 0x1d9 0x02 0x1da>; - clock-names = "aclk_ispp\0hclk_ispp\0clk_ispp"; - assigned-clocks = <0x02 0x1d9>; - assigned-clock-rates = <0x5f5e100>; - power-domains = <0x4d 0x1d>; - iommus = <0xb6>; - status = "disabled"; - phandle = <0x49>; - }; - - iommu@fdcd8f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdcd8f00 0x00 0x100>; - interrupts = <0x00 0x8e 0x04>; - interrupt-names = "fec1_mmu"; - clocks = <0x02 0x1d8 0x02 0x1d9 0x02 0x1da>; - clock-names = "aclk\0iface\0pclk"; - power-domains = <0x4d 0x1d>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "disabled"; - phandle = <0xb6>; - }; - - rkcif@fdce0000 { - compatible = "rockchip,rk3588-cif"; - reg = <0x00 0xfdce0000 0x00 0x800>; - reg-names = "cif_regs"; - interrupts = <0x00 0x9b 0x04>; - interrupt-names = "cif-intr"; - clocks = <0x02 0x1e4 0x02 0x1e5 0x02 0x1e3>; - clock-names = "aclk_cif\0hclk_cif\0dclk_cif"; - resets = <0x02 0x317 0x02 0x318 0x02 0x316>; - reset-names = "rst_cif_a\0rst_cif_h\0rst_cif_d"; - assigned-clocks = <0x02 0x1e3>; - assigned-clock-rates = <0x23c34600>; - power-domains = <0x4d 0x1b>; - rockchip,grf = <0xb7>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x3f>; - }; - - iommu@fdce0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdce0800 0x00 0x100 0x00 0xfdce0900 0x00 0x100>; - interrupts = <0x00 0x71 0x04>; - interrupt-names = "cif_mmu"; - clocks = <0x02 0x1e4 0x02 0x1e5>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x1b>; - rockchip,disable-mmu-reset; - #iommu-cells = <0x00>; - status = "disabled"; - phandle = <0x40>; - }; - - mipi0-csi2@fdd10000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd10000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x8f 0x04 0x00 0x90 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1cf 0x02 0x1cd>; - clock-names = "pclk_csi2host\0iclk_csi2host"; - resets = <0x02 0x324 0x02 0x334>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - mipi1-csi2@fdd20000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd20000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x91 0x04 0x00 0x92 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1d0 0x02 0x1ce>; - clock-names = "pclk_csi2host\0iclk_csi2host"; - resets = <0x02 0x325 0x02 0x335>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - mipi2-csi2@fdd30000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd30000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x93 0x04 0x00 0x94 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1d1>; - clock-names = "pclk_csi2host"; - resets = <0x02 0x326 0x02 0x336>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - mipi3-csi2@fdd40000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd40000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x95 0x04 0x00 0x96 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1d2>; - clock-names = "pclk_csi2host"; - resets = <0x02 0x327 0x02 0x337>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - vop@fdd90000 { - compatible = "rockchip,rk3588-vop"; - reg = <0x00 0xfdd90000 0x00 0x4200 0x00 0xfdd95000 0x00 0x1000>; - reg-names = "regs\0gamma_lut"; - interrupts = <0x00 0x9c 0x04>; - clocks = <0x02 0x270 0x02 0x26f 0x02 0x274 0x02 0x275 0x02 0x276 0x02 0x277 0x02 0x26e 0x02 0x271 0x02 0x272 0x02 0x273>; - clock-names = "aclk_vop\0hclk_vop\0dclk_vp0\0dclk_vp1\0dclk_vp2\0dclk_vp3\0pclk_vop\0dclk_src_vp0\0dclk_src_vp1\0dclk_src_vp2"; - assigned-clocks = <0x02 0x270>; - assigned-clock-rates = <0x2faf0800>; - resets = <0x02 0x349 0x02 0x348 0x02 0x34d 0x02 0x350 0x02 0x351 0x02 0x352>; - reset-names = "axi\0ahb\0dclk_vp0\0dclk_vp1\0dclk_vp2\0dclk_vp3"; - iommus = <0xb8>; - power-domains = <0x4d 0x18>; - rockchip,grf = <0xb7>; - rockchip,vop-grf = <0xb9>; - rockchip,vo1-grf = <0xba>; - rockchip,pmu = <0xbb>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - phandle = <0x2d>; - - port@0 { - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00>; - rockchip,plane-mask = <0x05>; - rockchip,primary-plane = <0x02>; - assigned-clocks = <0x02 0x270>; - assigned-clock-rates = <0x2faf0800>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xbc>; - phandle = <0xd9>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xbd>; - phandle = <0xe4>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xbe>; - phandle = <0x35>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0xbf>; - phandle = <0x18a>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0xc0>; - phandle = <0x194>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0xc1>; - phandle = <0x191>; - }; - }; - - port@1 { - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x01>; - rockchip,plane-mask = <0x0a>; - rockchip,primary-plane = <0x03>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xc2>; - phandle = <0x31>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xc3>; - phandle = <0xe5>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xc4>; - phandle = <0xe1>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0xc5>; - phandle = <0x18b>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0xc6>; - phandle = <0x195>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0xc7>; - phandle = <0x38>; - }; - }; - - port@2 { - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x02>; - assigned-clocks = <0x02 0x273>; - assigned-clock-parents = <0x02 0x04>; - rockchip,plane-mask = <0x140>; - rockchip,primary-plane = <0x08>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xc8>; - phandle = <0xda>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xc9>; - phandle = <0x34>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xca>; - phandle = <0xe2>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0xcb>; - phandle = <0xd5>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0xcc>; - phandle = <0xd6>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0xcd>; - phandle = <0x37>; - }; - - endpoint@6 { - reg = <0x06>; - remote-endpoint = <0xce>; - phandle = <0x196>; - }; - - endpoint@7 { - reg = <0x07>; - remote-endpoint = <0xcf>; - phandle = <0x192>; - }; - }; - - port@3 { - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x03>; - rockchip,plane-mask = <0x280>; - rockchip,primary-plane = <0x09>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xd0>; - phandle = <0x32>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xd1>; - phandle = <0x33>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xd2>; - phandle = <0x36>; - }; - }; - }; - }; - - iommu@fdd97e00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdd97e00 0x00 0x100 0x00 0xfdd97f00 0x00 0x100>; - interrupts = <0x00 0x9c 0x04>; - interrupt-names = "vop_mmu"; - clocks = <0x02 0x270 0x02 0x26f>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - rockchip,disable-device-link-resume; - rockchip,shootdown-entire; - status = "okay"; - phandle = <0xb8>; - }; - - spdif-tx@fddb0000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfddb0000 0x00 0x1000>; - interrupts = <0x00 0xc3 0x04>; - dmas = <0xd3 0x06>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x209 0x02 0x204>; - assigned-clocks = <0x02 0x205>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x19>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x1af>; - }; - - i2s@fddc0000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddc0000 0x00 0x1000>; - interrupts = <0x00 0xb8 0x04>; - clocks = <0x02 0x1fb 0x02 0x1fb 0x02 0x1f0>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x1f9>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd4 0x00>; - dma-names = "tx"; - power-domains = <0x4d 0x19>; - resets = <0x02 0x38d>; - reset-names = "tx-m"; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-tx@fdde0000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfdde0000 0x00 0x1000>; - interrupts = <0x00 0xc4 0x04>; - dmas = <0xd3 0x07>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x257 0x02 0x253>; - assigned-clocks = <0x02 0x254>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x1a>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - i2s@fddf0000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddf0000 0x00 0x1000>; - interrupts = <0x00 0xb9 0x04>; - clocks = <0x02 0x246 0x02 0x246 0x02 0x248>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x243>; - assigned-clock-parents = <0x02 0x07>; - dmas = <0xd4 0x02>; - dma-names = "tx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x3e8>; - reset-names = "tx-m"; - rockchip,always-on; - rockchip,hdmi-path; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x1ad>; - }; - - i2s@fddfc000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddfc000 0x00 0x1000>; - interrupts = <0x00 0xbd 0x04>; - clocks = <0x02 0x242 0x02 0x242 0x02 0x23e>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x23f>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd4 0x17>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x413>; - reset-names = "rx-m"; - rockchip,capture-only; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-rx@fde08000 { - compatible = "rockchip,rk3588-spdifrx\0rockchip,rk3308-spdifrx"; - reg = <0x00 0xfde08000 0x00 0x1000>; - interrupts = <0x00 0xc7 0x04>; - clocks = <0x02 0x25e 0x02 0x25d>; - clock-names = "mclk\0hclk"; - assigned-clocks = <0x02 0x25e>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0x64 0x15>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x3fd>; - reset-names = "spdifrx-m"; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - dsi@fde20000 { - compatible = "rockchip,rk3588-mipi-dsi2"; - reg = <0x00 0xfde20000 0x00 0x10000>; - interrupts = <0x00 0xa7 0x04>; - clocks = <0x02 0x278 0x02 0x27a>; - clock-names = "pclk\0sys_clk"; - resets = <0x02 0x354>; - reset-names = "apb"; - power-domains = <0x4d 0x18>; - phys = <0x2a>; - phy-names = "dcphy"; - rockchip,grf = <0xb9>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xd5>; - status = "disabled"; - phandle = <0xcb>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x32>; - status = "disabled"; - phandle = <0xd0>; - }; - }; - }; - }; - - dsi@fde30000 { - compatible = "rockchip,rk3588-mipi-dsi2"; - reg = <0x00 0xfde30000 0x00 0x10000>; - interrupts = <0x00 0xa8 0x04>; - clocks = <0x02 0x279 0x02 0x27b>; - clock-names = "pclk\0sys_clk"; - resets = <0x02 0x355>; - reset-names = "apb"; - power-domains = <0x4d 0x18>; - phys = <0x2b>; - phy-names = "dcphy"; - rockchip,grf = <0xb9>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xd6>; - status = "disabled"; - phandle = <0xcc>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x33>; - status = "disabled"; - phandle = <0xd1>; - }; - }; - }; - }; - - hdcp@fde40000 { - compatible = "rockchip,rk3588-hdcp"; - reg = <0x00 0xfde40000 0x00 0x80>; - interrupts = <0x00 0x9f 0x04>; - clocks = <0x02 0x1ed 0x02 0x1ef 0x02 0x1ee 0x02 0x1ec 0x02 0x1f1 0x02 0x1f2>; - clock-names = "aclk\0pclk\0hclk\0hclk_key\0aclk_trng\0pclk_trng"; - resets = <0x02 0x37f 0x02 0x37d 0x02 0x37c 0x02 0x37b 0x02 0x381>; - reset-names = "hdcp\0h_hdcp\0a_hdcp\0hdcp_key\0trng"; - power-domains = <0x4d 0x19>; - rockchip,vo-grf = <0xd7>; - status = "disabled"; - }; - - dp@fde50000 { - compatible = "rockchip,rk3588-dp"; - reg = <0x00 0xfde50000 0x00 0x4000>; - interrupts = <0x00 0xa1 0x04>; - clocks = <0x02 0x1e6 0x02 0x2cc 0x02 0x1fb 0x02 0x207 0x04>; - clock-names = "apb\0aux\0i2s\0spdif\0hclk"; - assigned-clocks = <0x02 0x2cc>; - assigned-clock-rates = <0xf42400>; - resets = <0x02 0x388>; - phys = <0xd8>; - power-domains = <0x4d 0x19>; - #sound-dai-cells = <0x01>; - status = "disabled"; - phandle = <0x1b0>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xd9>; - status = "disabled"; - phandle = <0xbc>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x31>; - status = "disabled"; - phandle = <0xc2>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xda>; - status = "disabled"; - phandle = <0xc8>; - }; - }; - }; - }; - - hdcp@fde70000 { - compatible = "rockchip,rk3588-hdcp"; - reg = <0x00 0xfde70000 0x00 0x80>; - interrupts = <0x00 0xa0 0x04>; - clocks = <0x02 0x217 0x02 0x219 0x02 0x218 0x02 0x216 0x02 0x228 0x02 0x229>; - clock-names = "aclk\0pclk\0hclk\0hclk_key\0aclk_trng\0pclk_trng"; - resets = <0x02 0x3c8 0x02 0x3c6 0x02 0x3c5 0x02 0x3c4 0x02 0x3ca>; - reset-names = "hdcp\0h_hdcp\0a_hdcp\0hdcp_key\0trng"; - power-domains = <0x4d 0x1a>; - rockchip,vo-grf = <0xba>; - status = "disabled"; - }; - - hdmi@fde80000 { - compatible = "rockchip,rk3588-dw-hdmi"; - reg = <0x00 0xfde80000 0x00 0x20000>; - interrupts = <0x00 0xa9 0x04 0x00 0xaa 0x04 0x00 0xab 0x04 0x00 0xac 0x04 0x00 0x168 0x04>; - clocks = <0x02 0x221 0x02 0x265 0x02 0x222 0x02 0x223 0x02 0x246 0x02 0x274 0x02 0x275 0x02 0x276 0x02 0x277 0x05 0x2e>; - clock-names = "pclk\0hpd\0earc\0hdmitx_ref\0aud\0dclk_vp0\0dclk_vp1\0dclk_vp2\0dclk_vp3\0hclk_vo1\0link_clk"; - resets = <0x02 0x3d0 0x02 0x49c>; - reset-names = "ref\0hdp"; - power-domains = <0x4d 0x1a>; - pinctrl-names = "default"; - pinctrl-0 = <0xdb 0xdc 0xdd 0xde>; - reg-io-width = <0x04>; - rockchip,grf = <0xb7>; - rockchip,vo1_grf = <0xba>; - phys = <0xdf>; - phy-names = "hdmi"; - #sound-dai-cells = <0x00>; - status = "okay"; - enable-gpios = <0xe0 0x00 0x00>; - phandle = <0x1ae>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x35>; - status = "okay"; - phandle = <0xbe>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xe1>; - status = "disabled"; - phandle = <0xc4>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xe2>; - status = "disabled"; - phandle = <0xca>; - }; - }; - }; - }; - - edp@fdec0000 { - compatible = "rockchip,rk3588-edp"; - reg = <0x00 0xfdec0000 0x00 0x1000>; - interrupts = <0x00 0xa3 0x04>; - clocks = <0x02 0x211 0x02 0x210 0x02 0x212 0x05>; - clock-names = "dp\0pclk\0spdif\0hclk"; - resets = <0x02 0x3e1 0x02 0x3e0>; - reset-names = "dp\0apb"; - phys = <0xe3>; - phy-names = "dp"; - power-domains = <0x4d 0x1a>; - rockchip,grf = <0xba>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xe4>; - status = "disabled"; - phandle = <0xbd>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xe5>; - status = "disabled"; - phandle = <0xc3>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x34>; - status = "disabled"; - phandle = <0xc9>; - }; - }; - }; - }; - - qos@fdf35000 { - compatible = "syscon"; - reg = <0x00 0xfdf35000 0x00 0x20>; - phandle = <0x6f>; - }; - - qos@fdf35200 { - compatible = "syscon"; - reg = <0x00 0xfdf35200 0x00 0x20>; - phandle = <0x70>; - }; - - qos@fdf35400 { - compatible = "syscon"; - reg = <0x00 0xfdf35400 0x00 0x20>; - phandle = <0x71>; - }; - - qos@fdf35600 { - compatible = "syscon"; - reg = <0x00 0xfdf35600 0x00 0x20>; - phandle = <0x72>; - }; - - qos@fdf36000 { - compatible = "syscon"; - reg = <0x00 0xfdf36000 0x00 0x20>; - phandle = <0x92>; - }; - - qos@fdf39000 { - compatible = "syscon"; - reg = <0x00 0xfdf39000 0x00 0x20>; - phandle = <0x97>; - }; - - qos@fdf3d800 { - compatible = "syscon"; - reg = <0x00 0xfdf3d800 0x00 0x20>; - phandle = <0x98>; - }; - - qos@fdf3e000 { - compatible = "syscon"; - reg = <0x00 0xfdf3e000 0x00 0x20>; - phandle = <0x94>; - }; - - qos@fdf3e200 { - compatible = "syscon"; - reg = <0x00 0xfdf3e200 0x00 0x20>; - phandle = <0x93>; - }; - - qos@fdf3e400 { - compatible = "syscon"; - reg = <0x00 0xfdf3e400 0x00 0x20>; - phandle = <0x95>; - }; - - qos@fdf3e600 { - compatible = "syscon"; - reg = <0x00 0xfdf3e600 0x00 0x20>; - phandle = <0x96>; - }; - - qos@fdf40000 { - compatible = "syscon"; - reg = <0x00 0xfdf40000 0x00 0x20>; - phandle = <0x90>; - }; - - qos@fdf40200 { - compatible = "syscon"; - reg = <0x00 0xfdf40200 0x00 0x20>; - phandle = <0x91>; - }; - - qos@fdf40400 { - compatible = "syscon"; - reg = <0x00 0xfdf40400 0x00 0x20>; - phandle = <0x8a>; - }; - - qos@fdf40500 { - compatible = "syscon"; - reg = <0x00 0xfdf40500 0x00 0x20>; - phandle = <0x8b>; - }; - - qos@fdf40600 { - compatible = "syscon"; - reg = <0x00 0xfdf40600 0x00 0x20>; - phandle = <0x8c>; - }; - - qos@fdf40800 { - compatible = "syscon"; - reg = <0x00 0xfdf40800 0x00 0x20>; - phandle = <0x8d>; - }; - - qos@fdf41000 { - compatible = "syscon"; - reg = <0x00 0xfdf41000 0x00 0x20>; - phandle = <0x8e>; - }; - - qos@fdf41100 { - compatible = "syscon"; - reg = <0x00 0xfdf41100 0x00 0x20>; - phandle = <0x8f>; - }; - - qos@fdf60000 { - compatible = "syscon"; - reg = <0x00 0xfdf60000 0x00 0x20>; - phandle = <0x75>; - }; - - qos@fdf60200 { - compatible = "syscon"; - reg = <0x00 0xfdf60200 0x00 0x20>; - phandle = <0x76>; - }; - - qos@fdf60400 { - compatible = "syscon"; - reg = <0x00 0xfdf60400 0x00 0x20>; - phandle = <0x77>; - }; - - qos@fdf61000 { - compatible = "syscon"; - reg = <0x00 0xfdf61000 0x00 0x20>; - phandle = <0x78>; - }; - - qos@fdf61200 { - compatible = "syscon"; - reg = <0x00 0xfdf61200 0x00 0x20>; - phandle = <0x79>; - }; - - qos@fdf61400 { - compatible = "syscon"; - reg = <0x00 0xfdf61400 0x00 0x20>; - phandle = <0x7a>; - }; - - qos@fdf62000 { - compatible = "syscon"; - reg = <0x00 0xfdf62000 0x00 0x20>; - phandle = <0x73>; - }; - - qos@fdf63000 { - compatible = "syscon"; - reg = <0x00 0xfdf63000 0x00 0x20>; - phandle = <0x74>; - }; - - qos@fdf64000 { - compatible = "syscon"; - reg = <0x00 0xfdf64000 0x00 0x20>; - phandle = <0x83>; - }; - - qos@fdf66000 { - compatible = "syscon"; - reg = <0x00 0xfdf66000 0x00 0x20>; - phandle = <0x7b>; - }; - - qos@fdf66200 { - compatible = "syscon"; - reg = <0x00 0xfdf66200 0x00 0x20>; - phandle = <0x7c>; - }; - - qos@fdf66400 { - compatible = "syscon"; - reg = <0x00 0xfdf66400 0x00 0x20>; - phandle = <0x7d>; - }; - - qos@fdf66600 { - compatible = "syscon"; - reg = <0x00 0xfdf66600 0x00 0x20>; - phandle = <0x7e>; - }; - - qos@fdf66800 { - compatible = "syscon"; - reg = <0x00 0xfdf66800 0x00 0x20>; - phandle = <0x7f>; - }; - - qos@fdf66a00 { - compatible = "syscon"; - reg = <0x00 0xfdf66a00 0x00 0x20>; - phandle = <0x80>; - }; - - qos@fdf66c00 { - compatible = "syscon"; - reg = <0x00 0xfdf66c00 0x00 0x20>; - phandle = <0x81>; - }; - - qos@fdf66e00 { - compatible = "syscon"; - reg = <0x00 0xfdf66e00 0x00 0x20>; - phandle = <0x82>; - }; - - qos@fdf67000 { - compatible = "syscon"; - reg = <0x00 0xfdf67000 0x00 0x20>; - phandle = <0x84>; - }; - - qos@fdf67200 { - compatible = "syscon"; - reg = <0x00 0xfdf67200 0x00 0x20>; - }; - - qos@fdf70000 { - compatible = "syscon"; - reg = <0x00 0xfdf70000 0x00 0x20>; - phandle = <0x6d>; - }; - - qos@fdf71000 { - compatible = "syscon"; - reg = <0x00 0xfdf71000 0x00 0x20>; - phandle = <0x6e>; - }; - - qos@fdf72000 { - compatible = "syscon"; - reg = <0x00 0xfdf72000 0x00 0x20>; - phandle = <0x6a>; - }; - - qos@fdf72200 { - compatible = "syscon"; - reg = <0x00 0xfdf72200 0x00 0x20>; - phandle = <0x6b>; - }; - - qos@fdf72400 { - compatible = "syscon"; - reg = <0x00 0xfdf72400 0x00 0x20>; - phandle = <0x6c>; - }; - - qos@fdf80000 { - compatible = "syscon"; - reg = <0x00 0xfdf80000 0x00 0x20>; - phandle = <0x87>; - }; - - qos@fdf81000 { - compatible = "syscon"; - reg = <0x00 0xfdf81000 0x00 0x20>; - phandle = <0x88>; - }; - - qos@fdf81200 { - compatible = "syscon"; - reg = <0x00 0xfdf81200 0x00 0x20>; - phandle = <0x89>; - }; - - qos@fdf82000 { - compatible = "syscon"; - reg = <0x00 0xfdf82000 0x00 0x20>; - phandle = <0x85>; - }; - - qos@fdf82200 { - compatible = "syscon"; - reg = <0x00 0xfdf82200 0x00 0x20>; - phandle = <0x86>; - }; - - dfi@fe060000 { - compatible = "rockchip,rk3588-dfi"; - reg = <0x00 0xfe060000 0x00 0x10000>; - rockchip,pmu_grf = <0xe6>; - status = "okay"; - phandle = <0x39>; - }; - - pcie@fe180000 { - compatible = "rockchip,rk3588-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x30 0x3f>; - clocks = <0x02 0x151 0x02 0x156 0x02 0x14c 0x02 0x15c 0x02 0x161 0x02 0x2c5>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux\0pipe"; - device_type = "pci"; - interrupts = <0x00 0xf8 0x04 0x00 0xf7 0x04 0x00 0xf6 0x04 0x00 0xf5 0x04 0x00 0xf4 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xe7 0x00 0x00 0x00 0x00 0x02 0xe7 0x01 0x00 0x00 0x00 0x03 0xe7 0x02 0x00 0x00 0x00 0x04 0xe7 0x03>; - linux,pci-domain = <0x03>; - num-ib-windows = <0x08>; - num-ob-windows = <0x08>; - num-viewport = <0x04>; - max-link-speed = <0x02>; - msi-map = <0x3000 0xe8 0x3000 0x1000>; - num-lanes = <0x01>; - phys = <0x5b 0x02>; - phy-names = "pcie-phy"; - ranges = <0x800 0x00 0xf3000000 0x00 0xf3000000 0x00 0x100000 0x81000000 0x00 0xf3100000 0x00 0xf3100000 0x00 0x100000 0x82000000 0x00 0xf3200000 0x00 0xf3200000 0x00 0xe00000 0xc3000000 0x09 0xc0000000 0x09 0xc0000000 0x00 0x40000000>; - reg = <0x00 0xfe180000 0x00 0x10000 0x0a 0x40c00000 0x00 0x400000>; - reg-names = "pcie-apb\0pcie-dbi"; - resets = <0x02 0x210 0x02 0x21f>; - reset-names = "pcie\0periph"; - rockchip,pipe-grf = <0x61>; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf5 0x01>; - phandle = <0xe7>; - }; - }; - - pcie@fe190000 { - compatible = "rockchip,rk3588-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x40 0x4f>; - clocks = <0x02 0x152 0x02 0x157 0x02 0x14d 0x02 0x15d 0x02 0x162 0x02 0x182>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux\0pipe"; - device_type = "pci"; - interrupts = <0x00 0xfd 0x04 0x00 0xfc 0x04 0x00 0xfb 0x04 0x00 0xfa 0x04 0x00 0xf9 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xe9 0x00 0x00 0x00 0x00 0x02 0xe9 0x01 0x00 0x00 0x00 0x03 0xe9 0x02 0x00 0x00 0x00 0x04 0xe9 0x03>; - linux,pci-domain = <0x04>; - num-ib-windows = <0x08>; - num-ob-windows = <0x08>; - num-viewport = <0x04>; - max-link-speed = <0x02>; - msi-map = <0x4000 0xe8 0x4000 0x1000>; - num-lanes = <0x01>; - phys = <0xea 0x02>; - phy-names = "pcie-phy"; - ranges = <0x800 0x00 0xf4000000 0x00 0xf4000000 0x00 0x100000 0x81000000 0x00 0xf4100000 0x00 0xf4100000 0x00 0x100000 0x82000000 0x00 0xf4200000 0x00 0xf4200000 0x00 0xe00000 0xc3000000 0x0a 0x00 0x0a 0x00 0x00 0x40000000>; - reg = <0x00 0xfe190000 0x00 0x10000 0x0a 0x41000000 0x00 0x400000>; - reg-names = "pcie-apb\0pcie-dbi"; - resets = <0x02 0x211 0x02 0x220>; - reset-names = "pcie\0periph"; - rockchip,pipe-grf = <0x61>; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xfa 0x01>; - phandle = <0xe9>; - }; - }; - - ethernet@fe1b0000 { - compatible = "rockchip,rk3588-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe1b0000 0x00 0x10000>; - interrupts = <0x00 0xe3 0x04 0x00 0xe2 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0xb7>; - rockchip,php_grf = <0x61>; - local-mac-address = [d2 5e 8a 13 5d df]; - clocks = <0x02 0x144 0x02 0x145 0x02 0x167 0x02 0x16c 0x02 0x142>; - clock-names = "stmmaceth\0clk_mac_ref\0pclk_mac\0aclk_mac\0ptp_ref"; - resets = <0x02 0x20a>; - reset-names = "stmmaceth"; - power-domains = <0x4d 0x21>; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0xeb>; - snps,mtl-rx-config = <0xec>; - snps,mtl-tx-config = <0xed>; - snps,txpbl = <0x04>; - status = "okay"; - phy-mode = "rgmii-rxid"; - clock_in_out = "output"; - snps,reset-gpio = <0xee 0x17 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - pinctrl-names = "default"; - pinctrl-0 = <0xef 0xf0 0xf1 0xf2 0xf3>; - tx_delay = <0x45>; - phy-handle = <0xf4>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x01>; - phandle = <0xf4>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0xeb>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x02>; - phandle = <0xec>; - - queue0 { - }; - - queue1 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x02>; - phandle = <0xed>; - - queue0 { - }; - - queue1 { - }; - }; - }; - - ethernet@fe1c0000 { - compatible = "rockchip,rk3588-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe1c0000 0x00 0x10000>; - interrupts = <0x00 0xea 0x04 0x00 0xe9 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0xb7>; - rockchip,php_grf = <0x61>; - local-mac-address = [d6 5e 8a 13 5d df]; - clocks = <0x02 0x144 0x02 0x145 0x02 0x168 0x02 0x16d 0x02 0x143>; - clock-names = "stmmaceth\0clk_mac_ref\0pclk_mac\0aclk_mac\0ptp_ref"; - resets = <0x02 0x20b>; - reset-names = "stmmaceth"; - power-domains = <0x4d 0x21>; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0xf5>; - snps,mtl-rx-config = <0xf6>; - snps,mtl-tx-config = <0xf7>; - snps,txpbl = <0x04>; - status = "okay"; - phy-mode = "rgmii-rxid"; - clock_in_out = "output"; - snps,reset-gpio = <0xee 0x0f 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - pinctrl-names = "default"; - pinctrl-0 = <0xf8 0xf9 0xfa 0xfb 0xfc>; - tx_delay = <0x42>; - phy-handle = <0xfd>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x01>; - phandle = <0xfd>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0xf5>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x02>; - phandle = <0xf6>; - - queue0 { - }; - - queue1 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x02>; - phandle = <0xf7>; - - queue0 { - }; - - queue1 { - }; - }; - }; - - sata@fe210000 { - compatible = "rockchip,rk-ahci\0snps,dwc-ahci"; - reg = <0x00 0xfe210000 0x00 0x1000>; - clocks = <0x02 0x171 0x02 0x16e 0x02 0x174 0x02 0x163 0x02 0x17e>; - clock-names = "sata\0pmalive\0rxoob\0ref\0asic"; - interrupts = <0x00 0x111 0x04>; - interrupt-names = "hostc"; - phys = <0xea 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - status = "disabled"; - }; - - sata@fe230000 { - compatible = "rockchip,rk-ahci\0snps,dwc-ahci"; - reg = <0x00 0xfe230000 0x00 0x1000>; - clocks = <0x02 0x173 0x02 0x170 0x02 0x176 0x02 0x165 0x02 0x180>; - clock-names = "sata\0pmalive\0rxoob\0ref\0asic"; - interrupts = <0x00 0x113 0x04>; - interrupt-names = "hostc"; - phys = <0x5b 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - status = "disabled"; - }; - - spi@fe2b0000 { - compatible = "rockchip,sfc"; - reg = <0x00 0xfe2b0000 0x00 0x4000>; - interrupts = <0x00 0xce 0x04>; - clocks = <0x02 0x13d 0x02 0x13e>; - clock-names = "clk_sfc\0hclk_sfc"; - assigned-clocks = <0x02 0x13d>; - assigned-clock-rates = <0x5f5e100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - mmc@fe2c0000 { - compatible = "rockchip,rk3588-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2c0000 0x00 0x4000>; - interrupts = <0x00 0xcb 0x04>; - clocks = <0x0e 0x17 0x0e 0x09 0x02 0x2c2 0x02 0x2c3>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - max-frequency = <0x8f0d180>; - pinctrl-names = "default"; - pinctrl-0 = <0xfe 0xff 0x100 0x101>; - power-domains = <0x4d 0x28>; - status = "okay"; - no-sdio; - no-mmc; - bus-width = <0x04>; - cap-mmc-highspeed; - cap-sd-highspeed; - disable-wp; - sd-uhs-sdr104; - vqmmc-supply = <0x102>; - }; - - mmc@fe2d0000 { - compatible = "rockchip,rk3588-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2d0000 0x00 0x4000>; - interrupts = <0x00 0xcc 0x04>; - clocks = <0x02 0x199 0x02 0x19a 0x02 0x2c0 0x02 0x2c1>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - max-frequency = <0xbebc200>; - pinctrl-names = "default"; - pinctrl-0 = <0x103>; - power-domains = <0x4d 0x25>; - status = "disabled"; - }; - - mmc@fe2e0000 { - compatible = "rockchip,rk3588-dwcmshc\0rockchip,dwcmshc-sdhci"; - reg = <0x00 0xfe2e0000 0x00 0x10000>; - interrupts = <0x00 0xcd 0x04>; - assigned-clocks = <0x02 0x13b 0x02 0x13c 0x02 0x13a>; - assigned-clock-rates = <0xbebc200 0x16e3600 0xbebc200>; - clocks = <0x02 0x13a 0x02 0x138 0x02 0x139 0x02 0x13b 0x02 0x13c>; - clock-names = "core\0bus\0axi\0block\0timer"; - resets = <0x02 0x1f6 0x02 0x1f4 0x02 0x1f5 0x02 0x1f7 0x02 0x1f8>; - reset-names = "core\0bus\0axi\0block\0timer"; - max-frequency = <0xbebc200>; - status = "okay"; - bus-width = <0x08>; - no-sdio; - no-sd; - non-removable; - mmc-hs400-1_8v; - mmc-hs400-enhanced-strobe; - }; - - crypto@fe370000 { - compatible = "rockchip,rk3588-crypto"; - reg = <0x00 0xfe370000 0x00 0x2000>; - interrupts = <0x00 0xd1 0x04>; - clocks = <0x0e 0x0b 0x0e 0x0c 0x0e 0x14 0x0e 0x15>; - clock-names = "aclk\0hclk\0sclk\0pka"; - resets = <0x104 0x0f>; - reset-names = "crypto-rst"; - status = "disabled"; - }; - - rng@fe378000 { - compatible = "rockchip,trngv1"; - reg = <0x00 0xfe378000 0x00 0x200>; - interrupts = <0x00 0x190 0x04>; - clocks = <0x0e 0x0c>; - clock-names = "hclk_trng"; - resets = <0x104 0x30>; - reset-names = "reset"; - status = "okay"; - }; - - i2s@fe470000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfe470000 0x00 0x1000>; - interrupts = <0x00 0xb4 0x04>; - clocks = <0x02 0x33 0x02 0x37 0x02 0x30>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x31 0x02 0x35>; - assigned-clock-parents = <0x02 0x05 0x02 0x05>; - dmas = <0x64 0x00 0x64 0x01>; - dma-names = "tx\0rx"; - power-domains = <0x4d 0x26>; - resets = <0x02 0x77 0x02 0x7a>; - reset-names = "tx-m\0rx-m"; - rockchip,clk-trcm = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x105 0x106 0x107 0x108>; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x1b4>; - }; - - i2s@fe480000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfe480000 0x00 0x1000>; - interrupts = <0x00 0xb5 0x04>; - clocks = <0x02 0x28c 0x02 0x290 0x02 0x288>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0x64 0x02 0x64 0x03>; - dma-names = "tx\0rx"; - resets = <0x02 0xc002a 0x02 0xc002d>; - reset-names = "tx-m\0rx-m"; - rockchip,clk-trcm = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x109 0x10a 0x10b 0x10c 0x10d 0x10e 0x10f 0x110 0x111 0x112>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - i2s@fe490000 { - compatible = "rockchip,rk3588-i2s\0rockchip,rk3066-i2s"; - reg = <0x00 0xfe490000 0x00 0x1000>; - interrupts = <0x00 0xb6 0x04>; - clocks = <0x02 0x27 0x02 0x22>; - clock-names = "i2s_clk\0i2s_hclk"; - assigned-clocks = <0x02 0x24>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd3 0x00 0xd3 0x01>; - dma-names = "tx\0rx"; - power-domains = <0x4d 0x26>; - rockchip,clk-trcm = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x113 0x114 0x115 0x116>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - i2s@fe4a0000 { - compatible = "rockchip,rk3588-i2s\0rockchip,rk3066-i2s"; - reg = <0x00 0xfe4a0000 0x00 0x1000>; - interrupts = <0x00 0xb7 0x04>; - clocks = <0x02 0x2d 0x02 0x23>; - clock-names = "i2s_clk\0i2s_hclk"; - assigned-clocks = <0x02 0x2a>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd3 0x02 0xd3 0x03>; - dma-names = "tx\0rx"; - power-domains = <0x4d 0x26>; - rockchip,clk-trcm = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x117 0x118 0x119 0x11a>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - pdm@fe4b0000 { - compatible = "rockchip,rk3588-pdm"; - reg = <0x00 0xfe4b0000 0x00 0x1000>; - clocks = <0x02 0x29f 0x02 0x29e>; - clock-names = "pdm_clk\0pdm_hclk"; - dmas = <0x64 0x04>; - dma-names = "rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x11b 0x11c 0x11d 0x11e 0x11f 0x120>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - pdm@fe4c0000 { - compatible = "rockchip,rk3588-pdm"; - reg = <0x00 0xfe4c0000 0x00 0x1000>; - clocks = <0x02 0x3b 0x02 0x3a>; - clock-names = "pdm_clk\0pdm_hclk"; - assigned-clocks = <0x02 0x3b>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd3 0x04>; - dma-names = "rx"; - power-domains = <0x4d 0x26>; - pinctrl-names = "default"; - pinctrl-0 = <0x121 0x122 0x123 0x124 0x125 0x126>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - vad@fe4d0000 { - compatible = "rockchip,rk3588-vad"; - reg = <0x00 0xfe4d0000 0x00 0x1000>; - reg-names = "vad"; - clocks = <0x02 0x2a0>; - clock-names = "hclk"; - interrupts = <0x00 0xca 0x04>; - rockchip,audio-src = <0x00>; - rockchip,det-channel = <0x00>; - rockchip,mode = <0x00>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-tx@fe4e0000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfe4e0000 0x00 0x1000>; - interrupts = <0x00 0xc1 0x04>; - dmas = <0x64 0x05>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x41 0x02 0x3e>; - assigned-clocks = <0x02 0x3f>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x26>; - pinctrl-names = "default"; - pinctrl-0 = <0x127>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-tx@fe4f0000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfe4f0000 0x00 0x1000>; - interrupts = <0x00 0xc2 0x04>; - dmas = <0xd3 0x05>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x47 0x02 0x44>; - assigned-clocks = <0x02 0x45>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x26>; - pinctrl-names = "default"; - pinctrl-0 = <0x128>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x1b1>; - }; - - codec-digital@fe500000 { - compatible = "rockchip,rk3588-codec-digital\0rockchip,codec-digital-v1"; - reg = <0x00 0xfe500000 0x00 0x1000>; - clocks = <0x02 0x29 0x02 0x2f>; - clock-names = "dac\0pclk"; - power-domains = <0x4d 0x26>; - resets = <0x02 0x84>; - reset-names = "reset"; - rockchip,grf = <0xb7>; - rockchip,pwm-output-mode; - pinctrl-names = "default"; - pinctrl-0 = <0x129>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - hwspinlock@fe5a0000 { - compatible = "rockchip,hwspinlock"; - reg = <0x00 0xfe5a0000 0x00 0x100>; - #hwlock-cells = <0x01>; - }; - - interrupt-controller@fe600000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0xfe600000 0x00 0x10000 0x00 0xfe680000 0x00 0x100000>; - interrupts = <0x01 0x09 0x04>; - phandle = <0x01>; - }; - - dma-controller@fea10000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfea10000 0x00 0x4000>; - interrupts = <0x00 0x56 0x04 0x00 0x57 0x04>; - clocks = <0x02 0x78>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0x64>; - }; - - dma-controller@fea30000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfea30000 0x00 0x4000>; - interrupts = <0x00 0x58 0x04 0x00 0x59 0x04>; - clocks = <0x02 0x79>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0xd3>; - }; - - can@fea50000 { - compatible = "rockchip,can-2.0"; - reg = <0x00 0xfea50000 0x00 0x1000>; - interrupts = <0x00 0x155 0x04>; - clocks = <0x02 0x70 0x02 0x6f>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x02 0xb9 0x02 0xb8>; - reset-names = "can\0can-apb"; - pinctrl-names = "default"; - pinctrl-0 = <0x12a>; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - }; - - can@fea60000 { - compatible = "rockchip,can-2.0"; - reg = <0x00 0xfea60000 0x00 0x1000>; - interrupts = <0x00 0x156 0x04>; - clocks = <0x02 0x72 0x02 0x71>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x02 0xbb 0x02 0xba>; - reset-names = "can\0can-apb"; - pinctrl-names = "default"; - pinctrl-0 = <0x12b>; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "okay"; - assigned-clocks = <0x02 0x72>; - assigned-clock-rates = <0xbebc200>; - }; - - can@fea70000 { - compatible = "rockchip,can-2.0"; - reg = <0x00 0xfea70000 0x00 0x1000>; - interrupts = <0x00 0x157 0x04>; - clocks = <0x02 0x74 0x02 0x73>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x02 0xbd 0x02 0xbc>; - reset-names = "can\0can-apb"; - pinctrl-names = "default"; - pinctrl-0 = <0x12c>; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - }; - - decompress@fea80000 { - compatible = "rockchip,hw-decompress"; - reg = <0x00 0xfea80000 0x00 0x1000>; - interrupts = <0x00 0x55 0x04>; - clocks = <0x02 0x75 0x02 0x77 0x02 0x76>; - clock-names = "aclk\0dclk\0pclk"; - resets = <0x02 0x118>; - reset-names = "dresetn"; - status = "disabled"; - }; - - i2c@fea90000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfea90000 0x00 0x1000>; - clocks = <0x02 0x8d 0x02 0x85>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x13e 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x12d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - rk8602@42 { - compatible = "rockchip,rk8602"; - reg = <0x42>; - vin-supply = <0x63>; - regulator-compatible = "rk860x-reg"; - regulator-name = "vdd_npu_s0"; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0xe7ef0>; - regulator-ramp-delay = <0x8fc>; - rockchip,suspend-voltage-selector = <0x01>; - regulator-boot-on; - regulator-always-on; - phandle = <0x9b>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - - i2c@feaa0000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfeaa0000 0x00 0x1000>; - clocks = <0x02 0x8e 0x02 0x86>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x13f 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x12e>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@feab0000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfeab0000 0x00 0x1000>; - clocks = <0x02 0x8f 0x02 0x87>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x140 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x12f>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - es8388@11 { - status = "disabled"; - #sound-dai-cells = <0x00>; - compatible = "everest,es8388\0everest,es8323"; - reg = <0x11>; - clocks = <0x02 0x39>; - clock-names = "mclk"; - assigned-clocks = <0x02 0x39>; - assigned-clock-rates = <0xbb8000>; - pinctrl-names = "default"; - pinctrl-0 = <0x130>; - phandle = <0x1b5>; - }; - }; - - i2c@feac0000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfeac0000 0x00 0x1000>; - clocks = <0x02 0x90 0x02 0x88>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x141 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x131>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@fead0000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfead0000 0x00 0x1000>; - clocks = <0x02 0x91 0x02 0x89>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x142 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x132>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - timer@feae0000 { - compatible = "rockchip,rk3588-timer\0rockchip,rk3288-timer"; - reg = <0x00 0xfeae0000 0x00 0x20>; - interrupts = <0x00 0x121 0x04>; - clocks = <0x02 0x5c 0x02 0x5f>; - clock-names = "pclk\0timer"; - }; - - watchdog@feaf0000 { - compatible = "snps,dw-wdt"; - reg = <0x00 0xfeaf0000 0x00 0x100>; - clocks = <0x02 0x6c 0x02 0x6b>; - clock-names = "tclk\0pclk"; - interrupts = <0x00 0x13b 0x04>; - status = "okay"; - }; - - spi@feb00000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfeb00000 0x00 0x1000>; - interrupts = <0x00 0x146 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0xa3 0x02 0x9e>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x64 0x0e 0x64 0x0f>; - dma-names = "tx\0rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x133 0x134 0x135>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@feb10000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfeb10000 0x00 0x1000>; - interrupts = <0x00 0x147 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0xa4 0x02 0x9f>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x64 0x10 0x64 0x11>; - dma-names = "tx\0rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x136 0x137 0x138>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@feb20000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfeb20000 0x00 0x1000>; - interrupts = <0x00 0x148 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0xa5 0x02 0xa0>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0xd3 0x0f 0xd3 0x10>; - dma-names = "tx\0rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x139 0x13a>; - num-cs = <0x01>; - status = "okay"; - assigned-clocks = <0x02 0xa5>; - assigned-clock-rates = <0xbebc200>; - - rk806single@0 { - compatible = "rockchip,rk806"; - spi-max-frequency = <0xf4240>; - reg = <0x00>; - interrupt-parent = <0x13b>; - interrupts = <0x07 0x08>; - pinctrl-names = "default\0pmic-power-off"; - pinctrl-0 = <0x13c 0x13d 0x13e 0x13f>; - pinctrl-1 = <0x140>; - low_voltage_threshold = <0xbb8>; - shutdown_voltage_threshold = <0xa8c>; - shutdown_temperture_threshold = <0xa0>; - hotdie_temperture_threshold = <0x73>; - pmic-reset-func = <0x01>; - vcc1-supply = <0x63>; - vcc2-supply = <0x63>; - vcc3-supply = <0x63>; - vcc4-supply = <0x63>; - vcc5-supply = <0x63>; - vcc6-supply = <0x63>; - vcc7-supply = <0x63>; - vcc8-supply = <0x63>; - vcc9-supply = <0x63>; - vcc10-supply = <0x63>; - vcc11-supply = <0x141>; - vcc12-supply = <0x63>; - vcc13-supply = <0x142>; - vcc14-supply = <0x142>; - vcca-supply = <0x63>; - - pwrkey { - status = "okay"; - }; - - pinctrl_rk806 { - gpio-controller; - #gpio-cells = <0x02>; - - rk806_dvs1_null { - pins = "gpio_pwrctrl2"; - function = "pin_fun0"; - phandle = <0x13d>; - }; - - rk806_dvs1_slp { - pins = "gpio_pwrctrl1"; - function = "pin_fun1"; - }; - - rk806_dvs1_pwrdn { - pins = "gpio_pwrctrl1"; - function = "pin_fun2"; - phandle = <0x140>; - }; - - rk806_dvs1_rst { - pins = "gpio_pwrctrl1"; - function = "pin_fun3"; - }; - - rk806_dvs2_null { - pins = "gpio_pwrctrl2"; - function = "pin_fun0"; - phandle = <0x13e>; - }; - - rk806_dvs2_slp { - pins = "gpio_pwrctrl2"; - function = "pin_fun1"; - }; - - rk806_dvs2_pwrdn { - pins = "gpio_pwrctrl2"; - function = "pin_fun2"; - }; - - rk806_dvs2_rst { - pins = "gpio_pwrctrl2"; - function = "pin_fun3"; - }; - - rk806_dvs2_dvs { - pins = "gpio_pwrctrl2"; - function = "pin_fun4"; - }; - - rk806_dvs2_gpio { - pins = "gpio_pwrctrl2"; - function = "pin_fun5"; - }; - - rk806_dvs3_null { - pins = "gpio_pwrctrl3"; - function = "pin_fun0"; - phandle = <0x13f>; - }; - - rk806_dvs3_slp { - pins = "gpio_pwrctrl3"; - function = "pin_fun1"; - }; - - rk806_dvs3_pwrdn { - pins = "gpio_pwrctrl3"; - function = "pin_fun2"; - }; - - rk806_dvs3_rst { - pins = "gpio_pwrctrl3"; - function = "pin_fun3"; - }; - - rk806_dvs3_dvs { - pins = "gpio_pwrctrl3"; - function = "pin_fun4"; - }; - - rk806_dvs3_gpio { - pins = "gpio_pwrctrl3"; - function = "pin_fun5"; - }; - }; - - regulators { - - DCDC_REG1 { - regulator-boot-on; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0xe7ef0>; - regulator-ramp-delay = <0x30d4>; - regulator-name = "vdd_gpu_s0"; - regulator-enable-ramp-delay = <0x190>; - phandle = <0x4f>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0xe7ef0>; - regulator-ramp-delay = <0x30d4>; - regulator-name = "vdd_cpu_lit_s0"; - phandle = <0x12>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xa4cb8>; - regulator-max-microvolt = <0xb71b0>; - regulator-ramp-delay = <0x30d4>; - regulator-name = "vdd_log_s0"; - phandle = <0x3c>; - - regulator-state-mem { - regulator-suspend-microvolt = <0xb71b0>; - regulator-on-in-suspend; - }; - }; - - DCDC_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0xe7ef0>; - regulator-init-microvolt = <0xb71b0>; - regulator-ramp-delay = <0x30d4>; - regulator-name = "vdd_vdenc_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xa4cb8>; - regulator-max-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x30d4>; - regulator-name = "vdd_ddr_s0"; - phandle = <0x3b>; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <0xcf850>; - }; - }; - - DCDC_REG6 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vdd2_ddr_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - DCDC_REG7 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1e8480>; - regulator-max-microvolt = <0x1e8480>; - regulator-name = "vdd_2v0_pldo_s3"; - phandle = <0x141>; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x1e8480>; - }; - }; - - DCDC_REG8 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vcc_3v3_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x325aa0>; - }; - }; - - DCDC_REG9 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vddq_ddr_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG10 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcc_1v8_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x1b7740>; - }; - }; - - PLDO_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "avcc_1v8_s0"; - phandle = <0x1ba>; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-on-in-suspend; - }; - }; - - PLDO_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcc_1v8_s0"; - phandle = <0x15e>; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <0x1b7740>; - regulator-on-in-suspend; - }; - }; - - PLDO_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x124f80>; - regulator-max-microvolt = <0x124f80>; - regulator-name = "avdd_1v2_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - PLDO_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vcc_3v3_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - PLDO_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vccio_sd_s0"; - phandle = <0x102>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - PLDO_REG6 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "pldo6_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x1b7740>; - }; - }; - - NLDO_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b0>; - regulator-max-microvolt = <0xb71b0>; - regulator-name = "vdd_0v75_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0xb71b0>; - }; - }; - - NLDO_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xcf850>; - regulator-max-microvolt = <0xcf850>; - regulator-name = "vdd_ddr_pll_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <0xcf850>; - }; - }; - - NLDO_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b0>; - regulator-max-microvolt = <0xb71b0>; - regulator-name = "avdd_0v75_s0"; - phandle = <0x1bb>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - NLDO_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xcf850>; - regulator-max-microvolt = <0xcf850>; - regulator-name = "vdd_0v85_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - NLDO_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b0>; - regulator-max-microvolt = <0xb71b0>; - regulator-name = "vdd_0v75_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - }; - }; - - spi@feb30000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfeb30000 0x00 0x1000>; - interrupts = <0x00 0x149 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0xa6 0x02 0xa1>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0xd3 0x11 0xd3 0x12>; - dma-names = "tx\0rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x143 0x144 0x145>; - num-cs = <0x02>; - status = "disabled"; - }; - - serial@feb40000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb40000 0x00 0x100>; - interrupts = <0x00 0x14c 0x04>; - clocks = <0x02 0xb7 0x02 0xab>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x64 0x08 0x64 0x09>; - pinctrl-names = "default"; - pinctrl-0 = <0x146>; - status = "disabled"; - }; - - serial@feb50000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb50000 0x00 0x100>; - interrupts = <0x00 0x14d 0x04>; - clocks = <0x02 0xbb 0x02 0xac>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x64 0x0a 0x64 0x0b>; - pinctrl-names = "default"; - pinctrl-0 = <0x147>; - status = "disabled"; - }; - - serial@feb60000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb60000 0x00 0x100>; - interrupts = <0x00 0x14e 0x04>; - clocks = <0x02 0xbf 0x02 0xad>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x64 0x0c 0x64 0x0d>; - pinctrl-names = "default"; - pinctrl-0 = <0x148>; - status = "disabled"; - }; - - serial@feb70000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb70000 0x00 0x100>; - interrupts = <0x00 0x14f 0x04>; - clocks = <0x02 0xc3 0x02 0xae>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd3 0x09 0xd3 0x0a>; - pinctrl-names = "default"; - pinctrl-0 = <0x149>; - status = "disabled"; - }; - - serial@feb80000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb80000 0x00 0x100>; - interrupts = <0x00 0x150 0x04>; - clocks = <0x02 0xc7 0x02 0xaf>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd3 0x0b 0xd3 0x0c>; - pinctrl-names = "default"; - pinctrl-0 = <0x14a>; - status = "disabled"; - }; - - serial@feb90000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb90000 0x00 0x100>; - interrupts = <0x00 0x151 0x04>; - clocks = <0x02 0xcb 0x02 0xb0>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd3 0x0d 0xd3 0x0e>; - pinctrl-names = "default"; - pinctrl-0 = <0x14b 0x14c>; - status = "okay"; - }; - - serial@feba0000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeba0000 0x00 0x100>; - interrupts = <0x00 0x152 0x04>; - clocks = <0x02 0xcf 0x02 0xb1>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd4 0x07 0xd4 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0x14d>; - status = "disabled"; - }; - - serial@febb0000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfebb0000 0x00 0x100>; - interrupts = <0x00 0x153 0x04>; - clocks = <0x02 0xd3 0x02 0xb2>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd4 0x09 0xd4 0x0a>; - pinctrl-names = "default"; - pinctrl-0 = <0x14e>; - status = "disabled"; - }; - - serial@febc0000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfebc0000 0x00 0x100>; - interrupts = <0x00 0x154 0x04>; - clocks = <0x02 0xd7 0x02 0xb3>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd4 0x0b 0xd4 0x0c>; - pinctrl-names = "default"; - pinctrl-0 = <0x14f>; - status = "disabled"; - }; - - pwm@febd0000 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebd0000 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x150>; - clocks = <0x02 0x54 0x02 0x53>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febd0010 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebd0010 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x151>; - clocks = <0x02 0x54 0x02 0x53>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febd0020 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebd0020 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x152>; - clocks = <0x02 0x54 0x02 0x53>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febd0030 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebd0030 0x00 0x10>; - interrupts = <0x00 0x15a 0x04 0x00 0x15b 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x153>; - clocks = <0x02 0x54 0x02 0x53>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febe0000 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebe0000 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x154>; - clocks = <0x02 0x57 0x02 0x56>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febe0010 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebe0010 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x155>; - clocks = <0x02 0x57 0x02 0x56>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febe0020 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebe0020 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x156>; - clocks = <0x02 0x57 0x02 0x56>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febe0030 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebe0030 0x00 0x10>; - interrupts = <0x00 0x15c 0x04 0x00 0x15d 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x157>; - clocks = <0x02 0x57 0x02 0x56>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febf0000 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebf0000 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x158>; - clocks = <0x02 0x5a 0x02 0x59>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febf0010 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebf0010 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x159>; - clocks = <0x02 0x5a 0x02 0x59>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febf0020 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebf0020 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x15a>; - clocks = <0x02 0x5a 0x02 0x59>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febf0030 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebf0030 0x00 0x10>; - interrupts = <0x00 0x15e 0x04 0x00 0x15f 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x15b>; - clocks = <0x02 0x5a 0x02 0x59>; - clock-names = "pwm\0pclk"; - status = "okay"; - phandle = <0x1ca>; - }; - - tsadc@fec00000 { - compatible = "rockchip,rk3588-tsadc"; - reg = <0x00 0xfec00000 0x00 0x400>; - interrupts = <0x00 0x18d 0x04>; - clocks = <0x02 0xaa 0x02 0xa9>; - clock-names = "tsadc\0apb_pclk"; - assigned-clocks = <0x02 0xaa>; - assigned-clock-rates = <0x1e8480>; - resets = <0x02 0xc1 0x02 0xc0>; - reset-names = "tsadc\0tsadc-apb"; - #thermal-sensor-cells = <0x01>; - rockchip,hw-tshut-temp = <0x1d4c0>; - rockchip,hw-tshut-mode = <0x00>; - rockchip,hw-tshut-polarity = <0x00>; - pinctrl-names = "gpio\0otpout"; - pinctrl-0 = <0x15c>; - pinctrl-1 = <0x15d>; - status = "okay"; - phandle = <0x4a>; - }; - - saradc@fec10000 { - compatible = "rockchip,rk3588-saradc"; - reg = <0x00 0xfec10000 0x00 0x10000>; - interrupts = <0x00 0x18e 0x04>; - #io-channel-cells = <0x01>; - clocks = <0x02 0x9d 0x02 0x9c>; - clock-names = "saradc\0apb_pclk"; - resets = <0x02 0xbe>; - reset-names = "saradc-apb"; - status = "okay"; - vref-supply = <0x15e>; - phandle = <0x1b3>; - }; - - mailbox@fec60000 { - compatible = "rockchip,rk3588-mailbox\0rockchip,rk3368-mailbox"; - reg = <0x00 0xfec60000 0x00 0x200>; - interrupts = <0x00 0x3d 0x04 0x00 0x3e 0x04 0x00 0x3f 0x04 0x00 0x40 0x04>; - clocks = <0x02 0x4c>; - clock-names = "pclk_mailbox"; - #mbox-cells = <0x01>; - status = "disabled"; - }; - - mailbox@fec70000 { - compatible = "rockchip,rk3588-mailbox\0rockchip,rk3368-mailbox"; - reg = <0x00 0xfec70000 0x00 0x200>; - interrupts = <0x00 0x45 0x04 0x00 0x46 0x04 0x00 0x47 0x04 0x00 0x48 0x04>; - clocks = <0x02 0x4d>; - clock-names = "pclk_mailbox"; - #mbox-cells = <0x01>; - status = "disabled"; - }; - - i2c@fec80000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfec80000 0x00 0x1000>; - clocks = <0x02 0x92 0x02 0x8a>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x143 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x15f>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - clock-frequency = <0x61a80>; - - gpio@21 { - compatible = "nxp,pca9555"; - reg = <0x21>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-group-num = <0xc8>; - status = "disabled"; - phandle = <0x1b8>; - }; - - hym8563@51 { - compatible = "haoyu,hym8563"; - reg = <0x51>; - #clock-cells = <0x00>; - clock-frequency = <0x8000>; - clock-output-names = "hym8563"; - pinctrl-names = "default"; - pinctrl-0 = <0x160>; - interrupt-parent = <0x13b>; - interrupts = <0x08 0x08>; - wakeup-source; - status = "disabled"; - phandle = <0x1c0>; - }; - - fusb302@22 { - compatible = "fcs,fusb302"; - reg = <0x22>; - interrupt-parent = <0x13b>; - interrupts = <0x1b 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0x161>; - vbus-supply = <0x162>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x163>; - phandle = <0x54>; - }; - }; - }; - - connector { - compatible = "usb-c-connector"; - label = "USB-C"; - data-role = "dual"; - power-role = "dual"; - try-power-role = "sink"; - op-sink-microwatt = <0xf4240>; - sink-pdos = <0x4019064>; - source-pdos = <0x401912c>; - - altmodes { - #address-cells = <0x01>; - #size-cells = <0x00>; - - altmode@0 { - reg = <0x00>; - svid = <0xff01>; - vdo = <0xffffffff>; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint { - remote-endpoint = <0x164>; - phandle = <0x16f>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint { - remote-endpoint = <0x165>; - phandle = <0x170>; - }; - }; - }; - }; - }; - }; - - i2c@fec90000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfec90000 0x00 0x1000>; - clocks = <0x02 0x93 0x02 0x8b>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x144 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x166>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@feca0000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfeca0000 0x00 0x1000>; - clocks = <0x02 0x94 0x02 0x8c>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x145 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x167>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@fecb0000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfecb0000 0x00 0x1000>; - interrupts = <0x00 0x14a 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0xa7 0x02 0xa2>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0xd4 0x0d 0xd4 0x0e>; - dma-names = "tx\0rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x168 0x169 0x16a>; - num-cs = <0x02>; - status = "disabled"; - }; - - otp@fecc0000 { - compatible = "rockchip,rk3588-otp"; - reg = <0x00 0xfecc0000 0x00 0x400>; - #address-cells = <0x01>; - #size-cells = <0x01>; - clocks = <0x02 0x96 0x02 0x95 0x02 0x97 0x02 0x99>; - clock-names = "otpc\0apb\0arb\0phy"; - resets = <0x02 0x12a 0x02 0x129 0x02 0x12b>; - reset-names = "otpc\0apb\0arb"; - - cpu-code@2 { - reg = <0x02 0x02>; - phandle = <0x29>; - }; - - specification-serial-number@6 { - reg = <0x06 0x01>; - bits = <0x00 0x05>; - phandle = <0x20>; - }; - - id@7 { - reg = <0x07 0x10>; - phandle = <0x27>; - }; - - cpu-version@1c { - reg = <0x1c 0x01>; - bits = <0x03 0x03>; - phandle = <0x28>; - }; - - cpub0-leakage@17 { - reg = <0x17 0x01>; - phandle = <0x23>; - }; - - cpub1-leakage@18 { - reg = <0x18 0x01>; - phandle = <0x25>; - }; - - cpul-leakage@19 { - reg = <0x19 0x01>; - phandle = <0x1f>; - }; - - log-leakage@1a { - reg = <0x1a 0x01>; - phandle = <0x3d>; - }; - - gpu-leakage@1b { - reg = <0x1b 0x01>; - phandle = <0x50>; - }; - - npu-leakage@28 { - reg = <0x28 0x01>; - phandle = <0x9c>; - }; - - codec-leakage@29 { - reg = <0x29 0x01>; - }; - }; - - mailbox@fece0000 { - compatible = "rockchip,rk3588-mailbox\0rockchip,rk3368-mailbox"; - reg = <0x00 0xfece0000 0x00 0x200>; - interrupts = <0x00 0x4d 0x04 0x00 0x4e 0x04 0x00 0x4f 0x04 0x00 0x50 0x04>; - clocks = <0x02 0x4e>; - clock-names = "pclk_mailbox"; - #mbox-cells = <0x01>; - status = "disabled"; - }; - - dma-controller@fed10000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfed10000 0x00 0x4000>; - interrupts = <0x00 0x5a 0x04 0x00 0x5b 0x04>; - clocks = <0x02 0x7a>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0xd4>; - }; - - phy@fed60000 { - compatible = "rockchip,rk3588-hdptx-phy"; - reg = <0x00 0xfed60000 0x00 0x2000>; - clocks = <0x02 0x2b5 0x02 0x267>; - clock-names = "ref\0apb"; - resets = <0x02 0x485 0x02 0xc003b 0x02 0xc003c 0x02 0xc003d>; - reset-names = "apb\0init\0cmn\0lane"; - rockchip,grf = <0x16b>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0xe3>; - }; - - hdmiphy@fed60000 { - compatible = "rockchip,rk3588-hdptx-phy-hdmi"; - reg = <0x00 0xfed60000 0x00 0x2000>; - clocks = <0x02 0x2b5 0x02 0x267>; - clock-names = "ref\0apb"; - resets = <0x02 0x48e 0x02 0x485 0x02 0xc003b 0x02 0xc003c 0x02 0xc003d 0x02 0x48c 0x02 0x48d>; - reset-names = "phy\0apb\0init\0cmn\0lane\0ropll\0lcpll"; - rockchip,grf = <0x16b>; - #phy-cells = <0x00>; - status = "okay"; - phandle = <0xdf>; - - clk-port { - #clock-cells = <0x00>; - status = "okay"; - phandle = <0x2e>; - }; - }; - - phy@fed80000 { - compatible = "rockchip,rk3588-usbdp-phy"; - reg = <0x00 0xfed80000 0x00 0x10000>; - rockchip,u2phy-grf = <0x16c>; - rockchip,usb-grf = <0x5f>; - rockchip,usbdpphy-grf = <0x16d>; - rockchip,vo-grf = <0xd7>; - clocks = <0x02 0x2b6 0x02 0x27f 0x02 0x269 0x16e>; - clock-names = "refclk\0immortal\0pclk\0utmi"; - resets = <0x02 0x28 0x02 0x29 0x02 0x2a 0x02 0x2b 0x02 0x482>; - reset-names = "init\0cmn\0lane\0pcs_apb\0pma_apb"; - status = "okay"; - orientation-switch; - svid = <0xff01>; - sbu1-dc-gpios = <0xe0 0x06 0x00>; - sbu2-dc-gpios = <0xe0 0x07 0x00>; - - dp-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0xd8>; - }; - - u3-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0x53>; - }; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x16f>; - phandle = <0x164>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x170>; - phandle = <0x165>; - }; - }; - }; - - phy@feda0000 { - compatible = "rockchip,rk3588-mipi-dcphy"; - reg = <0x00 0xfeda0000 0x00 0x10000>; - rockchip,grf = <0x171>; - clocks = <0x02 0x108 0x02 0x2b6>; - clock-names = "pclk\0ref"; - resets = <0x02 0xc0043 0x02 0x3e 0x02 0x3f 0x02 0xc0044>; - reset-names = "m_phy\0apb\0grf\0s_phy"; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x2a>; - }; - - phy@fedb0000 { - compatible = "rockchip,rk3588-mipi-dcphy"; - reg = <0x00 0xfedb0000 0x00 0x10000>; - rockchip,grf = <0x172>; - clocks = <0x02 0x109 0x02 0x2b6>; - clock-names = "pclk\0ref"; - resets = <0x02 0xc0045 0x02 0x43 0x02 0x44 0x02 0xc0046>; - reset-names = "m_phy\0apb\0grf\0s_phy"; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x2b>; - }; - - csi2-dphy0-hw@fedc0000 { - compatible = "rockchip,rk3588-csi2-dphy-hw"; - reg = <0x00 0xfedc0000 0x00 0x8000>; - clocks = <0x02 0x10c>; - clock-names = "pclk"; - resets = <0x02 0x17 0x02 0x16>; - reset-names = "srst_csiphy0\0srst_p_csiphy0"; - rockchip,grf = <0x173>; - rockchip,sys_grf = <0xb7>; - status = "disabled"; - phandle = <0x2c>; - }; - - phy@fee00000 { - compatible = "rockchip,rk3588-naneng-combphy"; - reg = <0x00 0xfee00000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x02 0x2bd 0x02 0x185 0x02 0x166>; - clock-names = "refclk\0apbclk\0phpclk"; - assigned-clocks = <0x02 0x2bd>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x02 0x20005 0x02 0x4d6>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x61>; - rockchip,pipe-phy-grf = <0x174>; - status = "disabled"; - phandle = <0xea>; - }; - - phy@fee20000 { - compatible = "rockchip,rk3588-naneng-combphy"; - reg = <0x00 0xfee20000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x02 0x2bf 0x02 0x187 0x02 0x166>; - clock-names = "refclk\0apbclk\0phpclk"; - assigned-clocks = <0x02 0x2bf>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x02 0x20007 0x02 0x4d8>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x61>; - rockchip,pipe-phy-grf = <0x175>; - rockchip,pcie1ln-sel-bits = <0x100 0x01 0x01 0x00>; - status = "disabled"; - phandle = <0x5b>; - }; - - sram@ff001000 { - compatible = "mmio-sram"; - reg = <0x00 0xff001000 0x00 0xef000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0xff001000 0xef000>; - - rkvdec-sram@0 { - reg = <0x00 0x78000>; - phandle = <0xae>; - }; - - rkvdec-sram@78000 { - reg = <0x78000 0x77000>; - phandle = <0xb0>; - }; - }; - - pinctrl { - compatible = "rockchip,rk3588-pinctrl"; - rockchip,grf = <0x176>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - phandle = <0x177>; - - gpio@fd8a0000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfd8a0000 0x00 0x100>; - interrupts = <0x00 0x115 0x04>; - clocks = <0x02 0x284 0x02 0x285>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-ranges = <0x177 0x00 0x00 0x20>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x13b>; - }; - - gpio@fec20000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfec20000 0x00 0x100>; - interrupts = <0x00 0x116 0x04>; - clocks = <0x02 0x7d 0x02 0x7e>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-ranges = <0x177 0x00 0x20 0x20>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x189>; - }; - - gpio@fec30000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfec30000 0x00 0x100>; - interrupts = <0x00 0x117 0x04>; - clocks = <0x02 0x7f 0x02 0x80>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-ranges = <0x177 0x00 0x40 0x20>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x1b9>; - }; - - gpio@fec40000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfec40000 0x00 0x100>; - interrupts = <0x00 0x118 0x04>; - clocks = <0x02 0x81 0x02 0x82>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-ranges = <0x177 0x00 0x60 0x20>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0xee>; - }; - - gpio@fec50000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfec50000 0x00 0x100>; - interrupts = <0x00 0x119 0x04>; - clocks = <0x02 0x83 0x02 0x84>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-ranges = <0x177 0x00 0x80 0x20>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0xe0>; - }; - - pcfg-pull-up { - bias-pull-up; - phandle = <0x17e>; - }; - - pcfg-pull-down { - bias-pull-down; - phandle = <0x181>; - }; - - pcfg-pull-none { - bias-disable; - phandle = <0x178>; - }; - - pcfg-pull-none-drv-level-2 { - bias-disable; - drive-strength = <0x02>; - phandle = <0x180>; - }; - - pcfg-pull-up-drv-level-1 { - bias-pull-up; - drive-strength = <0x01>; - phandle = <0x17f>; - }; - - pcfg-pull-up-drv-level-2 { - bias-pull-up; - drive-strength = <0x02>; - phandle = <0x179>; - }; - - pcfg-pull-up-drv-level-6 { - bias-pull-up; - drive-strength = <0x06>; - phandle = <0x17a>; - }; - - pcfg-pull-none-smt { - bias-disable; - input-schmitt-enable; - phandle = <0x17d>; - }; - - pcfg-pull-none-drv-level-1-smt { - bias-disable; - drive-strength = <0x01>; - input-schmitt-enable; - phandle = <0x17c>; - }; - - pcfg-pull-none-drv-level-5-smt { - bias-disable; - drive-strength = <0x05>; - input-schmitt-enable; - phandle = <0x17b>; - }; - - auddsm { - - auddsm-pins { - rockchip,pins = <0x03 0x01 0x04 0x178 0x03 0x02 0x04 0x178 0x03 0x03 0x04 0x178 0x03 0x04 0x04 0x178>; - phandle = <0x129>; - }; - }; - - bt1120 { - - bt1120-pins { - rockchip,pins = <0x04 0x08 0x02 0x178 0x04 0x00 0x02 0x178 0x04 0x01 0x02 0x178 0x04 0x02 0x02 0x178 0x04 0x03 0x02 0x178 0x04 0x04 0x02 0x178 0x04 0x05 0x02 0x178 0x04 0x06 0x02 0x178 0x04 0x07 0x02 0x178 0x04 0x0a 0x02 0x178 0x04 0x0b 0x02 0x178 0x04 0x0c 0x02 0x178 0x04 0x0d 0x02 0x178 0x04 0x0e 0x02 0x178 0x04 0x0f 0x02 0x178 0x04 0x10 0x02 0x178 0x04 0x11 0x02 0x178>; - phandle = <0x5c>; - }; - }; - - can0 { - - can0m0-pins { - rockchip,pins = <0x00 0x10 0x0b 0x178 0x00 0x0f 0x0b 0x178>; - phandle = <0x12a>; - }; - }; - - can1 { - - can1m1-pins { - rockchip,pins = <0x04 0x0a 0x0c 0x178 0x04 0x0b 0x0c 0x178>; - phandle = <0x12b>; - }; - }; - - can2 { - - can2m0-pins { - rockchip,pins = <0x03 0x14 0x09 0x178 0x03 0x15 0x09 0x178>; - phandle = <0x12c>; - }; - }; - - gmac1 { - - gmac1-miim { - rockchip,pins = <0x03 0x12 0x01 0x178 0x03 0x13 0x01 0x178>; - phandle = <0xf8>; - }; - - gmac1-rx-bus2 { - rockchip,pins = <0x03 0x07 0x01 0x178 0x03 0x08 0x01 0x178 0x03 0x09 0x01 0x178>; - phandle = <0xfa>; - }; - - gmac1-tx-bus2 { - rockchip,pins = <0x03 0x0b 0x01 0x17a 0x03 0x0c 0x01 0x17a 0x03 0x0d 0x01 0x178>; - phandle = <0xf9>; - }; - - gmac1-rgmii-clk { - rockchip,pins = <0x03 0x05 0x01 0x178 0x03 0x04 0x01 0x178>; - phandle = <0xfb>; - }; - - gmac1-rgmii-bus { - rockchip,pins = <0x03 0x02 0x01 0x178 0x03 0x03 0x01 0x178 0x03 0x00 0x01 0x17a 0x03 0x01 0x01 0x17a>; - phandle = <0xfc>; - }; - }; - - hdmi { - - hdmim0-tx0-cec { - rockchip,pins = <0x04 0x11 0x05 0x178>; - phandle = <0xdb>; - }; - - hdmim0-tx0-hpd { - rockchip,pins = <0x01 0x05 0x05 0x178>; - phandle = <0xdc>; - }; - - hdmim0-tx0-scl { - rockchip,pins = <0x04 0x0f 0x05 0x17b>; - phandle = <0xdd>; - }; - - hdmim0-tx0-sda { - rockchip,pins = <0x04 0x10 0x05 0x17c>; - phandle = <0xde>; - }; - - hdmim0-tx1-hpd { - rockchip,pins = <0x01 0x06 0x05 0x178>; - phandle = <0x18d>; - }; - - hdmim1-rx-cec { - rockchip,pins = <0x03 0x19 0x05 0x178>; - phandle = <0x197>; - }; - - hdmim1-rx-hpdin { - rockchip,pins = <0x03 0x1c 0x05 0x178>; - phandle = <0x198>; - }; - - hdmim1-rx-scl { - rockchip,pins = <0x03 0x1a 0x05 0x17d>; - phandle = <0x199>; - }; - - hdmim1-rx-sda { - rockchip,pins = <0x03 0x1b 0x05 0x17d>; - phandle = <0x19a>; - }; - - hdmim1-tx1-scl { - rockchip,pins = <0x03 0x16 0x05 0x17b>; - phandle = <0x18e>; - }; - - hdmim1-tx1-sda { - rockchip,pins = <0x03 0x15 0x05 0x17c>; - phandle = <0x18f>; - }; - - hdmim2-tx1-cec { - rockchip,pins = <0x03 0x14 0x05 0x178>; - phandle = <0x18c>; - }; - }; - - i2c0 { - - i2c0m2-xfer { - rockchip,pins = <0x00 0x19 0x03 0x17d 0x00 0x1a 0x03 0x17d>; - phandle = <0x62>; - }; - }; - - i2c1 { - - i2c1m2-xfer { - rockchip,pins = <0x00 0x1c 0x09 0x17d 0x00 0x1d 0x09 0x17d>; - phandle = <0x12d>; - }; - }; - - i2c2 { - - i2c2m0-xfer { - rockchip,pins = <0x00 0x0f 0x09 0x17d 0x00 0x10 0x09 0x17d>; - phandle = <0x12e>; - }; - }; - - i2c3 { - - i2c3m0-xfer { - rockchip,pins = <0x01 0x11 0x09 0x17d 0x01 0x10 0x09 0x17d>; - phandle = <0x12f>; - }; - }; - - i2c4 { - - i2c4m0-xfer { - rockchip,pins = <0x03 0x06 0x09 0x17d 0x03 0x05 0x09 0x17d>; - phandle = <0x131>; - }; - }; - - i2c5 { - - i2c5m0-xfer { - rockchip,pins = <0x03 0x17 0x09 0x17d 0x03 0x18 0x09 0x17d>; - phandle = <0x132>; - }; - }; - - i2c6 { - - i2c6m0-xfer { - rockchip,pins = <0x00 0x18 0x09 0x17d 0x00 0x17 0x09 0x17d>; - phandle = <0x15f>; - }; - }; - - i2c7 { - - i2c7m0-xfer { - rockchip,pins = <0x01 0x18 0x09 0x17d 0x01 0x19 0x09 0x17d>; - phandle = <0x166>; - }; - }; - - i2c8 { - - i2c8m0-xfer { - rockchip,pins = <0x04 0x1a 0x09 0x17d 0x04 0x1b 0x09 0x17d>; - phandle = <0x167>; - }; - }; - - i2s0 { - - i2s0-lrck { - rockchip,pins = <0x01 0x15 0x01 0x178>; - phandle = <0x105>; - }; - - i2s0-mclk { - rockchip,pins = <0x01 0x12 0x01 0x178>; - phandle = <0x130>; - }; - - i2s0-sclk { - rockchip,pins = <0x01 0x13 0x01 0x178>; - phandle = <0x106>; - }; - - i2s0-sdi0 { - rockchip,pins = <0x01 0x1c 0x02 0x178>; - phandle = <0x107>; - }; - - i2s0-sdo0 { - rockchip,pins = <0x01 0x17 0x01 0x178>; - phandle = <0x108>; - }; - }; - - i2s1 { - - i2s1m0-lrck { - rockchip,pins = <0x04 0x02 0x03 0x178>; - phandle = <0x109>; - }; - - i2s1m0-sclk { - rockchip,pins = <0x04 0x01 0x03 0x178>; - phandle = <0x10a>; - }; - - i2s1m0-sdi0 { - rockchip,pins = <0x04 0x05 0x03 0x178>; - phandle = <0x10b>; - }; - - i2s1m0-sdi1 { - rockchip,pins = <0x04 0x06 0x03 0x178>; - phandle = <0x10c>; - }; - - i2s1m0-sdi2 { - rockchip,pins = <0x04 0x07 0x03 0x178>; - phandle = <0x10d>; - }; - - i2s1m0-sdi3 { - rockchip,pins = <0x04 0x08 0x03 0x178>; - phandle = <0x10e>; - }; - - i2s1m0-sdo0 { - rockchip,pins = <0x04 0x09 0x03 0x178>; - phandle = <0x10f>; - }; - - i2s1m0-sdo1 { - rockchip,pins = <0x04 0x0a 0x03 0x178>; - phandle = <0x110>; - }; - - i2s1m0-sdo2 { - rockchip,pins = <0x04 0x0b 0x03 0x178>; - phandle = <0x111>; - }; - - i2s1m0-sdo3 { - rockchip,pins = <0x04 0x0c 0x03 0x178>; - phandle = <0x112>; - }; - }; - - i2s2 { - - i2s2m1-lrck { - rockchip,pins = <0x03 0x0e 0x03 0x178>; - phandle = <0x113>; - }; - - i2s2m1-sclk { - rockchip,pins = <0x03 0x0d 0x03 0x178>; - phandle = <0x114>; - }; - - i2s2m1-sdi { - rockchip,pins = <0x03 0x0a 0x03 0x178>; - phandle = <0x115>; - }; - - i2s2m1-sdo { - rockchip,pins = <0x03 0x0b 0x03 0x178>; - phandle = <0x116>; - }; - }; - - i2s3 { - - i2s3-lrck { - rockchip,pins = <0x03 0x02 0x03 0x178>; - phandle = <0x117>; - }; - - i2s3-sclk { - rockchip,pins = <0x03 0x01 0x03 0x178>; - phandle = <0x118>; - }; - - i2s3-sdi { - rockchip,pins = <0x03 0x04 0x03 0x178>; - phandle = <0x119>; - }; - - i2s3-sdo { - rockchip,pins = <0x03 0x03 0x03 0x178>; - phandle = <0x11a>; - }; - }; - - pdm0 { - - pdm0m0-clk { - rockchip,pins = <0x01 0x16 0x03 0x178>; - phandle = <0x11b>; - }; - - pdm0m0-clk1 { - rockchip,pins = <0x01 0x14 0x03 0x178>; - phandle = <0x11c>; - }; - - pdm0m0-sdi0 { - rockchip,pins = <0x01 0x1d 0x03 0x178>; - phandle = <0x11d>; - }; - - pdm0m0-sdi1 { - rockchip,pins = <0x01 0x19 0x03 0x178>; - phandle = <0x11e>; - }; - - pdm0m0-sdi2 { - rockchip,pins = <0x01 0x1a 0x03 0x178>; - phandle = <0x11f>; - }; - - pdm0m0-sdi3 { - rockchip,pins = <0x01 0x1b 0x03 0x178>; - phandle = <0x120>; - }; - }; - - pdm1 { - - pdm1m0-clk { - rockchip,pins = <0x04 0x1d 0x02 0x178>; - phandle = <0x121>; - }; - - pdm1m0-clk1 { - rockchip,pins = <0x04 0x1c 0x02 0x178>; - phandle = <0x122>; - }; - - pdm1m0-sdi0 { - rockchip,pins = <0x04 0x1b 0x02 0x178>; - phandle = <0x123>; - }; - - pdm1m0-sdi1 { - rockchip,pins = <0x04 0x1a 0x02 0x178>; - phandle = <0x124>; - }; - - pdm1m0-sdi2 { - rockchip,pins = <0x04 0x19 0x02 0x178>; - phandle = <0x125>; - }; - - pdm1m0-sdi3 { - rockchip,pins = <0x04 0x18 0x02 0x178>; - phandle = <0x126>; - }; - }; - - pmic { - - pmic-pins { - rockchip,pins = <0x00 0x07 0x00 0x17e 0x00 0x02 0x01 0x178 0x00 0x03 0x01 0x178 0x00 0x11 0x01 0x178 0x00 0x12 0x01 0x178 0x00 0x13 0x01 0x178 0x00 0x1e 0x01 0x178>; - phandle = <0x13c>; - }; - }; - - pwm0 { - - pwm0m0-pins { - rockchip,pins = <0x00 0x0f 0x03 0x178>; - phandle = <0x66>; - }; - }; - - pwm1 { - - pwm1m0-pins { - rockchip,pins = <0x00 0x10 0x03 0x178>; - phandle = <0x67>; - }; - }; - - pwm2 { - - pwm2m0-pins { - rockchip,pins = <0x00 0x14 0x03 0x178>; - phandle = <0x68>; - }; - }; - - pwm3 { - - pwm3m0-pins { - rockchip,pins = <0x00 0x1c 0x03 0x178>; - phandle = <0x69>; - }; - }; - - pwm4 { - - pwm4m0-pins { - rockchip,pins = <0x00 0x15 0x0b 0x178>; - phandle = <0x150>; - }; - }; - - pwm5 { - - pwm5m0-pins { - rockchip,pins = <0x00 0x09 0x03 0x178>; - phandle = <0x151>; - }; - }; - - pwm6 { - - pwm6m0-pins { - rockchip,pins = <0x00 0x17 0x0b 0x178>; - phandle = <0x152>; - }; - }; - - pwm7 { - - pwm7m0-pins { - rockchip,pins = <0x00 0x18 0x0b 0x178>; - phandle = <0x153>; - }; - }; - - pwm8 { - - pwm8m0-pins { - rockchip,pins = <0x03 0x07 0x0b 0x178>; - phandle = <0x154>; - }; - }; - - pwm9 { - - pwm9m0-pins { - rockchip,pins = <0x03 0x08 0x0b 0x178>; - phandle = <0x155>; - }; - }; - - pwm10 { - - pwm10m0-pins { - rockchip,pins = <0x03 0x00 0x0b 0x178>; - phandle = <0x156>; - }; - }; - - pwm11 { - - pwm11m0-pins { - rockchip,pins = <0x03 0x01 0x0b 0x178>; - phandle = <0x157>; - }; - }; - - pwm12 { - - pwm12m0-pins { - rockchip,pins = <0x03 0x0d 0x0b 0x178>; - phandle = <0x158>; - }; - }; - - pwm13 { - - pwm13m0-pins { - rockchip,pins = <0x03 0x0e 0x0b 0x178>; - phandle = <0x159>; - }; - }; - - pwm14 { - - pwm14m0-pins { - rockchip,pins = <0x03 0x12 0x0b 0x178>; - phandle = <0x15a>; - }; - }; - - pwm15 { - - pwm15m2-pins { - rockchip,pins = <0x01 0x16 0x0b 0x178>; - phandle = <0x15b>; - }; - }; - - sdio { - - sdiom1-pins { - rockchip,pins = <0x03 0x05 0x02 0x178 0x03 0x04 0x02 0x17e 0x03 0x00 0x02 0x17e 0x03 0x01 0x02 0x17e 0x03 0x02 0x02 0x17e 0x03 0x03 0x02 0x17e>; - phandle = <0x103>; - }; - }; - - sdmmc { - - sdmmc-bus4 { - rockchip,pins = <0x04 0x18 0x01 0x179 0x04 0x19 0x01 0x179 0x04 0x1a 0x01 0x179 0x04 0x1b 0x01 0x179>; - phandle = <0x101>; - }; - - sdmmc-clk { - rockchip,pins = <0x04 0x1d 0x01 0x179>; - phandle = <0xfe>; - }; - - sdmmc-cmd { - rockchip,pins = <0x04 0x1c 0x01 0x179>; - phandle = <0xff>; - }; - - sdmmc-det { - rockchip,pins = <0x00 0x04 0x01 0x17e>; - phandle = <0x100>; - }; - }; - - spdif0 { - - spdif0m0-tx { - rockchip,pins = <0x01 0x0e 0x03 0x178>; - phandle = <0x127>; - }; - }; - - spdif1 { - - spdif1m0-tx { - rockchip,pins = <0x01 0x0f 0x03 0x178>; - phandle = <0x128>; - }; - }; - - spi0 { - - spi0m0-pins { - rockchip,pins = <0x00 0x16 0x08 0x17a 0x00 0x17 0x08 0x17a 0x00 0x10 0x08 0x17a>; - phandle = <0x135>; - }; - - spi0m0-cs0 { - rockchip,pins = <0x00 0x19 0x08 0x17a>; - phandle = <0x133>; - }; - - spi0m0-cs1 { - rockchip,pins = <0x00 0x0f 0x08 0x17a>; - phandle = <0x134>; - }; - }; - - spi1 { - - spi1m1-pins { - rockchip,pins = <0x03 0x11 0x08 0x17a 0x03 0x10 0x08 0x17a 0x03 0x0f 0x08 0x17a>; - phandle = <0x138>; - }; - - spi1m1-cs0 { - rockchip,pins = <0x03 0x12 0x08 0x17a>; - phandle = <0x136>; - }; - - spi1m1-cs1 { - rockchip,pins = <0x03 0x13 0x08 0x17a>; - phandle = <0x137>; - }; - }; - - spi2 { - - spi2m2-pins { - rockchip,pins = <0x00 0x05 0x01 0x17f 0x00 0x0b 0x01 0x17f 0x00 0x06 0x01 0x17f>; - phandle = <0x13a>; - }; - - spi2m2-cs0 { - rockchip,pins = <0x00 0x09 0x01 0x17f>; - phandle = <0x139>; - }; - }; - - spi3 { - - spi3m1-pins { - rockchip,pins = <0x04 0x0f 0x08 0x17a 0x04 0x0d 0x08 0x17a 0x04 0x0e 0x08 0x17a>; - phandle = <0x145>; - }; - - spi3m1-cs0 { - rockchip,pins = <0x04 0x10 0x08 0x17a>; - phandle = <0x143>; - }; - - spi3m1-cs1 { - rockchip,pins = <0x04 0x11 0x08 0x17a>; - phandle = <0x144>; - }; - }; - - spi4 { - - spi4m0-pins { - rockchip,pins = <0x01 0x12 0x08 0x17a 0x01 0x10 0x08 0x17a 0x01 0x11 0x08 0x17a>; - phandle = <0x16a>; - }; - - spi4m0-cs0 { - rockchip,pins = <0x01 0x13 0x08 0x17a>; - phandle = <0x168>; - }; - - spi4m0-cs1 { - rockchip,pins = <0x01 0x14 0x08 0x17a>; - phandle = <0x169>; - }; - }; - - tsadc { - - tsadc-shut { - rockchip,pins = <0x00 0x01 0x02 0x178>; - phandle = <0x15d>; - }; - }; - - uart0 { - - uart0m2-xfer { - rockchip,pins = <0x04 0x04 0x0a 0x17e 0x04 0x03 0x0a 0x17e>; - phandle = <0x65>; - }; - }; - - uart1 { - - uart1m1-xfer { - rockchip,pins = <0x01 0x0f 0x0a 0x17e 0x01 0x0e 0x0a 0x17e>; - phandle = <0x146>; - }; - }; - - uart2 { - - uart2m0-xfer { - rockchip,pins = <0x00 0x0e 0x0a 0x17e 0x00 0x0d 0x0a 0x17e>; - phandle = <0x1ab>; - }; - - uart2m1-xfer { - rockchip,pins = <0x04 0x19 0x0a 0x17e 0x04 0x18 0x0a 0x17e>; - phandle = <0x147>; - }; - }; - - uart3 { - - uart3m1-xfer { - rockchip,pins = <0x03 0x0e 0x0a 0x17e 0x03 0x0d 0x0a 0x17e>; - phandle = <0x148>; - }; - }; - - uart4 { - - uart4m1-xfer { - rockchip,pins = <0x03 0x18 0x0a 0x17e 0x03 0x19 0x0a 0x17e>; - phandle = <0x149>; - }; - }; - - uart5 { - - uart5m1-xfer { - rockchip,pins = <0x03 0x15 0x0a 0x17e 0x03 0x14 0x0a 0x17e>; - phandle = <0x14a>; - }; - }; - - uart6 { - - uart6m1-xfer { - rockchip,pins = <0x01 0x00 0x0a 0x17e 0x01 0x01 0x0a 0x17e>; - phandle = <0x14b>; - }; - - uart6m1-ctsn { - rockchip,pins = <0x01 0x03 0x0a 0x178>; - phandle = <0x14c>; - }; - - uart6m1-rtsn { - rockchip,pins = <0x01 0x02 0x0a 0x178>; - phandle = <0x1c1>; - }; - }; - - uart7 { - - uart7m1-xfer { - rockchip,pins = <0x03 0x11 0x0a 0x17e 0x03 0x10 0x0a 0x17e>; - phandle = <0x14d>; - }; - }; - - uart8 { - - uart8m1-xfer { - rockchip,pins = <0x03 0x03 0x0a 0x17e 0x03 0x02 0x0a 0x17e>; - phandle = <0x14e>; - }; - }; - - uart9 { - - uart9m1-xfer { - rockchip,pins = <0x04 0x0d 0x0a 0x17e 0x04 0x0c 0x0a 0x17e>; - phandle = <0x14f>; - }; - }; - - gpio-func { - - tsadc-gpio-func { - rockchip,pins = <0x00 0x01 0x00 0x178>; - phandle = <0x15c>; - }; - }; - - gmac0 { - - gmac0-miim { - rockchip,pins = <0x04 0x14 0x01 0x178 0x04 0x15 0x01 0x178>; - phandle = <0xef>; - }; - - gmac0-rx-bus2 { - rockchip,pins = <0x02 0x11 0x01 0x178 0x02 0x12 0x01 0x178 0x04 0x12 0x01 0x178>; - phandle = <0xf1>; - }; - - gmac0-tx-bus2 { - rockchip,pins = <0x02 0x0e 0x01 0x17a 0x02 0x0f 0x01 0x17a 0x02 0x10 0x01 0x178>; - phandle = <0xf0>; - }; - - gmac0-rgmii-clk { - rockchip,pins = <0x02 0x08 0x01 0x178 0x02 0x0b 0x01 0x178>; - phandle = <0xf2>; - }; - - gmac0-rgmii-bus { - rockchip,pins = <0x02 0x06 0x01 0x178 0x02 0x07 0x01 0x178 0x02 0x09 0x01 0x17a 0x02 0x0a 0x01 0x17a>; - phandle = <0xf3>; - }; - }; - - dp { - - dp1-hpd { - rockchip,pins = <0x01 0x04 0x00 0x178>; - phandle = <0x188>; - }; - }; - - leds { - - leds-gpio { - rockchip,pins = <0x01 0x0b 0x00 0x178>; - phandle = <0x1ac>; - }; - }; - - hdmirx { - - hdmirx-det { - rockchip,pins = <0x01 0x1d 0x00 0x17e>; - phandle = <0x19b>; - }; - }; - - headphone { - - hp-det { - rockchip,pins = <0x01 0x14 0x00 0x178>; - phandle = <0x1b6>; - }; - }; - - hym8563 { - - hym8563-int { - rockchip,pins = <0x00 0x08 0x00 0x17e>; - phandle = <0x160>; - }; - }; - - lcd { - - lcd-rst-gpio { - rockchip,pins = <0x02 0x0c 0x00 0x178>; - }; - }; - - touch { - - touch-gpio { - rockchip,pins = <0x00 0x1d 0x00 0x17e 0x00 0x16 0x00 0x178>; - }; - }; - - usb-typec { - - usbc0-int { - rockchip,pins = <0x00 0x1b 0x00 0x17e>; - phandle = <0x161>; - }; - }; - - wireless-bluetooth { - - uart6-gpios { - rockchip,pins = <0x01 0x02 0x00 0x178>; - phandle = <0x1c5>; - }; - - bt-reset-gpio { - rockchip,pins = <0x00 0x16 0x00 0x178>; - phandle = <0x1c2>; - }; - - bt-wake-gpio { - rockchip,pins = <0x00 0x15 0x00 0x178>; - phandle = <0x1c3>; - }; - - bt-irq-gpio { - rockchip,pins = <0x00 0x00 0x00 0x181>; - phandle = <0x1c4>; - }; - }; - - wireless-wlan { - - wifi-host-wake-irq { - rockchip,pins = <0x00 0x0a 0x00 0x181>; - }; - - wifi-poweren-gpio { - rockchip,pins = <0x00 0x14 0x00 0x17e>; - phandle = <0x1c6>; - }; - }; - }; - - csi2-dphy3 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x182>; - status = "disabled"; - }; - - csi2-dphy4 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x182>; - status = "disabled"; - }; - - csi2-dphy5 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x182>; - status = "disabled"; - }; - - rkcif-mipi-lvds4 { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x183>; - }; - - rkcif-mipi-lvds4-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x183>; - status = "disabled"; - }; - - rkcif-mipi-lvds4-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x183>; - status = "disabled"; - }; - - rkcif-mipi-lvds4-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x183>; - status = "disabled"; - }; - - rkcif-mipi-lvds4-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x183>; - status = "disabled"; - }; - - rkcif-mipi-lvds5 { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x184>; - }; - - rkcif-mipi-lvds5-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x184>; - status = "disabled"; - }; - - rkcif-mipi-lvds5-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x184>; - status = "disabled"; - }; - - rkcif-mipi-lvds5-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x184>; - status = "disabled"; - }; - - rkcif-mipi-lvds5-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x184>; - status = "disabled"; - }; - - usbdrd3_1 { - compatible = "rockchip,rk3588-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x02 0x1a6 0x02 0x1a5 0x02 0x1a4>; - clock-names = "ref\0suspend\0bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - usb@fc400000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfc400000 0x00 0x400000>; - interrupts = <0x00 0xdd 0x04>; - power-domains = <0x4d 0x1f>; - resets = <0x02 0x2a7>; - reset-names = "usb3-otg"; - dr_mode = "host"; - phys = <0x185 0x186>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - snps,dis_enblslpm_quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,parkmode-disable-ss-quirk; - status = "okay"; - }; - }; - - syscon@fd5b8000 { - compatible = "rockchip,pcie30-phy-grf\0syscon"; - reg = <0x00 0xfd5b8000 0x00 0x10000>; - phandle = <0x1a9>; - }; - - syscon@fd5c0000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfd5c0000 0x00 0x100>; - phandle = <0x1a8>; - }; - - syscon@fd5cc000 { - compatible = "rockchip,rk3588-usbdpphy-grf\0syscon"; - reg = <0x00 0xfd5cc000 0x00 0x4000>; - phandle = <0x1a5>; - }; - - syscon@fd5d4000 { - compatible = "rockchip,rk3588-usb2phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd5d4000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - phandle = <0x1a4>; - - usb2-phy@4000 { - compatible = "rockchip,rk3588-usb2phy"; - reg = <0x4000 0x10>; - interrupts = <0x00 0x18a 0x04>; - resets = <0x02 0xc0048 0x02 0x489>; - reset-names = "phy\0apb"; - clocks = <0x02 0x2b5>; - clock-names = "phyclk"; - clock-output-names = "usb480m_phy1"; - #clock-cells = <0x00>; - rockchip,usbctrl-grf = <0x5f>; - status = "okay"; - phandle = <0x1a6>; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x60>; - phandle = <0x185>; - }; - }; - }; - - syscon@fd5e4000 { - compatible = "rockchip,rk3588-hdptxphy-grf\0syscon"; - reg = <0x00 0xfd5e4000 0x00 0x100>; - phandle = <0x1a3>; - }; - - mipi4-csi2@fdd50000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd50000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x97 0x04 0x00 0x98 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1d3>; - clock-names = "pclk_csi2host"; - resets = <0x02 0x328 0x02 0x338>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - mipi5-csi2@fdd60000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd60000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x99 0x04 0x00 0x9a 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1d4>; - clock-names = "pclk_csi2host"; - resets = <0x02 0x329 0x02 0x339>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - spdif-tx@fddb8000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfddb8000 0x00 0x1000>; - interrupts = <0x00 0xc6 0x04>; - dmas = <0xd3 0x16>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x20f 0x02 0x20a>; - assigned-clocks = <0x02 0x20b>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x19>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x1be>; - }; - - i2s@fddc8000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddc8000 0x00 0x1000>; - interrupts = <0x00 0xbc 0x04>; - clocks = <0x02 0x201 0x02 0x1fe>; - clock-names = "mclk_tx\0hclk"; - assigned-clocks = <0x02 0x1ff>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd4 0x16>; - dma-names = "tx"; - power-domains = <0x4d 0x19>; - resets = <0x02 0x391>; - reset-names = "tx-m"; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-tx@fdde8000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfdde8000 0x00 0x1000>; - interrupts = <0x00 0xc5 0x04>; - dmas = <0xd3 0x08>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x25c 0x02 0x258>; - assigned-clocks = <0x02 0x259>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x1a>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - i2s@fddf4000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddf4000 0x00 0x1000>; - interrupts = <0x00 0xba 0x04>; - clocks = <0x02 0x24c 0x02 0x24c 0x02 0x252>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x249>; - assigned-clock-parents = <0x02 0x07>; - dmas = <0xd4 0x04>; - dma-names = "tx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x3ef>; - reset-names = "tx-m"; - rockchip,always-on; - rockchip,hdmi-path; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x1bc>; - }; - - i2s@fddf8000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddf8000 0x00 0x1000>; - interrupts = <0x00 0xbb 0x04>; - clocks = <0x02 0x23c 0x02 0x23c 0x02 0x238>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x239>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd4 0x15>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x3c3>; - reset-names = "rx-m"; - rockchip,capture-only; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x1c8>; - }; - - i2s@fde00000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfde00000 0x00 0x1000>; - interrupts = <0x00 0xbe 0x04>; - clocks = <0x02 0x237 0x02 0x237 0x02 0x233>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x234>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd4 0x18>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x417>; - reset-names = "rx-m"; - rockchip,capture-only; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-rx@fde10000 { - compatible = "rockchip,rk3588-spdifrx\0rockchip,rk3308-spdifrx"; - reg = <0x00 0xfde10000 0x00 0x1000>; - interrupts = <0x00 0xc8 0x04>; - clocks = <0x02 0x260 0x02 0x25f>; - clock-names = "mclk\0hclk"; - assigned-clocks = <0x02 0x260>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0x64 0x16>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x3ff>; - reset-names = "spdifrx-m"; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-rx@fde18000 { - compatible = "rockchip,rk3588-spdifrx\0rockchip,rk3308-spdifrx"; - reg = <0x00 0xfde18000 0x00 0x1000>; - interrupts = <0x00 0xc9 0x04>; - clocks = <0x02 0x262 0x02 0x261>; - clock-names = "mclk\0hclk"; - assigned-clocks = <0x02 0x262>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0x64 0x17>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x401>; - reset-names = "spdifrx-m"; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - dp@fde60000 { - compatible = "rockchip,rk3588-dp"; - reg = <0x00 0xfde60000 0x00 0x4000>; - interrupts = <0x00 0xa2 0x04>; - clocks = <0x02 0x1e7 0x02 0x2cd 0x02 0x201 0x02 0x20d 0x04>; - clock-names = "apb\0aux\0i2s\0spdif\0hclk"; - assigned-clocks = <0x02 0x2cd>; - assigned-clock-rates = <0xf42400>; - resets = <0x02 0x389>; - phys = <0x187>; - power-domains = <0x4d 0x19>; - #sound-dai-cells = <0x01>; - status = "disabled"; - pinctrl-names = "default"; - pinctrl-0 = <0x188>; - hpd-gpios = <0x189 0x04 0x00>; - phandle = <0x1bf>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x18a>; - status = "disabled"; - phandle = <0xbf>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x18b>; - status = "disabled"; - phandle = <0xc5>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x37>; - status = "disabled"; - phandle = <0xcd>; - }; - }; - }; - }; - - hdmi@fdea0000 { - compatible = "rockchip,rk3588-dw-hdmi"; - reg = <0x00 0xfdea0000 0x00 0x20000>; - interrupts = <0x00 0xad 0x04 0x00 0xae 0x04 0x00 0xaf 0x04 0x00 0xb0 0x04 0x00 0x169 0x04>; - clocks = <0x02 0x224 0x02 0x266 0x02 0x225 0x02 0x226 0x02 0x24c 0x02 0x274 0x02 0x275 0x02 0x276 0x02 0x277 0x05 0x2f>; - clock-names = "pclk\0hpd\0earc\0hdmitx_ref\0aud\0dclk_vp0\0dclk_vp1\0dclk_vp2\0dclk_vp3\0hclk_vo1\0link_clk"; - resets = <0x02 0x3d7 0x02 0x49d>; - reset-names = "ref\0hdp"; - power-domains = <0x4d 0x1a>; - pinctrl-names = "default"; - pinctrl-0 = <0x18c 0x18d 0x18e 0x18f>; - reg-io-width = <0x04>; - rockchip,grf = <0xb7>; - rockchip,vo1_grf = <0xba>; - phys = <0x190>; - phy-names = "hdmi"; - #sound-dai-cells = <0x00>; - status = "disabled"; - enable-gpios = <0xe0 0x0a 0x00>; - phandle = <0x1bd>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x191>; - status = "disabled"; - phandle = <0xc1>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x38>; - status = "disabled"; - phandle = <0xc7>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x192>; - status = "disabled"; - phandle = <0xcf>; - }; - }; - }; - }; - - edp@fded0000 { - compatible = "rockchip,rk3588-edp"; - reg = <0x00 0xfded0000 0x00 0x1000>; - interrupts = <0x00 0xa4 0x04>; - clocks = <0x02 0x214 0x02 0x213 0x02 0x215 0x05>; - clock-names = "dp\0pclk\0spdif\0hclk"; - resets = <0x02 0x3e4 0x02 0x3e3>; - reset-names = "dp\0apb"; - phys = <0x193>; - phy-names = "dp"; - power-domains = <0x4d 0x1a>; - rockchip,grf = <0xba>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x194>; - status = "disabled"; - phandle = <0xc0>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x195>; - status = "disabled"; - phandle = <0xc6>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x196>; - status = "disabled"; - phandle = <0xce>; - }; - }; - }; - }; - - hdmirx-controller@fdee0000 { - compatible = "rockchip,rk3588-hdmirx-ctrler\0rockchip,hdmirx-ctrler"; - reg = <0x00 0xfdee0000 0x00 0x6000>; - reg-names = "hdmirx_regs"; - power-domains = <0x4d 0x1a>; - rockchip,grf = <0xb7>; - rockchip,vo1_grf = <0xba>; - interrupts = <0x00 0xb1 0x04 0x00 0x1b4 0x04 0x00 0xb3 0x04>; - interrupt-names = "cec\0hdmi\0dma"; - clocks = <0x02 0x21a 0x02 0x21f 0x02 0x2b2 0x02 0x21b 0x02 0x21c 0x02 0x232 0x05>; - clock-names = "aclk\0audio\0cr_para\0pclk\0ref\0hclk_s_hdmirx\0hclk_vo1"; - resets = <0x02 0x3d9 0x02 0x3da 0x02 0x3db 0x02 0x3b7>; - reset-names = "rst_a\0rst_p\0rst_ref\0rst_biu"; - pinctrl-0 = <0x197 0x198 0x199 0x19a 0x19b>; - pinctrl-names = "default"; - status = "disabled"; - hpd-trigger-level = <0x01>; - hdmirx-det-gpios = <0x189 0x1d 0x01>; - }; - - pcie@fe150000 { - compatible = "rockchip,rk3588-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x00 0x0f>; - clocks = <0x02 0x14e 0x02 0x153 0x02 0x149 0x02 0x158 0x02 0x15e 0x02 0x183>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux\0pipe"; - device_type = "pci"; - interrupts = <0x00 0x107 0x04 0x00 0x106 0x04 0x00 0x105 0x04 0x00 0x104 0x04 0x00 0x103 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x19c 0x00 0x00 0x00 0x00 0x02 0x19c 0x01 0x00 0x00 0x00 0x03 0x19c 0x02 0x00 0x00 0x00 0x04 0x19c 0x03>; - linux,pci-domain = <0x00>; - num-ib-windows = <0x10>; - num-ob-windows = <0x10>; - num-viewport = <0x08>; - max-link-speed = <0x03>; - msi-map = <0x00 0x19d 0x00 0x1000>; - num-lanes = <0x04>; - phys = <0x19e>; - phy-names = "pcie-phy"; - power-domains = <0x4d 0x22>; - ranges = <0x800 0x00 0xf0000000 0x00 0xf0000000 0x00 0x100000 0x81000000 0x00 0xf0100000 0x00 0xf0100000 0x00 0x100000 0x82000000 0x00 0xf0200000 0x00 0xf0200000 0x00 0xe00000 0xc3000000 0x09 0x00 0x09 0x00 0x00 0x40000000>; - reg = <0x00 0xfe150000 0x00 0x10000 0x0a 0x40000000 0x00 0x400000>; - reg-names = "pcie-apb\0pcie-dbi"; - resets = <0x02 0x20d 0x02 0x21c>; - reset-names = "pcie\0periph"; - rockchip,pipe-grf = <0x61>; - status = "disabled"; - reset-gpios = <0xe0 0x0e 0x00>; - vpcie3v3-supply = <0x19f>; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x104 0x01>; - phandle = <0x19c>; - }; - }; - - pcie@fe160000 { - compatible = "rockchip,rk3588-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x10 0x1f>; - clocks = <0x02 0x14f 0x02 0x154 0x02 0x14a 0x02 0x159 0x02 0x15f 0x02 0x184>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux\0pipe"; - device_type = "pci"; - interrupts = <0x00 0x102 0x04 0x00 0x101 0x04 0x00 0x100 0x04 0x00 0xff 0x04 0x00 0xfe 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x1a0 0x00 0x00 0x00 0x00 0x02 0x1a0 0x01 0x00 0x00 0x00 0x03 0x1a0 0x02 0x00 0x00 0x00 0x04 0x1a0 0x03>; - linux,pci-domain = <0x01>; - num-ib-windows = <0x10>; - num-ob-windows = <0x10>; - num-viewport = <0x08>; - max-link-speed = <0x03>; - msi-map = <0x1000 0x19d 0x1000 0x1000>; - num-lanes = <0x02>; - phys = <0x19e>; - phy-names = "pcie-phy"; - power-domains = <0x4d 0x22>; - ranges = <0x800 0x00 0xf1000000 0x00 0xf1000000 0x00 0x100000 0x81000000 0x00 0xf1100000 0x00 0xf1100000 0x00 0x100000 0x82000000 0x00 0xf1200000 0x00 0xf1200000 0x00 0xe00000 0xc3000000 0x09 0x40000000 0x09 0x40000000 0x00 0x40000000>; - reg = <0x00 0xfe160000 0x00 0x10000 0x0a 0x40400000 0x00 0x400000>; - reg-names = "pcie-apb\0pcie-dbi"; - resets = <0x02 0x20e 0x02 0x21d>; - reset-names = "pcie\0periph"; - rockchip,pipe-grf = <0x61>; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xff 0x01>; - phandle = <0x1a0>; - }; - }; - - pcie@fe170000 { - compatible = "rockchip,rk3588-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x20 0x2f>; - clocks = <0x02 0x150 0x02 0x155 0x02 0x14b 0x02 0x15b 0x02 0x160 0x02 0x2c4>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux\0pipe"; - device_type = "pci"; - interrupts = <0x00 0xf3 0x04 0x00 0xf2 0x04 0x00 0xf1 0x04 0x00 0xf0 0x04 0x00 0xef 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x1a1 0x00 0x00 0x00 0x00 0x02 0x1a1 0x01 0x00 0x00 0x00 0x03 0x1a1 0x02 0x00 0x00 0x00 0x04 0x1a1 0x03>; - linux,pci-domain = <0x02>; - num-ib-windows = <0x08>; - num-ob-windows = <0x08>; - num-viewport = <0x04>; - max-link-speed = <0x02>; - msi-map = <0x2000 0xe8 0x2000 0x1000>; - num-lanes = <0x01>; - phys = <0x1a2 0x02>; - phy-names = "pcie-phy"; - ranges = <0x800 0x00 0xf2000000 0x00 0xf2000000 0x00 0x100000 0x81000000 0x00 0xf2100000 0x00 0xf2100000 0x00 0x100000 0x82000000 0x00 0xf2200000 0x00 0xf2200000 0x00 0xe00000 0xc3000000 0x09 0x80000000 0x09 0x80000000 0x00 0x40000000>; - reg = <0x00 0xfe170000 0x00 0x10000 0x0a 0x40800000 0x00 0x400000>; - reg-names = "pcie-apb\0pcie-dbi"; - resets = <0x02 0x20f 0x02 0x21e>; - reset-names = "pcie\0periph"; - rockchip,pipe-grf = <0x61>; - status = "disabled"; - reset-gpios = <0x189 0x0c 0x00>; - rockchip,skip-scan-in-resume; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf0 0x01>; - phandle = <0x1a1>; - }; - }; - - sata@fe220000 { - compatible = "rockchip,rk-ahci\0snps,dwc-ahci"; - reg = <0x00 0xfe220000 0x00 0x1000>; - clocks = <0x02 0x172 0x02 0x16f 0x02 0x175 0x02 0x164 0x02 0x17f>; - clock-names = "sata\0pmalive\0rxoob\0ref\0asic"; - interrupts = <0x00 0x112 0x04>; - interrupt-names = "hostc"; - phys = <0x1a2 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - status = "disabled"; - }; - - phy@fed70000 { - compatible = "rockchip,rk3588-hdptx-phy"; - reg = <0x00 0xfed70000 0x00 0x2000>; - clocks = <0x02 0x2b5 0x02 0x268>; - clock-names = "ref\0apb"; - resets = <0x02 0x486 0x02 0xc003f 0x02 0xc0040 0x02 0xc0041>; - reset-names = "apb\0init\0cmn\0lane"; - rockchip,grf = <0x1a3>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x193>; - }; - - hdmiphy@fed70000 { - compatible = "rockchip,rk3588-hdptx-phy-hdmi"; - reg = <0x00 0xfed70000 0x00 0x2000>; - clocks = <0x02 0x2b5 0x02 0x268>; - clock-names = "ref\0apb"; - resets = <0x02 0x491 0x02 0x486 0x02 0xc003f 0x02 0xc0040 0x02 0xc0041 0x02 0x48f 0x02 0x490>; - reset-names = "phy\0apb\0init\0cmn\0lane\0ropll\0lcpll"; - rockchip,grf = <0x1a3>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x190>; - - clk-port { - #clock-cells = <0x00>; - status = "okay"; - phandle = <0x2f>; - }; - }; - - phy@fed90000 { - compatible = "rockchip,rk3588-usbdp-phy"; - reg = <0x00 0xfed90000 0x00 0x10000>; - rockchip,u2phy-grf = <0x1a4>; - rockchip,usb-grf = <0x5f>; - rockchip,usbdpphy-grf = <0x1a5>; - rockchip,vo-grf = <0xd7>; - clocks = <0x02 0x2b6 0x02 0x280 0x02 0x26a 0x1a6>; - clock-names = "refclk\0immortal\0pclk\0utmi"; - resets = <0x02 0x2f 0x02 0x30 0x02 0x31 0x02 0x32 0x02 0x484>; - reset-names = "init\0cmn\0lane\0pcs_apb\0pma_apb"; - status = "okay"; - rockchip,dp-lane-mux = <0x00 0x01>; - - dp-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0x187>; - }; - - u3-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0x186>; - }; - }; - - csi2-dphy1-hw@fedc8000 { - compatible = "rockchip,rk3588-csi2-dphy-hw"; - reg = <0x00 0xfedc8000 0x00 0x8000>; - clocks = <0x02 0x10d>; - clock-names = "pclk"; - resets = <0x02 0x19 0x02 0x18>; - reset-names = "srst_csiphy1\0srst_p_csiphy1"; - rockchip,grf = <0x1a7>; - rockchip,sys_grf = <0xb7>; - status = "disabled"; - phandle = <0x182>; - }; - - phy@fee10000 { - compatible = "rockchip,rk3588-naneng-combphy"; - reg = <0x00 0xfee10000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x02 0x2be 0x02 0x186 0x02 0x166>; - clock-names = "refclk\0apbclk\0phpclk"; - assigned-clocks = <0x02 0x2be>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x02 0x20006 0x02 0x4d7>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x61>; - rockchip,pipe-phy-grf = <0x1a8>; - rockchip,pcie1ln-sel-bits = <0x100 0x00 0x00 0x00>; - status = "okay"; - phandle = <0x1a2>; - }; - - phy@fee80000 { - compatible = "rockchip,rk3588-pcie3-phy"; - reg = <0x00 0xfee80000 0x00 0x20000>; - #phy-cells = <0x00>; - clocks = <0x02 0x188>; - clock-names = "pclk"; - resets = <0x02 0x2000a>; - reset-names = "phy"; - rockchip,pipe-grf = <0x61>; - rockchip,phy-grf = <0x1a9>; - status = "disabled"; - phandle = <0x19e>; - }; - - test-power { - status = "okay"; - }; - - vcc12v-dcin { - compatible = "regulator-fixed"; - regulator-name = "vcc12v_dcin"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b00>; - regulator-max-microvolt = <0xb71b00>; - phandle = <0x1aa>; - }; - - vcc5v0-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - vin-supply = <0x1aa>; - phandle = <0x63>; - }; - - vcc5v0-usbdcin { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_usbdcin"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - vin-supply = <0x1aa>; - }; - - vcc5v0-usb { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_usb"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - vin-supply = <0x1aa>; - phandle = <0x1b7>; - }; - - vcc-1v1-nldo-s3 { - compatible = "regulator-fixed"; - regulator-name = "vcc_1v1_nldo_s3"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x10c8e0>; - regulator-max-microvolt = <0x10c8e0>; - vin-supply = <0x63>; - phandle = <0x142>; - }; - - chosen { - bootargs = "storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal storagenode=/mmc@fe2e0000 androidboot.verifiedbootstate=orange rw rootwait earlycon=video,efifb,800x600,32 console=tty2 irqchip.gicv3_pseudo_nmi=0 root=PARTLABEL=rootfs rootfstype=ext4 overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 loglevel=7 nokaslr"; - }; - - memory@40000000 { - device_type = "memory"; - reg = <0x00 0x9400000 0x00 0xe6c00000>; - }; - - cspmu@fd10c000 { - compatible = "rockchip,cspmu"; - reg = <0x00 0xfd10c000 0x00 0x1000 0x00 0xfd10d000 0x00 0x1000 0x00 0xfd10e000 0x00 0x1000 0x00 0xfd10f000 0x00 0x1000 0x00 0xfd12c000 0x00 0x1000 0x00 0xfd12d000 0x00 0x1000 0x00 0xfd12e000 0x00 0x1000 0x00 0xfd12f000 0x00 0x1000>; - }; - - debug@fd104000 { - compatible = "rockchip,debug"; - reg = <0x00 0xfd104000 0x00 0x1000 0x00 0xfd105000 0x00 0x1000 0x00 0xfd106000 0x00 0x1000 0x00 0xfd107000 0x00 0x1000 0x00 0xfd124000 0x00 0x1000 0x00 0xfd125000 0x00 0x1000 0x00 0xfd126000 0x00 0x1000 0x00 0xfd127000 0x00 0x1000>; - }; - - fiq-debugger { - compatible = "rockchip,fiq-debugger"; - rockchip,serial-id = <0x02>; - rockchip,wake-irq = <0x00>; - rockchip,irq-mode-enable = <0x01>; - rockchip,baudrate = <0x16e360>; - interrupts = <0x00 0x1a7 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0x1ab>; - status = "okay"; - }; - - reserved-memory { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - cma { - compatible = "shared-dma-pool"; - reusable; - size = <0x00 0x800000>; - linux,cma-default; - reg = <0x00 0x49400000 0x00 0x10000000>; - }; - - drm-logo@00000000 { - compatible = "rockchip,drm-logo"; - reg = <0x00 0x00 0x00 0x00>; - phandle = <0x30>; - }; - - drm-cubic-lut@00000000 { - compatible = "rockchip,drm-cubic-lut"; - reg = <0x00 0x00 0x00 0x00>; - }; - - ramoops@110000 { - compatible = "ramoops"; - reg = <0x00 0x110000 0x00 0xf0000>; - record-size = <0x20000>; - console-size = <0x80000>; - ftrace-size = <0x00>; - pmsg-size = <0x50000>; - }; - }; - - leds { - compatible = "gpio-leds"; - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <0x1ac>; - - power { - label = ":power"; - linux,default-trigger = "ir-power-click"; - default-state = "on"; - gpios = <0x189 0x0b 0x00>; - }; - }; - - hdmi0-sound { - status = "okay"; - compatible = "rockchip,hdmi"; - rockchip,mclk-fs = <0x80>; - rockchip,card-name = "rockchip-hdmi0"; - rockchip,cpu = <0x1ad>; - rockchip,codec = <0x1ae>; - rockchip,jack-det; - }; - - dp0-sound { - status = "disabled"; - compatible = "rockchip,hdmi"; - rockchip,card-name = "rockchip-dp0"; - rockchip,mclk-fs = <0x200>; - rockchip,cpu = <0x1af>; - rockchip,codec = <0x1b0 0x01>; - rockchip,jack-det; - }; - - spdif-tx1-dc { - status = "disabled"; - compatible = "linux,spdif-dit"; - #sound-dai-cells = <0x00>; - phandle = <0x1b2>; - }; - - spdif-tx1-sound { - status = "disabled"; - compatible = "simple-audio-card"; - simple-audio-card,mclk-fs = <0x80>; - simple-audio-card,name = "rockchip,spdif-tx1"; - - simple-audio-card,cpu { - sound-dai = <0x1b1>; - }; - - simple-audio-card,codec { - sound-dai = <0x1b2>; - }; - }; - - adc-keys { - status = "okay"; - compatible = "adc-keys"; - io-channels = <0x1b3 0x01>; - io-channel-names = "buttons"; - keyup-threshold-microvolt = <0x1b7740>; - poll-interval = <0x64>; - - recovery-key { - label = "F12"; - linux,code = <0x58>; - press-threshold-microvolt = <0x4268>; - }; - }; - - es8388-sound { - status = "disabled"; - compatible = "firefly,multicodecs-card"; - rockchip,card-name = "rockchip-es8388"; - hp-det-gpio = <0x189 0x14 0x01>; - io-channels = <0x1b3 0x03>; - io-channel-names = "adc-detect"; - spk-con-gpio = <0xee 0x0a 0x00>; - hp-con-gpio = <0xe0 0x08 0x00>; - linein-type = <0x00>; - rockchip,format = "i2s"; - rockchip,mclk-fs = <0x100>; - rockchip,cpu = <0x1b4>; - rockchip,codec = <0x1b5>; - rockchip,audio-routing = "Headphone\0LOUT1\0Headphone\0ROUT1\0Speaker\0LOUT2\0Speaker\0ROUT2\0Headphone\0Headphone Power\0Headphone\0Headphone Power\0Speaker\0Speaker Power\0Speaker\0Speaker Power\0LINPUT1\0Main Mic\0LINPUT2\0Main Mic\0RINPUT1\0Headset Mic\0RINPUT2\0Headset Mic"; - pinctrl-names = "default"; - pinctrl-0 = <0x1b6>; - firefly,not-use-dapm; - }; - - vcc5v0-host { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_host"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - enable-active-high; - gpio = <0xe0 0x0e 0x00>; - vin-supply = <0x1b7>; - status = "okay"; - phandle = <0x60>; - }; - - vcc-hub-reset-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_hub_reset"; - regulator-boot-on; - regulator-always-on; - enable-active-high; - status = "disabled"; - gpio = <0x1b8 0x04 0x00>; - }; - - vbus5v0-typec-pwr-en-regulator { - compatible = "regulator-fixed"; - regulator-name = "vbus5v0_typec_pwr_en"; - enable-active-high; - status = "disabled"; - gpio = <0xe0 0x0a 0x00>; - phandle = <0x162>; - }; - - vcc-hub3-reset-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_hub3_reset"; - regulator-always-on; - enable-active-high; - status = "disabled"; - gpio = <0x1b8 0x06 0x00>; - }; - - vcc5v0-host3 { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_host3"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - enable-active-high; - gpio = <0xe0 0x0a 0x00>; - vin-supply = <0x1b7>; - status = "disabled"; - }; - - vcc-sata-pwr-en-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_sata_pwr_en"; - regulator-boot-on; - regulator-always-on; - enable-active-high; - status = "disabled"; - gpio = <0x1b8 0x0a 0x00>; - }; - - vcc-fan-pwr-en-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_fan_pwr_en"; - regulator-boot-on; - regulator-always-on; - enable-active-high; - status = "okay"; - gpio = <0x1b8 0x0b 0x00>; - }; - - vcc-sdcard-pwr-en-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_sdcard_pwr_en"; - regulator-boot-on; - regulator-always-on; - enable-active-high; - gpio = <0xee 0x11 0x00>; - status = "okay"; - }; - - vcc3v3-pcie30 { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_pcie30"; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - enable-active-high; - gpios = <0x1b9 0x15 0x00>; - startup-delay-us = <0x1388>; - vin-supply = <0x1aa>; - status = "disabled"; - phandle = <0x19f>; - }; - - pcie30-avdd1v8 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd1v8"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - vin-supply = <0x1ba>; - }; - - pcie30-avdd0v75 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd0v75"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0xb71b0>; - regulator-max-microvolt = <0xb71b0>; - vin-supply = <0x1bb>; - }; - - hdmi1-sound { - status = "disabled"; - compatible = "rockchip,hdmi"; - rockchip,mclk-fs = <0x80>; - rockchip,card-name = "rockchip-hdmi1"; - rockchip,cpu = <0x1bc>; - rockchip,codec = <0x1bd>; - rockchip,jack-det; - }; - - dp1-sound { - status = "disabled"; - compatible = "rockchip,hdmi"; - rockchip,card-name = "rockchip,dp1"; - rockchip,mclk-fs = <0x200>; - rockchip,cpu = <0x1be>; - rockchip,codec = <0x1bf 0x01>; - rockchip,jack-det; - }; - - wireless-bluetooth { - compatible = "bluetooth-platdata"; - clocks = <0x1c0>; - clock-names = "ext_clock"; - uart_rts_gpios = <0x189 0x02 0x01>; - pinctrl-names = "default\0rts_gpio"; - pinctrl-0 = <0x1c1 0x1c2 0x1c3 0x1c4>; - pinctrl-1 = <0x1c5>; - BT,reset_gpio = <0x13b 0x16 0x00>; - BT,wake_gpio = <0x13b 0x15 0x00>; - BT,wake_host_irq = <0x13b 0x00 0x00>; - status = "disabled"; - }; - - wireless-wlan { - compatible = "wlan-platdata"; - wifi_chip_type = "ap6275p"; - pinctrl-names = "default"; - pinctrl-0 = <0x1c6>; - WIFI,poweren_gpio = <0x13b 0x14 0x00>; - status = "disabled"; - }; - - hdmiin-dc { - compatible = "rockchip,dummy-codec"; - #sound-dai-cells = <0x00>; - phandle = <0x1c9>; - }; - - hdmiin-sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "rockchip,hdmiin"; - simple-audio-card,bitclock-master = <0x1c7>; - simple-audio-card,frame-master = <0x1c7>; - status = "disabled"; - - simple-audio-card,cpu { - sound-dai = <0x1c8>; - }; - - simple-audio-card,codec { - sound-dai = <0x1c9>; - phandle = <0x1c7>; - }; - }; - - pwm-fan { - compatible = "pwm-fan"; - #cooling-cells = <0x02>; - fan-supply = <0x1aa>; - pwms = <0x1ca 0x00 0xc350 0x01>; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.toml b/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.toml deleted file mode 100644 index 5dd99570d..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.toml +++ /dev/null @@ -1,122 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 8 -# Guest vm physical cpu sets. -phys_cpu_sets = [1, 2, 4, 8, 16, 32, 64, 128] -phys_cpu_ids = [0x00, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700] - - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x1008_0000 -# The load address of the kernel image. -kernel_load_addr = 0x1008_0000 -# The load address of the device tree blob (DTB). -dtb_load_addr = 0x1000_0000 -# The location of image: "memory" | "fs". -# load from memory -image_location = "memory" -# The file path of the kernel image. -kernel_path = "/path/to/kernel" -# The file path of the device tree blob (DTB). -dtb_path = "/path/to/dtb" - -# load from file system. -# image_location = "fs". -## The file path of the kernel image. -# kernel_path = "linux-arceos-aarch64.bin" -## The file path of the device tree blob (DTB). -# dtb_path = "linux-rk3588.dtb" - -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "disk.img" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x940_0000, 0xd550_0000, 0x7, 1], # ram 3G MAP_IDENTICAL - [0x920_0000, 0x2000, 0xf, 2] - -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - - -interrupt_mode = "passthrough" -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - [ - "ramoops", - 0x11_0000, - 0x11_0000, - 0xf_0000, - 0x17, - ], - [ - "sram", - 0x10_f000, - 0x10_f000, - 0x1000, - 0x17, - ], - [ - "gpu", - 0xfb00_0000, - 0xfb00_0000, - 0x20_0000, - 0x17, - ], - [ - "uart8250 UART", - 0xfd00_0000, - 0xfd00_0000, - 0x200_0000, - 0x17, - ], - [ - "usb", - 0xfc00_0000, - 0xfc00_0000, - 0x100_0000, - 0x17, - ], - [ - "uncached", - 0x0, - 0x0, - 0x10_f000, - 0x17, - ], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.dts b/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.dts deleted file mode 100644 index f59ffcabe..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.dts +++ /dev/null @@ -1,1302 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000080000000 0x0000000000010000; -/ { - compatible = "phytium,pe2204"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x40000000 0x00 0x40000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial0 = "/soc/uart@2800c000"; - serial1 = "/soc/uart@2800d000"; - serial2 = "/soc/uart@2800e000"; - serial3 = "/soc/uart@2800f000"; - ethernet0 = "/soc/ethernet@3200c000"; - ethernet1 = "/soc/ethernet@3200e000"; - ethernet2 = "/soc/ethernet@32010000"; - ethernet3 = "/soc/ethernet@32012000"; - serial4 = "/soc/uart@28014000"; - serial5 = "/soc/uart@2802A000"; - serial6 = "/soc/uart@28032000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - firmware { - - scmi { - compatible = "arm,scmi"; - mboxes = <0x02 0x00>; - mbox-names = "tx"; - shmem = <0x03>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@13 { - reg = <0x13>; - #clock-cells = <0x01>; - phandle = <0x09>; - }; - - protocol@15 { - reg = <0x15>; - #thermal-sensor-cells = <0x01>; - phandle = <0x04>; - }; - }; - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - }; - }; - - thermal-zones { - - sensor0 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x00>; - }; - - sensor1 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x01>; - }; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - // cluster0 { - - // core0 { - // cpu = <0x05>; - // }; - // }; - - cluster1 { - - core0 { - cpu = <0x06>; - }; - }; - - // cluster2 { - - // core0 { - // cpu = <0x07>; - // }; - - // core1 { - // cpu = <0x08>; - // }; - // }; - }; - - // cpu@0 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x200>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x07>; - // }; - - // cpu@1 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x201>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x08>; - // }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x00>; - // enable-method = "psci"; - // clocks = <0x09 0x00>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x05>; - // }; - - cpu@101 { - device_type = "cpu"; - compatible = "phytium,ftc664\0arm,armv8"; - reg = <0x00 0x100>; - enable-method = "psci"; - clocks = <0x09 0x01>; - capacity-dmips-mhz = <0x161c>; - phandle = <0x06>; - }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0x01 0x07 0x08>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - clocks { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - clk48mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2dc6c00>; - phandle = <0x13>; - }; - - clk50mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2faf080>; - phandle = <0x0d>; - }; - - clk100mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x5f5e100>; - phandle = <0x0c>; - }; - - clk200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbebc200>; - phandle = <0x11>; - }; - - clk250mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xee6b280>; - phandle = <0x12>; - }; - - clk300mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x11e1a300>; - phandle = <0x0b>; - }; - - clk600mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x23c34600>; - phandle = <0x0e>; - }; - - clk1200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x47868c00>; - phandle = <0x0a>; - }; - }; - - iommu@30000000 { - compatible = "arm,smmu-v3"; - reg = <0x00 0x30000000 0x00 0x800000>; - interrupts = <0x00 0xf0 0x01 0x00 0xef 0x01 0x00 0xec 0x01 0x00 0xf2 0x01>; - interrupt-names = "eventq\0priq\0cmdq-sync\0gerror"; - dma-coherent; - #iommu-cells = <0x01>; - phandle = <0x10>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - mmc@28000000 { - compatible = "phytium,mci"; - reg = <0x00 0x28000000 0x00 0x1000>; - interrupts = <0x00 0x48 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "disabled"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - }; - - mmc@28001000 { - compatible = "phytium,mci"; - reg = <0x00 0x28001000 0x00 0x1000>; - interrupts = <0x00 0x49 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "disabled"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - no-sd; - non-removable; - }; - - nand@28002000 { - compatible = "phytium,nfc"; - reg = <0x00 0x28002000 0x00 0x1000>; - interrupts = <0x00 0x4a 0x04>; - status = "disabled"; - }; - - ddma@28003000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28003000 0x00 0x1000>; - interrupts = <0x00 0x4b 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - ddma@28004000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28004000 0x00 0x1000>; - interrupts = <0x00 0x4c 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - spi@28008000 { - compatible = "phytium,qspi-nor"; - reg = <0x00 0x28008000 0x00 0x1000 0x00 0x00 0x00 0xfffffff>; - reg-names = "qspi\0qspi_mm"; - clocks = <0x0b>; - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x00>; - spi-rx-bus-width = <0x01>; - spi-max-frequency = <0x1312d00>; - status = "okay"; - }; - }; - - uart@2800c000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800c000 0x00 0x1000>; - interrupts = <0x00 0x53 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800e000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800e000 0x00 0x1000>; - interrupts = <0x00 0x55 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800f000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800f000 0x00 0x1000>; - interrupts = <0x00 0x56 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - lpc@28010000 { - compatible = "simple-mfd\0syscon"; - reg = <0x00 0x28010000 0x00 0x1000>; - reg-io-width = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x28010000 0x1000>; - - kcs@24 { - compatible = "phytium,kcs-bmc"; - reg = <0x24 0x01 0x30 0x01 0x3c 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@28 { - compatible = "phytium,kcs-bmc"; - reg = <0x28 0x01 0x34 0x01 0x40 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@2c { - compatible = "phytium,kcs-bmc"; - reg = <0x2c 0x01 0x38 0x01 0x44 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@8c { - compatible = "phytium,kcs-bmc"; - reg = <0x8c 0x01 0x90 0x01 0x94 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - bt@48 { - compatible = "phytium,bt-bmc"; - reg = <0x48 0x20>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - }; - - gpio@28034000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28034000 0x00 0x1000>; - interrupts = <0x00 0x6c 0x04 0x00 0x6d 0x04 0x00 0x6e 0x04 0x00 0x6f 0x04 0x00 0x70 0x04 0x00 0x71 0x04 0x00 0x72 0x04 0x00 0x73 0x04 0x00 0x74 0x04 0x00 0x75 0x04 0x00 0x76 0x04 0x00 0x77 0x04 0x00 0x78 0x04 0x00 0x79 0x04 0x00 0x7a 0x04 0x00 0x7b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28035000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28035000 0x00 0x1000>; - interrupts = <0x00 0x7c 0x04 0x00 0x7d 0x04 0x00 0x7e 0x04 0x00 0x7f 0x04 0x00 0x80 0x04 0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04 0x00 0x89 0x04 0x00 0x8a 0x04 0x00 0x8b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x14>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28036000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28036000 0x00 0x1000>; - interrupts = <0x00 0x8c 0x04 0x00 0x8d 0x04 0x00 0x8e 0x04 0x00 0x8f 0x04 0x00 0x90 0x04 0x00 0x91 0x04 0x00 0x92 0x04 0x00 0x93 0x04 0x00 0x94 0x04 0x00 0x95 0x04 0x00 0x96 0x04 0x00 0x97 0x04 0x00 0x98 0x04 0x00 0x99 0x04 0x00 0x9a 0x04 0x00 0x9b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x15>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28037000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28037000 0x00 0x1000>; - interrupts = <0x00 0x9c 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28038000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28038000 0x00 0x1000>; - interrupts = <0x00 0x9d 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28039000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28039000 0x00 0x1000>; - interrupts = <0x00 0x9e 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - spi@2803a000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803a000 0x00 0x1000>; - interrupts = <0x00 0x9f 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - global-cs = <0x01>; - - spidev@0 { - compatible = "spidev"; - reg = <0x00>; - spi-max-frequency = <0x2faf080>; - status = "disabled"; - }; - }; - - spi@2803b000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803b000 0x00 0x1000>; - interrupts = <0x00 0xa0 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803c000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803c000 0x00 0x1000>; - interrupts = <0x00 0xa1 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803d000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803d000 0x00 0x1000>; - interrupts = <0x00 0xa2 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - watchdog@28040000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28041000 0x00 0x1000 0x00 0x28040000 0x00 0x1000>; - interrupts = <0x00 0xa4 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - watchdog@28042000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28043000 0x00 0x1000 0x00 0x28042000 0x00 0x1000>; - interrupts = <0x00 0xa5 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - pwm@2804a000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804a000 0x00 0x1000>; - interrupts = <0x00 0xad 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - pwm@2804b000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804b000 0x00 0x1000>; - interrupts = <0x00 0xae 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - tacho@28054000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28054000 0x00 0x1000>; - interrupts = <0x00 0xc2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28055000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28055000 0x00 0x1000>; - interrupts = <0x00 0xc3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28056000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28056000 0x00 0x1000>; - interrupts = <0x00 0xc4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28057000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28057000 0x00 0x1000>; - interrupts = <0x00 0xc5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28058000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28058000 0x00 0x1000>; - interrupts = <0x00 0xc6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28059000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28059000 0x00 0x1000>; - interrupts = <0x00 0xc7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805a000 0x00 0x1000>; - interrupts = <0x00 0xc8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805b000 0x00 0x1000>; - interrupts = <0x00 0xc9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805c000 0x00 0x1000>; - interrupts = <0x00 0xca 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805d000 0x00 0x1000>; - interrupts = <0x00 0xcb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805e000 0x00 0x1000>; - interrupts = <0x00 0xcc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805f000 0x00 0x1000>; - interrupts = <0x00 0xcd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28060000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28060000 0x00 0x1000>; - interrupts = <0x00 0xce 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28061000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28061000 0x00 0x1000>; - interrupts = <0x00 0xcf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28062000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28062000 0x00 0x1000>; - interrupts = <0x00 0xd0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28063000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28063000 0x00 0x1000>; - interrupts = <0x00 0xd1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28064000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28064000 0x00 0x1000>; - interrupts = <0x00 0xd2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28065000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28065000 0x00 0x1000>; - interrupts = <0x00 0xd3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28066000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28066000 0x00 0x1000>; - interrupts = <0x00 0xd4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28067000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28067000 0x00 0x1000>; - interrupts = <0x00 0xd5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28068000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28068000 0x00 0x1000>; - interrupts = <0x00 0xd6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28069000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28069000 0x00 0x1000>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806a000 0x00 0x1000>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806b000 0x00 0x1000>; - interrupts = <0x00 0xd9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806c000 0x00 0x1000>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806d000 0x00 0x1000>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806e000 0x00 0x1000>; - interrupts = <0x00 0xdc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806f000 0x00 0x1000>; - interrupts = <0x00 0xdd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28070000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28070000 0x00 0x1000>; - interrupts = <0x00 0xde 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28071000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28071000 0x00 0x1000>; - interrupts = <0x00 0xdf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28072000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28072000 0x00 0x1000>; - interrupts = <0x00 0xe0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28073000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28073000 0x00 0x1000>; - interrupts = <0x00 0xe1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28074000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28074000 0x00 0x1000>; - interrupts = <0x00 0xe2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28075000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28075000 0x00 0x1000>; - interrupts = <0x00 0xe3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28076000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28076000 0x00 0x1000>; - interrupts = <0x00 0xe4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28077000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28077000 0x00 0x1000>; - interrupts = <0x00 0xe5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28078000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28078000 0x00 0x1000>; - interrupts = <0x00 0xe6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28079000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28079000 0x00 0x1000>; - interrupts = <0x00 0xe7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb2@31800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31800000 0x00 0x80000 0x00 0x31990000 0x00 0x10000>; - interrupts = <0x00 0x20 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@31880000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31880000 0x00 0x80000 0x00 0x319a0000 0x00 0x10000>; - interrupts = <0x00 0x21 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@31900000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31900000 0x00 0x80000 0x00 0x319b0000 0x00 0x10000>; - interrupts = <0x00 0x22 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@32800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32800000 0x00 0x40000 0x00 0x32880000 0x00 0x40000>; - interrupts = <0x00 0x0e 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@32840000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32840000 0x00 0x40000 0x00 0x328c0000 0x00 0x40000>; - interrupts = <0x00 0x0f 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - dc@32000000 { - compatible = "phytium,dc"; - reg = <0x00 0x32000000 0x00 0x8000>; - interrupts = <0x00 0x2c 0x04>; - status = "okay"; - pipe_mask = [01]; - edp_mask = [00]; - }; - - i2s_dp0@32009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x32009000 0x00 0x1000 0x00 0x32008000 0x00 0x1000>; - interrupts = <0x00 0x2f 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp0"; - status = "okay"; - }; - - i2s_dp1@3200B000 { - compatible = "phytium,i2s"; - reg = <0x00 0x3200b000 0x00 0x1000 0x00 0x3200a000 0x00 0x1000>; - interrupts = <0x00 0x30 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp1"; - status = "disabled"; - }; - - pmdk_dp { - compatible = "phytium,pmdk-dp"; - status = "okay"; - num-dp = <0x01>; - dp-mask = [01]; - }; - - mailbox@32a00000 { - compatible = "phytium,mbox"; - reg = <0x00 0x32a00000 0x00 0x1000>; - interrupts = <0x00 0x16 0x04>; - #mbox-cells = <0x01>; - phandle = <0x02>; - }; - - rng@32a36000 { - compatible = "phytium,rng"; - reg = <0x00 0x32a36000 0x00 0x1000>; - status = "okay"; - }; - - sram@32a10000 { - compatible = "phytium,pe220x-sram-ns\0mmio-sram"; - reg = <0x00 0x32a10000 0x00 0x2000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x32a10000 0x2000>; - - scp-shmem@0 { - compatible = "arm,scmi-shmem"; - reg = <0x1000 0x400>; - }; - - scp-shmem@1 { - compatible = "arm,scmi-shmem"; - reg = <0x1400 0x400>; - phandle = <0x03>; - }; - }; - - gdma@32b34000 { - compatible = "phytium,gdma"; - dma-channels = <0x10>; - max-outstanding = <0x10>; - reg = <0x00 0x32b34000 0x00 0x1000>; - interrupts = <0x00 0xea 0x04>; - #dma-cells = <0x01>; - }; - - spinlock@32b36000 { - compatible = "phytium,hwspinlock"; - reg = <0x00 0x32b36000 0x00 0x1000>; - #hwlock-cells = <0x01>; - nr-locks = <0x20>; - status = "disabled"; - }; - - pcie@40000000 { - compatible = "pci-host-ecam-generic"; - device_type = "pci"; - #address-cells = <0x03>; - #size-cells = <0x02>; - #interrupt-cells = <0x01>; - reg = <0x00 0x40000000 0x00 0x10000000>; - msi-parent = <0x0f>; - bus-range = <0x00 0xff>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x07 0x04>; - ranges = <0x1000000 0x00 0x00 0x00 0x50000000 0x00 0xf00000 0x2000000 0x00 0x58000000 0x00 0x58000000 0x00 0x28000000 0x3000000 0x10 0x00 0x10 0x00 0x10 0x00>; - iommu-map = <0x00 0x10 0x00 0x10000>; - status = "okay"; - }; - - edac@32b28000 { - compatible = "phytium,pe220x-edac"; - reg = <0x00 0x32b28000 0x00 0x1000 0x00 0x31400000 0x00 0x1000 0x00 0x31401000 0x00 0x1000>; - interrupts = <0x00 0x00 0x04 0x00 0x01 0x04>; - status = "disabled"; - }; - - hda@28006000 { - compatible = "phytium,hda"; - reg = <0x00 0x28006000 0x00 0x1000>; - interrupts = <0x00 0x4e 0x04>; - status = "disabled"; - }; - - i2s@28009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x28009000 0x00 0x1000 0x00 0x28005000 0x00 0x1000>; - interrupts = <0x00 0x4d 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - status = "okay"; - #sound-dai-cells = <0x00>; - dai-name = "phytium-i2s-lsd"; - phandle = <0x16>; - }; - - can@2800a000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800a000 0x00 0x1000>; - interrupts = <0x00 0x51 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - can@2800b000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800b000 0x00 0x1000>; - interrupts = <0x00 0x52 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - keypad@2807a000 { - compatible = "phytium,keypad"; - reg = <0x00 0x2807a000 0x00 0x1000>; - interrupts = <0x00 0xbd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb3@31a08000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a08000 0x00 0x18000>; - interrupts = <0x00 0x10 0x04>; - status = "okay"; - }; - - usb3@31a28000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a28000 0x00 0x18000>; - interrupts = <0x00 0x11 0x04>; - status = "okay"; - }; - - sata@31a40000 { - compatible = "generic-ahci"; - reg = <0x00 0x31a40000 0x00 0x1000>; - interrupts = <0x00 0x2a 0x04>; - status = "okay"; - }; - - sata@32014000 { - compatible = "generic-ahci"; - reg = <0x00 0x32014000 0x00 0x1000>; - interrupts = <0x00 0x2b 0x04>; - status = "okay"; - }; - - ethernet@3200c000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200c000 0x00 0x2000>; - interrupts = <0x00 0x37 0x04 0x00 0x38 0x04 0x00 0x39 0x04 0x00 0x3a 0x04 0x00 0x1c 0x04 0x00 0x1d 0x04 0x00 0x1e 0x04 0x00 0x1f 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - support-tsn; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@3200e000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200e000 0x00 0x2000>; - interrupts = <0x00 0x3b 0x04 0x00 0x3c 0x04 0x00 0x3d 0x04 0x00 0x3e 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@32010000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32010000 0x00 0x2000>; - interrupts = <0x00 0x40 0x04 0x00 0x41 0x04 0x00 0x42 0x04 0x00 0x43 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - ethernet@32012000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32012000 0x00 0x2000>; - interrupts = <0x00 0x44 0x04 0x00 0x45 0x04 0x00 0x46 0x04 0x00 0x47 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - vpu@32b00000 { - compatible = "phytium,vpu"; - reg = <0x00 0x32b00000 0x00 0x20000>; - interrupts = <0x00 0x0c 0x04>; - status = "okay"; - }; - - i2c@28026000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28026000 0x00 0x1000>; - interrupts = <0x00 0x65 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28030000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28030000 0x00 0x1000>; - interrupts = <0x00 0x6a 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - es8336@10 { - #sound-dai-cells = <0x00>; - compatible = "everest,es8336"; - reg = <0x10>; - phandle = <0x17>; - }; - }; - - uart@28014000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28014000 0x00 0x1000>; - interrupts = <0x00 0x5c 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - i2c@28016000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28016000 0x00 0x1000>; - interrupts = <0x00 0x5d 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28024000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28024000 0x00 0x1000>; - interrupts = <0x00 0x64 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - uart@2802A000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2802a000 0x00 0x1000>; - interrupts = <0x00 0x67 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@28032000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28032000 0x00 0x1000>; - interrupts = <0x00 0x6b 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 rootwait fsckfix root=/dev/sda2 rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory { - device_type = "memory"; - reg = <0x20 0x40000000 0x00 0x40000000>; - }; - - leds { - compatible = "gpio-leds"; - - sysled { - label = "sysled"; - gpios = <0x14 0x05 0x00>; - linux,default-trigger = "none"; - }; - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "phytium,pe220x-i2s-audio"; - simple-audio-card,pin-switches = "mic-in"; - simple-audio-card,widgets = "Microphone\0mic-in\0Headphone\0Headphones"; - simple-audio-card,routing = "MIC2\0mic-in"; - simple-audio-card,hp-det-gpio = <0x15 0x0b 0x01>; - - simple-audio-card,cpu { - sound-dai = <0x16>; - }; - - simple-audio-card,codec { - sound-dai = <0x17>; - }; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.toml b/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.toml deleted file mode 100644 index e136765f6..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.toml +++ /dev/null @@ -1,62 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x100] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_4008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "memory" -# The load address of the kernel image. -kernel_load_addr = 0x20_4008_0000 -## The file path of the kernel image. -kernel_path = "/path/to/Image" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_4000_0000 -#dtb_path = "/path/to/linux-aarch64-tac_e400-smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_4000_0000, 0x4000_0000, 0x7, 1], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -interrupt_mode = "passthrough" -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/nimbos-aarch64-qemu-smp1.toml b/os/axvisor/configs/vms/nimbos-aarch64-qemu-smp1.toml deleted file mode 100644 index 17dac0d43..000000000 --- a/os/axvisor/configs/vms/nimbos-aarch64-qemu-smp1.toml +++ /dev/null @@ -1,81 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "nimbos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu sets. -phys_cpu_ids = [0] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x4008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The file path of the kernel image. -kernel_path = "nimbos-aarch64.bin" -# kernel_path = "/home/debin/Codes/arm_tiny/build/arm_tiny.bin" -# The load address of the kernel image. -kernel_load_addr = 0x4008_0000 - -## The file path of the BIOS image. -# bios_path = "" -## The load address of the BIOS image. -# bios_load_addr = 0 -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x4000_0000, 0x100_0000, 0x7, 0], # Low RAM 16M 0b00111 R|W|EXECUTE -] - -# -# Device specifications -# -[devices] -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [ - # [ - # "vgicd-v2", - # 0x800_0000, - # 0x10_000, - # 25, - # 1, - # [2], - # ], # Vgicdv2 4k irq-25 EmuDeviceTGicdV2 config: vcpu_num -] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/pl011@9000000"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - ["/pcie@10000000"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/nimbos-riscv64-qemu-smp1.toml b/os/axvisor/configs/vms/nimbos-riscv64-qemu-smp1.toml deleted file mode 100644 index 5b08bc1d4..000000000 --- a/os/axvisor/configs/vms/nimbos-riscv64-qemu-smp1.toml +++ /dev/null @@ -1,59 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "nimbos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu sets. -phys_cpu_sets = [1] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x9020_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The file path of the kernel image. -kernel_path = "nimbos-riscv64.bin" -# The load address of the kernel image. -kernel_load_addr = 0x9020_0000 - -## The file path of the BIOS image. -# bios_path = "" -## The load address of the BIOS image. -# bios_load_addr = -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x9000_0000, 0x100_0000, 0xf, 0], # Low RAM 16M 0b1111 R|W|EXECUTE|U -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["PLIC@c000000", 0x0c00_0000, 0x0c00_0000, 0x21_0000, 0x1], - ["UART@10000000", 0x1000_0000, 0x1000_0000, 0x1000, 0x1], -] diff --git a/os/axvisor/configs/vms/nimbos-x86_64-qemu-smp1.toml b/os/axvisor/configs/vms/nimbos-x86_64-qemu-smp1.toml deleted file mode 100644 index f937a41b1..000000000 --- a/os/axvisor/configs/vms/nimbos-x86_64-qemu-smp1.toml +++ /dev/null @@ -1,80 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "nimbos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu sets. -phys_cpu_sets = [1] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8000 -# The location of image: "memory" | "fs". -# Load kernel image into memory; path will be patched by CI script. -image_location = "memory" -# The file path of the kernel image. -kernel_path = "/tmp/.axvisor-images/qemu_x86_64_nimbos/qemu-x86_64" -# The load address of the kernel image. -kernel_load_addr = 0x20_0000 -# The file path of the BIOS image. -# NimbOS x86_64 needs axvm-bios at 0x8000 to bootstrap; path will be patched by setup script. -bios_path = "/tmp/.axvisor-images/qemu_x86_64_nimbos/axvm-bios.bin" -# The load address of the BIOS image. -bios_load_addr = 0x8000 - -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x0000_0000, 0x100_0000, 0x7, 0], # Low RAM 16M 0b111 R|W|EXECUTE -] - -# -# Device specifications -# -[devices] -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - [ - "IO APIC", - 0xfec0_0000, - 0xfec0_0000, - 0x1000, - 0x1, - ], - [ - "Local APIC", - 0xfee0_0000, - 0xfee0_0000, - 0x1000, - 0x1, - ], - [ - "HPET", - 0xfed0_0000, - 0xfed0_0000, - 0x1000, - 0x1, - ], -] diff --git a/os/axvisor/configs/vms/rtthread-aarch64-e2000-smp1.toml b/os/axvisor/configs/vms/rtthread-aarch64-e2000-smp1.toml deleted file mode 100644 index c999c3404..000000000 --- a/os/axvisor/configs/vms/rtthread-aarch64-e2000-smp1.toml +++ /dev/null @@ -1,63 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "rtthread" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x100] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x8008_0000 -## The file path of the kernel image. -kernel_path = "/guest/rtthread/phytiumpi" -## The file path of the device tree blob (DTB). -# dtb_load_addr = 0x8000_0000 -#dtb_path = "/path/to/linux-aarch64-tac_e400-smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 0], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -interrupt_mode = "passthrough" -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - # [0x28041000, 0x100_0000] - [0x32b30000, 0x1000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/doc/FDT_Configuration_Guide.md b/os/axvisor/doc/FDT_Configuration_Guide.md deleted file mode 100644 index a9724ac15..000000000 --- a/os/axvisor/doc/FDT_Configuration_Guide.md +++ /dev/null @@ -1,274 +0,0 @@ -# AxVisor 设备树配置使用说明 - -本文档详细说明了在 AxVisor 中如何配置和使用设备树(FDT)来生成客户机 VM 的设备树。 - -本文档所述功能只在aarch64 架构下支持。 - -## 1. 概述 - -AxVisor 支持两种方式生成客户机 VM 的设备树: - -1. **使用预定义的设备树文件**:通过 [kernel] 部分的 `dtb_path` 指定设备树文件路径 -2. **动态生成设备树**:当 `dtb_path` 字段未使用时,根据配置文件中的参数动态生成设备树 - -无论采用哪种方式,CPU 节点和内存节点都会根据配置进行更新。 - -## 2. 配置文件结构 - -配置文件采用 TOML 格式,主要包含以下几个部分: - -```toml -[base] -# 基本配置信息 - -[kernel] -# 内核和设备树配置 - -[devices] -# 设备配置信息 -``` - -## 3. 设备树处理机制 - -### 3.1 使用预定义设备树文件 - -当 [kernel] 部分的 `dtb_path` 配置了设备树文件路径时: - -```toml -[kernel] -dtb_path = "/path/to/device-tree.dtb" -``` - -AxVisor 会优先使用提供的设备树文件,并根据以下配置更新其中的 CPU 节点和内存节点: - -- CPU 节点根据 [base] 部分的 `phys_cpu_ids` 更新 -- 内存节点根据 [kernel] 部分的 `memory_regions` 更新 - -注意:当使用预定义设备树文件时,[devices] 部分的 `passthrough_devices` 中如果有规范化的[Name,Base-Ipa,Base-Pa,Length,Alloc-Irq]设备配置,则axvisor会直接按照 `passthrough_devices`中的配置给guest映射设备内存,设备树中的解析则会被忽略,只是将更改过内存和cpu的预定义设备树文件直接传给guest。 - -### 3.2 动态生成设备树 - -当 [kernel] 部分的 `dtb_path` 未添加时: - -```toml -[kernel] -# dtb_path = "" -``` - -AxVisor 会根据配置文件中的参数动态生成客户机设备树: - -1. **CPU 节点**:根据 [base] 部分的 `phys_cpu_ids` 生成 -2. **内存节点**:根据 [kernel] 部分的 `memory_regions` 生成 -3. **其他设备节点**:根据 [devices] 部分的 `passthrough_devices` 和 `excluded_devices` 生成 - -## 4. 配置参数详解 - -### 4.1 基本配置 [base] - -```toml -[base] -id = 1 # 客户机 VM ID -name = "linux-qemu" # 客户机 VM 名称 -vm_type = 1 # 虚拟化类型 -cpu_num = 1 # 虚拟 CPU 数量 -phys_cpu_ids = [0] # 客户机 VM 物理 CPU 集合 -``` - -注意:配置文件中的 `phys_cpu_sets` 字段已不再需要手动配置。AxVisor 会根据主机设备树和 `phys_cpu_ids` 自动识别并生成相应的 CPU 集合掩码。 - -### 4.2 内核配置 [kernel] - -```toml -[kernel] -entry_point = 0x8020_0000 # 内核镜像入口点 -image_location = "memory" # 镜像位置 ("memory" | "fs") -kernel_path = "tmp/Image" # 内核镜像文件路径 -kernel_load_addr = 0x8020_0000 # 内核镜像加载地址 -dtb_path = "tmp/linux.dtb" # 设备树文件路径(空字符串表示动态生成) -dtb_load_addr = 0x8000_0000 # 设备树加载地址 - -# 内存区域配置,格式为 (基地址, 大小, 标志, 映射类型) -其中映射类型0为MAP_Alloc(由host负责,随机分配内存),1为Map_Identical(由host负责1:1给guest映射内存,但是起始地址随机),2为MAP_Reserved(由host负责,将host中一块标记为reserved的内存完全1:1映射给guest,起始地址和配置一致) -memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 0], # 系统 RAM 1G MAP_IDENTICAL -] -``` - -### 4.3 设备配置 [devices] - -```toml -[devices] -# 直通设备配置(仅在动态生成设备树时生效) -passthrough_devices = [ - ["/intc"], -] - -# 排除设备配置(仅在动态生成设备树时生效) -excluded_devices = [ - ["/intc"], -] -``` - -注意:直通设备配置已简化,现在只需要提供从根节点开始的完整路径即可,如 ["/intc"]。设备的地址、大小等信息会根据设备树自动识别并直通,无需手动填写。 - -## 5. 设备直通机制 - -### 5.1 直通设备配置 - -`passthrough_devices` 定义了需要直通给客户机的设备节点: - -```toml -passthrough_devices = [ - ["/"], # 直通根节点及其所有子节点 - ["/intc"], # 直通 /intc 节点及其子节点 -] -``` - -设备节点格式为从根节点开始的全局路径(如 `/intc`),在直通时会将以下节点包含在客户机设备树中: - -1. 指定的直通节点本身 -2. 直通节点的所有后代节点 -3. 与直通设备相关的依赖节点 - -注意: -1. 此配置仅在动态生成设备树时生效,当使用预定义设备树文件时将被忽略。 -2. 直通设备配置已简化,现在只需要提供从根节点开始的完整路径即可,设备的地址、大小等信息会根据设备树自动识别并直通。 - -### 5.2 排除设备配置 - -`excluded_devices` 定义了不希望直通给客户机的设备节点: - -```toml -excluded_devices = [ - ["/timer"], # 排除 /timer 节点及其子节点 -] -``` - -在查找所有直通节点后,会将排除的节点及其后代节点从最终的客户机设备树中移除。 - -注意:此配置仅在动态生成设备树时生效,当使用预定义设备树文件时将被忽略。 - -### 5.3 直通地址配置 - -`passthrough_addresses` 定义了直通给客户机使用的地址信息: - -``` -passthrough_addresses = [ - [0x28041000, 0x100_0000], -] -``` - -该字段定义的地址会直通给客户机使用,这在某些情况下非常有用,例如设备树文件为非标准设备树格式或客户机系统时定制linux。 - -## 6. 示例配置 - -### 6.1 使用预定义设备树文件的配置 - -```toml -[base] -id = 1 -name = "linux-qemu" -vm_type = 1 -cpu_num = 2 -phys_cpu_ids = [0, 1] -# phys_cpu_sets 不再需要手动配置,会自动根据 phys_cpu_ids 生成 - -[kernel] -entry_point = 0x8020_0000 -image_location = "memory" -kernel_path = "tmp/Image" -kernel_load_addr = 0x8020_0000 -dtb_path = "/home/user/device-tree.dtb" # 使用预定义设备树文件 -dtb_load_addr = 0x8000_0000 - -memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -[devices] -# 注意:以下配置在使用预定义设备树时将被忽略 -passthrough_devices = [ - ["/intc"], -] -# 直通地址配置 -passthrough_addresses = [ - [0x28041000, 0x100_0000], -] - -excluded_devices = [ - ["/timer"], -] -``` - -### 6.2 动态生成设备树的配置 - -```toml -[base] -id = 1 -name = "linux-qemu" -vm_type = 1 -cpu_num = 2 -phys_cpu_ids = [0, 1] -# phys_cpu_sets 不再需要手动配置,会自动根据 phys_cpu_ids 生成 - -[kernel] -entry_point = 0x8020_0000 -image_location = "memory" -kernel_path = "tmp/Image" -kernel_load_addr = 0x8020_0000 -# dtb_path = "" # 不使用该字段表示动态生成设备树 -dtb_load_addr = 0x8000_0000 - -memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -[devices] -# 以下配置仅在动态生成设备树时生效 -# 注意:直通设备配置已简化,现在只需要提供从根节点开始的完整路径即可 -passthrough_devices = [ - ["/"], - ["/intc"], -] -# 直通地址配置 -passthrough_addresses = [ - [0x28041000, 0x100_0000], -] -excluded_devices = [ - ["/timer"], - ["/watchdog"], -] -``` - -## 7. 处理流程 - -1. **检查 dtb_path**: - - 如果使用 `dtb_path` 字段,则加载并使用预定义的设备树文件,此时 `passthrough_devices` 和 `excluded_devices` 配置将被忽略 - - 如果未使用 `dtb_path` 字段,则动态生成设备树,此时 `passthrough_devices` 和 `excluded_devices` 配置生效 - -2. **CPU 节点处理**: - - 根据 `phys_cpu_ids` 配置更新或生成 CPU 节点 - - 只包含配置中指定的 CPU - - 自动根据 `phys_cpu_ids` 生成 `phys_cpu_sets`,无需手动配置 - -3. **内存节点处理**: - - 根据 `memory_regions` 配置更新或生成内存节点 - - 按照指定的地址和大小创建内存区域 - -4. **设备节点处理**(仅在动态生成时): - - 根据 `passthrough_devices` 确定需要包含的设备节点 - - 包括直通节点、其后代节点以及相关依赖节点 - - 根据 `excluded_devices` 排除指定的设备节点及其后代节点 - -5. **生成最终设备树**: - - 将处理后的节点组合成完整的设备树 - - 存储在全局缓存中供后续使用 - -## 8. 特别配置 -1. **qemu 启动参数**: -``` - arceos_args = ["BUS=mmio", "BLK=y", "LOG=info", "SMP=4", "MEM=8g", - "QEMU_ARGS=\"-machine gic-version=3 -cpu cortex-a72 -append 'root=/dev/vda rw init=/init' \"", - "DISK_IMG=\"tmp/qemu/rootfs.img\"",] -``` -其中当不提供设备树时 `-append 'root=/dev/vda rw init=/init'`参数必须添加,目的是在主机设备树中添加chosen节点的bootargs属性。 \ No newline at end of file diff --git a/os/axvisor/doc/qemu-quickstart.md b/os/axvisor/doc/qemu-quickstart.md deleted file mode 100644 index 61d55096d..000000000 --- a/os/axvisor/doc/qemu-quickstart.md +++ /dev/null @@ -1,151 +0,0 @@ -# QEMU Quickstart Guide - -English | [中文](qemu-quickstart_cn.md) - -This guide covers how to set up the AxVisor development environment locally and run different guest operating systems on QEMU. - -## Prerequisites - -- **OS**: Linux (native or WSL2) -- **Architecture**: x86_64 host - -## 1. Install System Dependencies - -```bash -sudo apt update && sudo apt install -y \ - build-essential gcc libssl-dev libudev-dev pkg-config \ - qemu-system-x86 qemu-system-arm qemu-system-misc \ - git curl wget -``` - -## 2. Install Rust Toolchain - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source "$HOME/.cargo/env" -``` - -Once you enter the project directory, Rust will automatically install the required nightly toolchain, components, and cross-compilation targets based on `rust-toolchain.toml` — no manual configuration needed. - -Install additional Cargo tools: - -```bash -cargo install cargo-binutils -cargo +stable install ostool --version '^0.8' -``` - -- `cargo-binutils`: provides `rust-objcopy`, `rust-objdump`, etc. -- `ostool`: custom build runner for AxVisor - -## 3. KVM Setup (NimbOS x86_64 Only) - -NimbOS runs on x86_64 QEMU and requires KVM hardware acceleration. ArceOS and Linux use AArch64 QEMU (TCG mode) and do not need KVM — you can skip this section. - -Verify the KVM device exists: - -```bash -ls -la /dev/kvm -``` - -Add your user to the `kvm` group: - -```bash -sudo usermod -aG kvm $USER -``` - -Apply the group change in the current terminal without re-logging: - -```bash -newgrp kvm -``` - -Verify: - -```bash -id # output should include "kvm" -``` - -## 4. Running Guest OSes - -This branch provides a one-click setup script `scripts/setup_qemu.sh` that automatically downloads guest images, patches configuration paths, and prepares the rootfs. - -### ArceOS (AArch64) - -```bash -./scripts/setup_qemu.sh arceos - -cargo xtask qemu \ - --build-config configs/board/qemu-aarch64.toml \ - --qemu-config .github/workflows/qemu-aarch64.toml \ - --vmconfigs tmp/vmconfigs/arceos-aarch64-qemu-smp1.generated.toml -``` - -Success indicator: `Hello, world!` appears in the output. - -### Linux (AArch64) - -```bash -./scripts/setup_qemu.sh linux - -cargo xtask qemu \ - --build-config configs/board/qemu-aarch64.toml \ - --qemu-config .github/workflows/qemu-aarch64.toml \ - --vmconfigs tmp/vmconfigs/linux-aarch64-qemu-smp1.generated.toml -``` - -Success indicator: `test pass!` appears in the output. - -### NimbOS (x86_64, requires KVM) - -```bash -./scripts/setup_qemu.sh nimbos - -cargo xtask qemu \ - --build-config configs/board/qemu-x86_64.toml \ - --qemu-config .github/workflows/qemu-x86_64-kvm.toml \ - --vmconfigs tmp/vmconfigs/nimbos-x86_64-qemu-smp1.generated.toml -``` - -After booting, you will enter the Rust user shell (`>>` prompt). Type `usertests` to run the test suite. All tests passing will print `usertests passed!` - -> **Note**: NimbOS requires VT-x/KVM. If `/dev/kvm` does not exist or has insufficient permissions, you will get a `Permission denied` error. WSL2 requires nested virtualization support in the kernel to use KVM. - -## 5. What Does setup_qemu.sh Do? - -The script automates three steps, eliminating manual work: - -1. **Download images**: calls `cargo xtask image download` to fetch guest images to `/tmp/.axvisor-images/` -2. **Generate temp configs**: copies VM config templates to `tmp/vmconfigs/*.generated.toml`, then uses `sed` to update `kernel_path` (and `bios_path` for NimbOS) to actual image paths without modifying tracked files in `configs/vms/*.toml` -3. **Prepare rootfs**: copies `rootfs.img` to the project's `tmp/` directory for QEMU to use - -You can also perform these steps manually if you prefer not to use the script. - -## Troubleshooting - -### `Path tmp/Image not found` - -The `kernel_path` in the VM config points to a non-existent file. Run `./scripts/setup_qemu.sh ` to automatically fix the paths. - -### `Could not access KVM kernel module: Permission denied` - -Your user is not in the `kvm` group. See the "KVM Setup" section above. - -### `qemu-system-aarch64: command not found` - -QEMU is not installed. Run the `apt install` command from Step 1. - -### `Auto syncing from registry ... timed out` - -This usually indicates unstable access to GitHub Raw endpoints. `scripts/setup_qemu.sh` includes one built-in recovery attempt: when the first image download fails, it bootstraps a local registry and retries once automatically. The script also has a default fallback registry (currently pointing to `v0.0.22.toml`). - -If your network is unstable for specific registry URLs, you can override the fallback registry: - -```bash -export AXVISOR_REGISTRY_FALLBACK_URL="https://raw.githubusercontent.com/arceos-hypervisor/axvisor-guest/refs/heads/main/registry/v0.0.22.toml" -./scripts/setup_qemu.sh arceos -``` - -### First build is very slow - -This is expected. AxVisor has many dependencies, and the first compilation needs to download and build all crates. Subsequent incremental builds will be much faster. - diff --git a/os/axvisor/doc/qemu-quickstart_cn.md b/os/axvisor/doc/qemu-quickstart_cn.md deleted file mode 100644 index 8a3c25553..000000000 --- a/os/axvisor/doc/qemu-quickstart_cn.md +++ /dev/null @@ -1,151 +0,0 @@ -# QEMU 快速上手指南 - -[English](qemu-quickstart.md) | 中文 - -本文档介绍如何在本地搭建 AxVisor 的开发运行环境,并通过 QEMU 运行不同的客户机系统。 - -## 环境要求 - -- **操作系统**:Linux(原生 / WSL2 均可) -- **架构**:x86_64 宿主机 - -## 1. 安装系统依赖 - -```bash -sudo apt update && sudo apt install -y \ - build-essential gcc libssl-dev libudev-dev pkg-config \ - qemu-system-x86 qemu-system-arm qemu-system-misc \ - git curl wget -``` - -## 2. 安装 Rust 工具链 - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source "$HOME/.cargo/env" -``` - -进入项目目录后,Rust 会根据 `rust-toolchain.toml` 自动安装所需的 nightly 工具链、组件和交叉编译目标,无需手动配置。 - -安装额外的 Cargo 工具: - -```bash -cargo install cargo-binutils -cargo +stable install ostool --version '^0.8' -``` - -- `cargo-binutils`:提供 `rust-objcopy`、`rust-objdump` 等工具 -- `ostool`:AxVisor 的自定义构建运行器 - -## 3. KVM 配置(仅 NimbOS x86_64 需要) - -NimbOS 运行在 x86_64 QEMU 上并依赖 KVM 硬件加速。ArceOS 和 Linux 使用 AArch64 QEMU(TCG 模式),不需要 KVM,可跳过本节。 - -确认 KVM 设备存在: - -```bash -ls -la /dev/kvm -``` - -将当前用户加入 `kvm` 组: - -```bash -sudo usermod -aG kvm $USER -``` - -使组权限在当前终端立即生效(无需重新登录): - -```bash -newgrp kvm -``` - -验证: - -```bash -id # 输出应包含 "kvm" -``` - -## 4. 运行客户机 - -本分支提供了一键部署脚本 `scripts/setup_qemu.sh`,自动完成镜像下载、配置路径修改和 rootfs 准备。 - -### ArceOS(AArch64) - -```bash -./scripts/setup_qemu.sh arceos - -cargo xtask qemu \ - --build-config configs/board/qemu-aarch64.toml \ - --qemu-config .github/workflows/qemu-aarch64.toml \ - --vmconfigs tmp/vmconfigs/arceos-aarch64-qemu-smp1.generated.toml -``` - -启动成功标志:输出中出现 `Hello, world!` - -### Linux(AArch64) - -```bash -./scripts/setup_qemu.sh linux - -cargo xtask qemu \ - --build-config configs/board/qemu-aarch64.toml \ - --qemu-config .github/workflows/qemu-aarch64.toml \ - --vmconfigs tmp/vmconfigs/linux-aarch64-qemu-smp1.generated.toml -``` - -启动成功标志:输出中出现 `test pass!` - -### NimbOS(x86_64,需要 KVM) - -```bash -./scripts/setup_qemu.sh nimbos - -cargo xtask qemu \ - --build-config configs/board/qemu-x86_64.toml \ - --qemu-config .github/workflows/qemu-x86_64-kvm.toml \ - --vmconfigs tmp/vmconfigs/nimbos-x86_64-qemu-smp1.generated.toml -``` - -启动成功后会进入 Rust user shell(`>>` 提示符),输入 `usertests` 运行测试套件,全部通过后输出 `usertests passed!` - -> **注意**:NimbOS 依赖 VT-x/KVM。如果 `/dev/kvm` 不存在或权限不足,会报 `Permission denied` 错误。WSL2 需要内核支持嵌套虚拟化才能使用 KVM。 - -## 5. setup_qemu.sh 做了什么 - -该脚本自动完成以下三步,省去手动操作: - -1. **下载镜像**:调用 `cargo xtask image download` 将 Guest 镜像下载到 `/tmp/.axvisor-images/` -2. **生成临时配置**:复制模板 VM 配置到 `tmp/vmconfigs/*.generated.toml`,并用 `sed` 更新 `kernel_path`(以及 NimbOS 的 `bios_path`)到实际镜像路径,不修改仓库内 `configs/vms/*.toml` -3. **准备 rootfs**:将 `rootfs.img` 复制到项目的 `tmp/` 目录下供 QEMU 使用 - -如果不想使用脚本,也可以手动执行上述步骤。 - -## 常见问题 - -### `Path tmp/Image not found` - -VM 配置中的 `kernel_path` 指向了不存在的文件。运行 `./scripts/setup_qemu.sh ` 会自动修正路径。 - -### `Could not access KVM kernel module: Permission denied` - -当前用户不在 `kvm` 组中。参见上文「KVM 配置」一节。 - -### `qemu-system-aarch64: command not found` - -未安装 QEMU。执行第 1 步的 `apt install` 命令。 - -### `Auto syncing from registry ... timed out` - -这通常是访问 GitHub Raw 不稳定导致的。`scripts/setup_qemu.sh` 已内置一次自动恢复逻辑:首次下载失败后,会尝试自动引导本地 registry 并重试镜像下载。脚本内也提供了默认 fallback registry(当前指向 `v0.0.22.toml`)。 - -如果你所在网络环境对部分 URL 不稳定,可显式覆盖 fallback registry: - -```bash -export AXVISOR_REGISTRY_FALLBACK_URL="https://raw.githubusercontent.com/arceos-hypervisor/axvisor-guest/refs/heads/main/registry/v0.0.22.toml" -./scripts/setup_qemu.sh arceos -``` - -### 首次构建非常慢 - -正常现象。AxVisor 依赖较多,首次编译需要下载并编译所有 crate。后续增量编译会快很多。 - diff --git a/os/axvisor/doc/shell.md b/os/axvisor/doc/shell.md deleted file mode 100644 index e0c4188fd..000000000 --- a/os/axvisor/doc/shell.md +++ /dev/null @@ -1,1104 +0,0 @@ -# AxVisor Shell 模块详细介绍 - -## 概述 - -AxVisor Shell 模块是 AxVisor 虚拟化管理器中的一个重要组件,为用户提供了一个功能丰富的交互式命令行界面。该模块基于 Rust 语言实现,具有完整的命令解析、历史记录、终端控制和虚拟机管理功能。 - -``` -┌─────────────────────────────────────────────┐ -│ Shell Interface Layer │ -│ ┌─────────────┐ ┌─────────────┐ │ -│ │ Interactive │ │ Command CLI │ │ -│ │ Shell │ │ Parser │ │ -│ └─────────────┘ └─────────────┘ │ -├─────────────────────────────────────────────┤ -│ VM Management Facade │ -│ ┌─────────────┐ ┌──────────────────┐ │ -│ │ Controller │ │ Query & Monitor │ │ -│ └─────────────┘ └──────────────────┘ │ -├─────────────────────────────────────────────┤ -│ Existing VMM Components │ -│ VMList │ VCpu │ IVC │ Timer │ Config │ │ -└─────────────────────────────────────────────┘ -``` - -## 模块架构 - -### 目录结构 -``` -src/shell/ -├── mod.rs # 主模块,实现交互式shell界面 -└── command/ - ├── mod.rs # 命令框架和解析器 - ├── base.rs # 基础Unix命令实现 - ├── vm.rs # 虚拟机管理命令 - └── history.rs # 命令历史记录管理 -``` - -## 核心组件 - -### 1. 交互式Shell界面 ([shell/mod.rs](/src/shell/mod.rs)) - -#### 主要功能 -- **实时字符输入处理**: 支持逐字符读取和处理用户输入 -- **光标控制**: 支持左右箭头键移动光标位置 -- **行编辑功能**: 支持删除、插入字符等基本编辑操作 -- **历史记录导航**: 通过上下箭头键浏览命令历史 -- **转义序列处理**: 支持部分ANSI转义序列和特殊键处理 - -#### 关键特性 -```rust -const MAX_LINE_LEN: usize = 256; // 最大命令行长度 - -enum InputState { - Normal, // 正常输入状态 - Escape, // ESC键按下状态 - EscapeSeq, // 转义序列处理状态 -} -``` - -#### 支持的按键操作 -- **回车键 (CR/LF)**: 执行当前命令 -- **退格键 (BS/DEL)**: 删除光标前的字符 -- **ESC序列**: 处理箭头键和功能键 -- **上/下箭头**: 浏览命令历史 -- **左/右箭头**: 移动光标位置 - -### 2. 命令框架和解析器 ([command/mod.rs](/src/shell/command/mod.rs)) - -#### 命令树结构 -采用基于树状结构的命令系统,支持主命令和子命令的层次化组织: - -```rust -#[derive(Debug, Clone)] -pub struct CommandNode { - handler: Option, // 命令处理函数 - subcommands: BTreeMap, // 子命令映射 - description: &'static str, // 命令描述 - usage: Option<&'static str>, // 使用说明 - log_level: log::LevelFilter, // 日志级别 - options: Vec, // 命令选项 - flags: Vec, // 命令标志 -} -``` - -#### 命令解析功能 -- **智能分词**: 支持引号包围的参数和转义字符 -- **选项解析**: 支持短选项(-x)和长选项(--option) -- **参数验证**: 自动验证必需选项和参数格式 -- **错误处理**: 详细的错误信息和使用提示 -- **灵活格式**: 支持 `--option=value` 和 `--option value` 两种格式 - -#### 分词示例 - -```rust -// src/shell/command/mod.rs:186-215 -fn tokenize(input: &str) -> Vec { - // 支持引号包围的参数 - // 例: echo "hello world" -> ["echo", "hello world"] - - // 支持转义字符 - // 例: echo \"quoted\" -> ["echo", "\"quoted\""] - - // 自动处理空白符分隔 -} -``` - -#### 解析错误类型 -```rust -pub enum ParseError { - UnknownCommand(String), // 未知命令 - UnknownOption(String), // 未知选项 - MissingValue(String), // 缺少参数值 - MissingRequiredOption(String), // 缺少必需选项 - NoHandler(String), // 没有处理函数 -} -``` - -### 3. 基础Unix命令 ([command/base.rs](/src/shell/command/base.rs)) - -实现了部分Unix风格命令,包括: - -#### 文件系统操作命令 -- **ls**: 列出目录内容,支持 `-l`(详细信息) 和 `-a`(显示隐藏文件) 选项 -- **cat**: 显示文件内容,支持多文件连接输出 -- **mkdir**: 创建目录,支持 `-p`(创建父目录) 选项 -- **rm**: 删除文件和目录,支持 `-r`(递归)、`-f`(强制)、`-d`(删除空目录) 选项 -- **cp**: 复制文件和目录,支持 `-r`(递归复制) 选项 -- **mv**: 移动/重命名文件和目录 -- **touch**: 创建空文件 - -#### 系统信息命令 -- **pwd**: 显示当前工作目录 -- **cd**: 切换目录 -- **uname**: 显示系统信息,支持 `-a`(全部信息)、`-s`(内核名)、`-m`(架构) 选项 -- **echo**: 输出文本,支持 `-n`(不换行) 选项和文件重定向 - -#### 系统控制命令 -- **exit**: 退出shell,支持指定退出码 -- **log**: 控制日志级别 (off/error/warn/info/debug/trace) **有计划实现** - -#### 文件权限显示 -实现了完整的Unix风格文件权限显示: -```rust -fn file_type_to_char(ty: FileType) -> char { - match ty { - is_dir() => 'd', - is_file() => '-', - is_symlink() => 'l', - is_char_device() => 'c', - is_block_device() => 'b', - is_socket() => 's', - is_fifo() => 'p', - _ => '?' - } -} -``` - -### 4. 虚拟机管理命令 ([command/vm.rs](/src/shell/command/vm.rs)) - -提供完整的虚拟机生命周期管理功能: - -#### 主要子命令 -- **vm create**: 从配置文件创建虚拟机,支持批量创建多个VM -- **vm start**: 启动虚拟机 - - 不带参数:启动所有虚拟机 - - 指定VM ID:启动特定虚拟机 - - 支持 `--detach` 后台模式运行 - - 支持 `--console` 连接到控制台(计划实现) -- **vm stop**: 停止虚拟机 - - 必须指定VM ID - - 支持 `--force` 强制停止 - - 支持 `--graceful` 优雅关闭 -- **vm suspend**: 暂停(挂起)运行中的虚拟机 (功能不完善) - - 必须指定VM ID - - 所有VCpu将在下次VMExit时进入等待队列 - - VM状态转换为Suspended -- **vm resume**: 恢复已暂停的虚拟机 (功能不完善) - - 必须指定VM ID - - 唤醒所有VCpu任务,恢复执行 - - VM状态从Suspended转换回Running -- **vm restart**: 重启虚拟机,必须指定VM ID (功能不完善) - - 支持 `--force` 强制重启 - - 自动等待VM完全停止后再启动 -- **vm delete**: 删除虚拟机 - - 必须指定VM ID - - 需要 `--force` 确认删除 - - 支持 `--keep-data` 保留数据选项 -- **vm list**: 列出虚拟机 - - 显示所有已创建的虚拟机 - - `--format json` 支持JSON格式输出 - - 表格模式显示:ID、名称、状态、VCPU列表、内存、VCPU状态汇总 -- **vm show**: 显示虚拟机详细信息 - - 必须指定VM ID - - 默认模式:显示基本信息和摘要 - - `--full` / `-f`: 显示完整详细信息(内存区域、设备、配置等) - - `--config` / `-c`: 显示配置信息(入口点、中断模式、直通设备等) - - `--stats` / `-s`: 显示统计信息(EPT、内存区域、设备数量等) - -#### 功能特性 -``` rust -// 虚拟机状态显示 -let state = if vm.running() { - "🟢 running" -} else if vm.shutting_down() { - "🟡 stopping" -} else { - "🔴 stopped" -}; -``` - -#### 详细信息显示 -- **配置信息** (`--config`): - - BSP/AP入口点地址 - - 中断模式 (InterruptMode) - - 直通设备列表 (PassThrough Devices) - - 设备名称、GPA范围、HPA范围 - - 模拟设备列表 (Emulated Devices) -- **资源统计** (`--stats`): - - EPT根页表地址 - - 内存区域详细信息 (GPA范围、大小) - - VCPU数量和设备数量 -- **运行状态**: - - VCPU状态分布 (Free/Running/Blocked/Invalid/Created/Ready) - - CPU亲和性设置 (Physical CPU affinity mask) - - 虚拟机整体状态 (运行中/停止中/已停止) - -#### 支持的选项和标志 -- `--all` / `-a`: (vm list) 显示所有虚拟机(默认已包含所有VM) -- `--format json`: (vm list) JSON格式输出 -- `--full` / `-f`: (vm show) 显示完整详细信息 -- `--config` / `-c`: (vm show) 显示配置信息 -- `--stats` / `-s`: (vm show) 显示统计信息 -- `--force` / `-f`: (vm stop/delete/restart) 强制操作(无需确认) -- `--graceful` / `-g`: (vm stop) 优雅关闭 -- `--console` / `-c`: (vm start) 连接到控制台(计划实现) -- `--watch` / `-w`: (vm status) 实时监控(已移除,功能未实现) -- `--keep-data`: (vm delete) 保留VM数据(功能未实现) - -#### 输出格式示例 - -**Table格式** (默认): -``` -VM ID NAME STATUS VCPU MEMORY VCPU STATE ------- --------------- ------------ --------------- ---------- -------------------- -0 linux-vm Running 0,1 512MB Run:2 -1 test-vm Stopped 0 256MB Free:1 -``` - -**简化表格** (vm list 输出): -``` -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 linux-vm Running 2 512MB -1 test-vm Stopped 1 256MB -``` - -**JSON格式** (`--format json`): -``` json -{ - "vms": [ - { - "id": 0, - "name": "linux-vm", - "state": "running", - "vcpu": 2, - "memory": "512MB", - "interrupt_mode": "Emulated" - } - ] -} -``` - -### 5. 命令历史管理 ([command/history.rs](/src/shell/command/history.rs)) - -#### 核心功能 -```rust -pub struct CommandHistory { - history: Vec, // 历史命令列表 - current_index: usize, // 当前索引位置 - max_size: usize, // 最大历史记录数 -} -``` - -#### 关键特性 -- **去重处理**: 避免连续重复命令 -- **循环缓冲**: 超出最大容量时自动删除最旧记录 -- **导航功能**: 支持前进/后退浏览 -- **空命令过滤**: 自动忽略空白命令 - -#### 终端控制 -```rust -pub fn clear_line_and_redraw( - stdout: &mut dyn Write, - prompt: &str, - content: &str, - cursor_pos: usize, -) { - write!(stdout, "\r"); // 回到行首 - write!(stdout, "\x1b[2K"); // 清除整行 - write!(stdout, "{}{}", prompt, content); // 重绘内容 - // 调整光标位置 - if cursor_pos < content.len() { - write!(stdout, "\x1b[{}D", content.len() - cursor_pos); - } -} -``` - -## 内置命令 - -### 系统级内置命令 -- **help**: 显示可用命令列表 - - 列出所有顶级命令及其子命令 - - 包含内置命令和系统命令 -- **help ``**: 显示特定命令的详细帮助 - - 显示命令描述 - - 显示用法 (Usage) - - 列出所有选项 (Options) - - 列出所有标志 (Flags) - - 列出所有子命令 (Subcommands) -- **clear**: 清屏 (发送ANSI清屏序列 `\x1b[2J\x1b[H`) -- **exit/quit**: 退出shell - -### VM 管理命令列表 - -执行 `help vm` 可以看到完整的 VM 命令列表: - -``` -VM - virtual machine management - -Most commonly used vm commands: - create Create a new virtual machine - start Start a virtual machine - stop Stop a virtual machine - suspend Suspend (pause) a running virtual machine - resume Resume a suspended virtual machine - restart Restart a virtual machine - delete Delete a virtual machine - -Information commands: - list Show table of all VMs - show Show VM details (requires VM_ID) - - Default: basic information - - --full: complete detailed information - - --config: show configuration - - --stats: show statistics - -Use 'vm --help' for more information on a specific command. -``` - -### 错误处理 -Shell会对命令解析和执行错误提供友好的提示信息: -```bash -# 未知命令 -$ unknown_cmd -Error: Unknown command 'unknown_cmd' -Type 'help' to see available commands - -# 未知选项 -$ ls --invalid -Error: Unknown option '--invalid' - -# 缺少参数值 -$ vm create -Error: No VM configuration file specified -Usage: vm create [CONFIG_FILE] - -# 缺少必需选项 -$ vm stop -Error: No VM specified -Usage: vm stop [OPTIONS] -``` - -## VM 生命周期和状态管理 - -### VM 状态机 - -AxVisor 的 VM 状态遵循严格的状态机模型: - -``` - ┌──────────┐ - │ Loading │ (VM 正在创建/加载) - └────┬─────┘ - │ create complete - ▼ - ┌──────────┐ - ┌─────▶│ Loaded │ (VM 已加载,未启动) - │ └────┬─────┘ - │ │ start - │ ▼ - │ ┌──────────┐ - │ ┌───┤ Running │ (VM 正在运行) - │ │ └────┬─────┘ - │ │ │ - │ │ ├─── suspend ────▶ ┌───────────┐ - │ │ │ │ Suspended │ (VM 已暂停) - │ │ │ └─────┬─────┘ - │ │ │ │ resume - │ │ │ ◀──────────────────────┘ - │ │ │ - │ │ │ shutdown/stop - │ │ ▼ - │ │ ┌──────────┐ - │ │ │ Stopping │ (VM 正在关闭) - │ │ └────┬─────┘ - │ │ │ all vcpus exited - │ │ ▼ - │ │ ┌──────────┐ - │ └──▶│ Stopped │ (VM 已停止) - │ └────┬─────┘ - │ │ delete - │ ▼ - │ [Resources Freed] - │ │ - └───────────┘ restart -``` - -### VM 状态定义 - -```rust -pub enum VMStatus { - Loading, // VM 正在创建/加载 - Loaded, // VM 已加载但未启动 - Running, // VM 正在运行 - Suspended, // VM 已暂停(可恢复) - Stopping, // VM 正在关闭中 - Stopped, // VM 已完全停止 -} -``` - -#### 状态转换规则 - -| 当前状态 | 可执行操作 | 目标状态 | 说明 | -|---------|-----------|---------|------| -| Loading | - | Loaded | 创建完成后自动转换 | -| Loaded | `vm start` | Running | 启动 VCpu 任务开始执行 | -| Loaded | `vm delete` | Stopped | 直接删除未启动的 VM | -| Running | `vm stop` | Stopping | 发送关闭信号给所有 VCpu | -| Running | `vm suspend` | Suspended | 暂停所有 VCpu 执行 | -| Suspended | `vm resume` | Running | 恢复 VCpu 执行 | -| Suspended | `vm stop` | Stopping | 从暂停状态直接关闭 | -| Stopping | - | Stopped | 所有 VCpu 退出后自动转换 | -| Stopped | `vm delete` | [释放资源] | 清理并释放 VM 资源 | -| Stopped | `vm start` | Running | 重新启动已停止的 VM | - -### VCpu 生命周期 - -每个 VM 包含一个或多个 VCpu(虚拟 CPU),它们的生命周期与 VM 状态紧密关联: - -``` -VM Start - │ - ├─▶ 创建 VCpu 任务 (alloc_vcpu_task) - │ │ - │ ├─ 设置 CPU 亲和性 - │ ├─ 初始化 TaskExt (Weak 引用 VM) - │ └─ spawn_task 到调度器 - │ - ├─▶ VCpu 任务运行 (vcpu_run) - │ │ - │ ├─ 等待 VM Running 状态 - │ ├─ mark_vcpu_running() - │ └─ 进入运行循环 - │ │ - │ ├─ vm.run_vcpu() - 执行 Guest 代码 - │ ├─ 处理 VM Exit (hypercall, interrupt, halt...) - │ ├─ 检查 VM 暂停状态 - │ └─ 检查 VM 关闭状态 ──┐ - │ │ - │ ▼ vm.stopping() == true - ├─▶ VCpu 任务退出 │ - │ │◀───────────────────────┘ - │ ├─ mark_vcpu_exiting() - 递减运行计数 - │ ├─ 最后一个 VCpu 设置 VM 为 Stopped - │ └─ 任务函数返回,进入 Exited 状态 - │ - └─▶ VCpu 清理 (cleanup_vm_vcpus) - │ - ├─ 遍历所有 VCpu 任务 - ├─ 调用 task.join() 等待退出 - ├─ 释放 VM 的 Arc 引用 - └─ 清理等待队列资源 -``` - -#### VCpu 任务特性 - -1. **Weak 引用**:VCpu 任务通过 `TaskExt` 持有 VM 的 `Weak` 引用,避免循环引用 -2. **CPU 亲和性**:可配置 VCpu 绑定到特定物理 CPU -3. **协作式退出**:VCpu 检测到 `vm.stopping()` 后主动退出 -4. **引用计数管理**:退出前释放所有对 VM 的引用 - -#### VCpu 任务生命周期扩展 - -``` -VM Running - │ - ├─▶ VCpu 任务运行循环 - │ │ - │ ├─ vm.run_vcpu() - 执行 Guest 代码 - │ ├─ 处理 VM Exit - │ ├─ 检查 VM 状态 - │ │ │ - │ │ ├─ vm.stopping() == true ──▶ 退出循环 - │ │ │ - │ │ └─ vm.vm_status() == Suspended ──▶ 进入等待队列 - │ │ │ - │ │ │ wait for notify - │ │ │ - │ │ ▼ - │ │ 被唤醒 (resume) - │ │ │ - │ │ ◀────────────────────────────────────┘ - │ │ - │ └─ 继续执行 -``` - -### VM 删除流程详解 - -`vm delete` 命令执行完整的资源清理流程,确保没有资源泄漏: - -#### 删除流程步骤 - -``` -1. 状态检查和关闭信号 - ├─ 检查 VM 当前状态 - ├─ 如果 Running/Suspended/Stopping - │ ├─ 设置状态为 Stopping - │ └─ 调用 vm.shutdown() 通知 Guest - └─ 如果 Loaded - └─ 直接设置为 Stopped - -2. 从全局列表移除 - ├─ 调用 vm_list::remove_vm(vm_id) - ├─ 获得 VM 的 Arc 引用 - └─ 打印当前 Arc 引用计数 (调试信息) - -3. VCpu 任务清理 ⭐ (核心步骤) - ├─ 调用 cleanup_vm_vcpus(vm_id) - │ ├─ 从全局队列移除 VM 的 VCpu 列表 - │ ├─ 遍历所有 VCpu 任务 - │ │ ├─ task.join() - 阻塞等待任务退出 - │ │ └─ 释放 VCpu 持有的 VM Arc 引用 - │ └─ 清理等待队列资源 - └─ 打印清理后的 Arc 引用计数 - -4. 验证引用计数 - ├─ 期望:Arc count == 1 (仅剩当前函数持有) - ├─ 实际:检查并打印 Arc::strong_count(&vm) - └─ 如果 count > 1:警告可能的引用泄漏 - -5. 资源释放 - ├─ 函数返回时 vm (Arc) 被 drop - ├─ 如果 count == 1,触发 AxVM::drop() - │ ├─ 释放 EPT 页表 - │ ├─ 释放内存区域 - │ └─ 释放设备资源 - └─ VM 对象完全销毁 -``` - -#### 关键实现代码片段 - -```rust -// src/vmm/vcpus.rs:241-260 -pub(crate) fn cleanup_vm_vcpus(vm_id: usize) { - if let Some(vm_vcpus) = VM_VCPU_TASK_WAIT_QUEUE.remove(&vm_id) { - let task_count = vm_vcpus.vcpu_task_list.len(); - - info!("VM[{}] Joining {} VCpu tasks...", vm_id, task_count); - - // ⭐ 关键:真正 join 所有 VCpu 任务 - for (idx, task) in vm_vcpus.vcpu_task_list.iter().enumerate() { - debug!("VM[{}] Joining VCpu task[{}]: {}", vm_id, idx, task.id_name()); - if let Some(exit_code) = task.join() { - debug!("VM[{}] VCpu task[{}] exited with code: {}", vm_id, idx, exit_code); - } - } - - info!("VM[{}] VCpu resources cleaned up, {} VCpu tasks joined successfully", - vm_id, task_count); - } -} -``` - -#### 删除示例输出 - -```bash -$ vm delete 2 -Deleting stopped VM[2]... - [Debug] VM Arc strong_count: 2 -✓ VM[2] removed from VM list - Waiting for vCPU threads to exit... - [Debug] VM Arc count before cleanup: 1 - Cleaning up VCpu resources... -[ 67.812092 0:2 axvisor::vmm::vcpus:243] VM[2] Joining 1 VCpu tasks... -[ 67.819730 0:2 axvisor::vmm::vcpus:253] VM[2] VCpu resources cleaned up, 1 VCpu tasks joined successfully - [Debug] VM Arc count after final wait: 1 -✓ VM[2] deleted completely - [Debug] VM Arc strong_count: 1 - ✓ Perfect! VM will be freed immediately when function returns - VM[2] will be freed now -[ 67.848026 0:2 axvm::vm:884] Dropping VM[2] -[ 67.853407 0:2 axvm::vm:775] Cleaning up VM[2] resources... -[ 67.860698 0:2 axvm::vm:878] VM[2] resources cleanup completed -[ 67.867209 0:2 axvm::vm:889] VM[2] dropped -✓ VM[2] deletion completed -``` - -### 命令提示符 -```rust -pub fn print_prompt() { - #[cfg(feature = "fs")] - print!("axvisor:{}$ ", std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - print!("axvisor:$ "); - std::io::stdout().flush().unwrap(); -} -``` - -## 扩展性 - -### 添加新命令 - -1. 在对应的模块中实现命令处理函数 -2. 定义命令节点和选项/标志 -3. 在 `build_command_tree()` 中注册命令 - -### 命令定义示例 - -```rust -tree.insert( - "mycommand".to_string(), - CommandNode::new("My custom command") - .with_handler(my_command_handler) - .with_usage("mycommand [OPTIONS] ") - .with_option( - OptionDef::new("config", "Config file path") - .with_short('c') - .with_long("config") - .required() - ) - .with_flag( - FlagDef::new("verbose", "Verbose output") - .with_short('v') - .with_long("verbose") - ), -); -``` - -# 使用说明 - -## Shell功能特性 - -AxVisor Shell模块**默认启用**,但不同功能对features有不同要求: - -### 功能分层 - -#### 🟢 基础功能(无需额外feature) -- 交互式命令行界面 -- 命令历史记录(上下箭头导航) -- 光标移动和行编辑 -- 内置命令:`help`, `clear`, `exit` -- 系统命令:`uname`, `log` -- VM管理命令:`vm list`, `vm show`, `vm status`, `vm stop` 等 - -#### 🟡 文件系统功能(需要 `fs` feature) -- 文件操作命令:`ls`, `cat`, `mkdir`, `rm`, `cp`, `mv`, `touch`, `cd`, `pwd`, `echo` -- `vm create` - 从配置文件创建VM -- `vm /` - 从文件系统加载VM镜像启动 - -## vmconfigs 配置说明 - -`vmconfigs` 参数决定了 AxVisor 启动时是否自动创建和启动虚拟机: - -### 📌 配置行为 - -| vmconfigs 配置 | 启动行为 | 使用场景 | -|---------------|---------|---------| -| **有值**(指定配置文件)| ✅ 自动创建并启动VM | 预加载VM,启动后VM已运行 | -| **无值**(不指定)| ❌ 不创建VM,进入空Shell | 手动管理VM,通过Shell创建 | - -### 配置示例 - -#### 场景1:自动启动VM -```bash -# VM会在启动时自动创建并运行 -./axvisor.sh run \ - --plat aarch64-generic \ - --vmconfigs configs/vms/nimbos-aarch64-qemu-smp1.toml -``` - -**启动后**: -``` -Welcome to AxVisor Shell! -... -VMM starting, booting VMs... -VM[0] boot success - -axvisor:/$ vm list -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 nimbos-vm 🟢 running 1 512MB -``` - -#### 场景2:不自动启动VM(空Shell) -```bash -# 不指定 vmconfigs 参数 -./axvisor.sh run --plat aarch64-generic --features fs,ept-level-4 -``` - -**启动后**(需要手动创建VM): -``` -Welcome to AxVisor Shell! -... - -axvisor:/$ vm list -No virtual machines found. - -axvisor:/$ vm create /path/to/vm.toml -✓ Successfully created VM from config: /path/to/vm.toml - -axvisor:/$ vm start 0 -✓ VM[0] started successfully -``` - -### 配置方式 - -#### 命令行指定 -```bash -./axvisor.sh run --vmconfigs configs/vms/vm1.toml,configs/vms/vm2.toml -``` - -#### 配置文件指定 -在 `.hvconfig.toml` 中: -```toml -vmconfigs = [ - "configs/vms/nimbos-aarch64-qemu-smp1.toml", - "configs/vms/linux-aarch64-qemu.toml" -] -``` - -### 💡 使用建议 - -| 使用场景 | 推荐配置 | -|---------|---------| -| **生产环境** - 固定的VM配置 | 指定 `vmconfigs`,自动启动 | -| **开发调试** - 频繁修改VM配置 | 不指定 `vmconfigs`,Shell中手动创建 | -| **演示测试** - 需要快速启动 | 指定 `vmconfigs`,自动启动 | -| **交互式管理** - 动态创建多个VM | 不指定或只指定部分,其余手动创建 | - -## 启用方式 - -### 方式一:自动启动VM(指定 vmconfigs) - -指定 `--vmconfigs` 参数,AxVisor 会在启动时自动创建并启动虚拟机: - -```bash -# VM会自动启动 -./axvisor.sh run \ - --plat aarch64-generic \ - --vmconfigs configs/vms/nimbos-aarch64-qemu-smp1.toml -``` - -**启动后状态**: -- ✅ VM已创建并运行 -- ✅ Shell可直接管理VM -- ✅ 可执行 `vm list`, `vm status` 等命令 - -**可用功能**: -- VM状态查询和管理 -- 系统信息查看 -- 命令历史和行编辑 -- 日志级别控制 - -**不可用功能**(无 `fs` feature时): -- 文件操作命令 -- 从文件系统动态创建新VM - -### 方式二:空Shell模式(不指定 vmconfigs) - -不指定 `--vmconfigs`,AxVisor 启动后不会创建VM,提供纯净的Shell环境: - -```bash -# 启动时不创建VM,需要启用fs以便手动创建 -./axvisor.sh run --plat aarch64-generic --features fs,ept-level-4 -``` - -**启动后状态**: -- ❌ 无VM运行 -- ✅ Shell就绪,等待用户操作 -- ✅ 可通过 `vm create` 手动创建VM - -**使用场景**: -- 需要在Shell中动态创建多个VM -- 测试不同的VM配置 -- 交互式VM管理 - -### 方式三:完整Shell功能(带文件系统 + vmconfigs) - -结合文件系统和 vmconfigs,既可以自动启动预定义的VM,又可以使用文件操作和动态创建VM: - -#### 步骤1:准备磁盘镜像 - -```bash -# 创建磁盘镜像(以FAT32为例) -dd if=/dev/zero of=disk.img bs=1M count=64 -mkfs.vfat disk.img - -# 挂载并放入VM配置文件 -mkdir -p mnt -sudo mount disk.img mnt -sudo cp configs/vms/*.toml mnt/ -sudo umount mnt -``` - -#### 步骤2:运行AxVisor(完整功能) - -```bash -# 同时启用文件系统和自动启动VM -./axvisor.sh run \ - --plat aarch64-generic \ - --vmconfigs configs/vms/nimbos-aarch64-qemu-smp1.toml \ - --features fs,ept-level-4 \ - --arceos-args "BUS=mmio,BLK=y,DISK_IMG=disk.img,MEM=8g,LOG=info" -``` - -**启动后状态**: -- ✅ VM已自动创建并运行 -- ✅ 文件系统已挂载 -- ✅ 可执行所有Shell命令 -- ✅ 可从文件系统创建更多VM - -**完整功能**: -``` bash -axvisor:/$ vm list # 查看已启动的VM -axvisor:/$ ls -la # 浏览文件系统 -axvisor:/$ cat /vm2.toml # 查看其他配置文件 -axvisor:/$ vm create /vm2.toml # 创建更多VM -``` - -#### 文件系统类型选择 - -ArceOS 默认使用 **FAT32** 文件系统。如需使用其他文件系统,可通过 ArceOS 的构建参数指定: - -```bash -# 使用EXT4文件系统(需要创建ext4格式的磁盘镜像) -./axvisor.sh run \ - --plat aarch64-generic \ - --vmconfigs configs/vms/nimbos-aarch64-qemu-smp1.toml \ - --features fs,ept-level-4 \ - --arceos-features ext4fs \ - --arceos-args "BUS=mmio,BLK=y,DISK_IMG=disk-ext4.img,MEM=8g" -``` - -## 实际使用示例 - -### 示例1:NimbOS客户机(自动启动) - -使用 `--vmconfigs` 让 NimbOS 在启动时自动运行: - -```bash -# 1. 准备NimbOS镜像 -./scripts/nimbos.sh --arch aarch64 - -# 2. 启动AxVisor(VM会自动启动) -./axvisor.sh run \ - --plat aarch64-generic \ - --features fs,ept-level-4 \ - --vmconfigs configs/vms/nimbos-aarch64-qemu-smp1.toml \ - --arceos-args "BUS=mmio,BLK=y,DISK_IMG=tmp/nimbos-aarch64.img,LOG=info" - -# 3. 在Shell中操作(VM已运行) -# 查看VM状态 -axvisor:/$ vm list -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 nimbos-vm 🟢 running 1 512MB - -axvisor:/$ vm status 0 # 查看详细状态 -axvisor:/$ log debug # 调整日志级别 -``` - -### 示例2:交互式创建VM(手动管理) - -不使用 `--vmconfigs`,在Shell中手动创建和管理VM: - -```bash -# 1. 准备镜像和配置文件 -./scripts/nimbos.sh --arch aarch64 - -# 2. 启动AxVisor(不指定vmconfigs,不自动启动VM) -./axvisor.sh run \ - --plat aarch64-generic \ - --features fs,ept-level-4 \ - --arceos-args "BUS=mmio,BLK=y,DISK_IMG=tmp/nimbos-aarch64.img,LOG=info" - -# 3. 在Shell中手动创建和启动VM -axvisor:/$ vm list -No virtual machines found. - -axvisor:/$ ls / # 浏览文件系统 -nimbos-aarch64-qemu-smp1.toml -... - -axvisor:/$ vm create /nimbos-aarch64-qemu-smp1.toml -✓ Successfully created VM from config - -axvisor:/$ vm list -a -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 nimbos-vm 🔴 stopped 1 512MB - -axvisor:/$ vm start 0 -✓ VM[0] started successfully - -axvisor:/$ vm list -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 nimbos-vm 🟢 running 1 512MB -``` - -### 示例3:混合模式(部分自动,部分手动) - -自动启动一个VM,再手动创建更多: - -```bash -# 启动AxVisor,自动启动第一个VM -./axvisor.sh run \ - --plat aarch64-generic \ - --features fs,ept-level-4 \ - --vmconfigs configs/vms/vm1.toml \ - --arceos-args "BUS=mmio,BLK=y,DISK_IMG=disk.img,LOG=info" - -# Shell中查看和创建更多VM -axvisor:/$ vm list -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 vm1 🟢 running 2 1024MB - -axvisor:/$ vm create /configs/vm2.toml -✓ Successfully created VM from config - -axvisor:/$ vm start 1 -✓ VM[1] started successfully - -axvisor:/$ vm list -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 vm1 🟢 running 2 1024MB -1 vm2 🟢 running 1 512MB -``` - -### 代码层面说明 - -Shell模块在代码中的启用方式: - -```rust -// src/main.rs -fn main() { - // ... 初始化代码 ... - - // Shell总是被调用,无条件编译 - shell::console_init(); -} -``` - -```rust -// src/shell/command/base.rs -// 文件系统相关命令通过条件编译控制 -#[cfg(feature = "fs")] -fn do_ls(cmd: &ParsedCommand) { /* ... */ } - -#[cfg(feature = "fs")] -fn do_cat(cmd: &ParsedCommand) { /* ... */ } - -// 这些命令在构建命令树时也受条件编译控制 -pub fn build_base_cmd(tree: &mut BTreeMap) { - #[cfg(feature = "fs")] - tree.insert("ls".to_string(), /* ... */); - - #[cfg(feature = "fs")] - tree.insert("cat".to_string(), /* ... */); - - // 非文件系统命令始终可用 - tree.insert("uname".to_string(), /* ... */); - tree.insert("log".to_string(), /* ... */); -} -``` - -这种设计使得: -1. **Shell界面始终可用** - 提供基本的交互和VM管理能力 -2. **文件系统功能可选** - 仅在需要时启用,减少依赖 -3. **灵活的部署方式** - 支持从内存或文件系统加载VM - -## 快速开始 - -启动AxVisor后会自动进入Shell界面: -``` -Welcome to AxVisor Shell! -Type 'help' to see available commands -Use UP/DOWN arrows to navigate command history - -axvisor:/$ -``` - -### 基本操作 -- `help` - 查看所有命令 -- `help ` - 查看特定命令帮助 -- `clear` - 清屏 -- `exit` - 退出 - -### 键盘快捷键 -- **上/下箭头**: 浏览命令历史 -- **左/右箭头**: 移动光标 -- **退格键**: 删除字符 - -## 常用命令 - -### 文件操作 -```bash -ls -la # 列出文件(详细信息+隐藏文件) -cat file.txt # 查看文件内容 -mkdir -p dir/subdir # 创建目录 -cp -r source dest # 复制文件/目录 -mv old new # 移动/重命名 -rm -rf path # 删除文件/目录 -touch file.txt # 创建空文件 -``` - -### 虚拟机管理 -```bash -vm list # 列出所有虚拟机 -vm list --format json # JSON格式输出 -vm create config.toml # 创建虚拟机 -vm create vm1.toml vm2.toml # 批量创建虚拟机 -vm start # 启动所有虚拟机 -vm start 1 # 启动VM(ID=1) -vm start -d 1 # 后台启动VM -vm stop -f 1 # 强制停止VM -vm suspend 1 # 暂停VM(ID=1) -vm resume 1 # 恢复暂停的VM -vm restart 1 # 重启VM -vm restart -f 1 # 强制重启VM -vm delete -f 1 # 删除VM(需要确认) -vm status # 显示所有VM状态概览(已移除) -vm status 1 # 查看特定VM状态(已移除) -vm show 1 # 查看VM基本信息 -vm show -f 1 # 查看VM完整详细信息 -vm show -c 1 # 查看VM配置 -vm show -s 1 # 查看VM统计信息 -vm show -c -s 1 # 查看VM配置和统计信息 -``` - -### 系统信息 -```bash -pwd # 当前目录 -uname -a # 系统信息 -``` - -## 典型工作流 - -### 单虚拟机场景 -```bash -# 1. 检查环境 -ls -la -pwd - -# 2. 创建虚拟机 -vm create linux.toml - -# 3. 启动虚拟机 -vm start 1 - -# 4. 监控状态 -vm status 1 -vm show -c -s 1 # 查看详细配置和统计 - -# 5. 停止虚拟机 -vm stop 1 -``` - -### 多虚拟机场景 -```bash -# 1. 批量创建虚拟机 -vm create vm1.toml vm2.toml vm3.toml - -# 2. 查看所有虚拟机 -vm list -a - -# 3. 启动所有虚拟机 -vm start - -# 4. 查看整体状态 -vm status # 显示所有VM的状态概览 - -# 5. 停止特定虚拟机 -vm stop 2 - -# 6. 重启虚拟机 -vm restart 1 - -# 7. 删除虚拟机 -vm delete -f 3 -``` - -更多详细信息请使用 `help ` 查看具体命令的使用方法。 diff --git a/os/axvisor/doc/task.py-usage.md b/os/axvisor/doc/task.py-usage.md deleted file mode 100644 index 5aab2b5fa..000000000 --- a/os/axvisor/doc/task.py-usage.md +++ /dev/null @@ -1,271 +0,0 @@ -# task.py 使用说明 - -## 概述 - -`task.py` 是 Axvisor 项目的主要命令行工具,提供了项目的构建、运行和设置功能。它是一个统一的入口点,简化了开发和部署流程。 - -## 基本用法 - -```bash -# 激活虚拟环境 -source ./activate.sh - -./task.py [options] -``` - -## 可用命令 - -### 1. build - 构建项目 - -./scripts/task.py [options] -构建 Axvisor 项目。 - -```bash -./task.py build [options] -``` - -**功能**: - -- 自动设置 ArceOS 依赖(如果尚未设置) -./scripts/task.py build [options] -- 执行 make 构建 - -**示例**: - -```bash -# 使用 .hvconfig.toml 中的配置构建 -./scripts/task.py build - -# 指定平台构建 -./scripts/task.py build --plat aarch64-generic - -# 添加特性 -./scripts/task.py build --features "feature1,feature2" - -# 添加 ArceOS 特性 -./scripts/task.py build --arceos-features "page-alloc-64g,smp" - -# 添加 ArceOS Makefile 参数 -./scripts/task.py build --arceos-args "NET=y,BLK=y,MEM=8g,LOG=debug" - -# 指定 VM 配置文件 -./scripts/task.py build --vmconfigs "config1.toml,config2.toml" -``` - -### 3. run - 运行项目 - -构建并运行 Axvisor 项目。 - -```bash -./scripts/task.py run [options] -``` - -**功能**: - -- 首先执行构建步骤 -- 如果构建成功,则运行项目 -./scripts/task.py run - -**示例**: -./scripts/task.py run --plat aarch64-generic --arceos-args "MEM=4g,BUS=mmio,BLK=y,LOG=debug" --features "fs" - -```bash -# 使用 .hvconfig.toml 中的配置运行 -./task.py run - -# 使用特定配置运行,执行后会根据参数生成 .hvconfig.toml -./task.py run --plat aarch64-generic --arceos-args "MEM=4g,BUS=mmio,BLK=y,LOG=debug" --features "fs" -``` - -./scripts/task.py run --arceos-args "QEMU_ARGS=\"-smp 4 -m 2G -netdev user,id=net0\"" - -### 通用参数 - -以下参数适用于 `build` 和 `run` 命令: - -| 参数 | 类型 | 默认值 | 说明 | -|------|------|--------|------| -| `--plat` | string | aarch64-generic | 指定目标平台 | -| `--arch` | string | 自动检测 | 指定目标架构 | -| `--package` | string | 自动检测 | 指定平台包名 | -cp .hvconfig.dev.toml .hvconfig.toml && ./scripts/task.py build -cp .hvconfig.prod.toml .hvconfig.toml && ./scripts/task.py run -| `--features` | string | 无 | Hypervisor 特性(逗号分隔) | -| `--arceos-features` | string | 无 | ArceOS 特性(逗号分隔) | -| `--arceos-args` | string | 无 | ArceOS 参数(逗号分隔) | -| `--vmconfigs` | string | 无 | VM 配置文件路径(逗号分隔) | - -### 参数详解 - -#### --plat (平台) - -指定目标平台,系统会自动从 `platform/{plat}/axconfig.toml` 读取对应的架构和包配置。 - -```bash ---plat aarch64-generic ---plat x86-qemu-q35 -``` - -**架构特定的 QEMU 参数**: - -- `aarch64`: `-machine virtualization=on,gic-version=3` -- `x86_64`: `-enable-kvm -cpu host` -- `riscv64`: `-machine virt -cpu rv64` - -#### --features (Hypervisor 特性) - -指定 Hypervisor 的特性,多个特性用逗号分隔。 - -```bash ---features "fs" -``` - -#### --arceos-features (ArceOS 特性) - -指定 ArceOS 的特性,多个特性用逗号分隔。 - -```bash ---arceos-features "page-alloc-64g" ---arceos-features "smp,net,blk" -``` - -#### --arceos-args (ArceOS 参数) - -指定传递给 ArceOS 的参数,支持键值对和标志,多个参数用逗号分隔。 - -```bash ---arceos-args "NET=y,BLK=y,MEM=8g" ---arceos-args "SMP=4,LOG=debug" -``` - -#### --vmconfigs (VM 配置文件) - -指定 VM 配置文件的路径,多个文件用逗号分隔。 - -```bash ---vmconfigs "vm1.toml" ---vmconfigs "vm1.toml,vm2.toml,vm3.toml" -``` - -## 配置文件 - -`task.py` 支持通过 `.hvconfig.toml` 配置文件设置默认参数,命令行参数会覆盖配置文件中的设置。 - -### 配置文件示例 - -创建 `.hvconfig.toml` 文件: - -```toml -plat = "aarch64-generic" -arceos_args = [ "BUS=mmio", "BLK=y", "MEM=8g", "LOG=debug", "QEMU_ARGS=\"-machine gic-version=3\""] -vmconfigs = [ "tmp/arceos-aarch64.toml",] -``` - -### 配置优先级 - -1. **命令行参数** (最高优先级) -2. **配置文件** (.hvconfig.toml) - -## 高级用法 - -### 1. 自定义 QEMU 参数 - -```bash -# 添加自定义 QEMU 参数,会与架构特定参数合并 -./task.py run --arceos-args "QEMU_ARGS=\"-smp 4 -m 2G -netdev user,id=net0\"" - -# 对于 aarch64,最终的 QEMU_ARGS 会是: -# "-smp 4 -m 2G -netdev user,id=net0 -machine virtualization=on" -``` - -### 4. 批量配置 - -创建多个配置文件用于不同的开发场景: - -```bash -# 开发配置 -cp .hvconfig.toml .hvconfig.dev.toml -# 编辑 .hvconfig.dev.toml 添加调试参数 - -# 生产配置 -cp .hvconfig.toml .hvconfig.prod.toml -# 编辑 .hvconfig.prod.toml 优化性能参数 - -# 使用不同配置 -cp .hvconfig.dev.toml .hvconfig.toml && ./task.py build -cp .hvconfig.prod.toml .hvconfig.toml && ./task.py run -``` - -## 故障排除 - -### 常见问题 - -1. **构建失败** - - ```bash - # 清理并重新设置 - rm -rf .arceos - ./task.py clean - ./task.py build - ``` - -2. **平台配置找不到** - - ```text - 警告:平台配置文件 platform/xxx/axconfig.toml 不存在 - ``` - - - 检查平台名称是否正确 - - 确保对应的平台目录存在 - -3. **TOML 库缺失** - - ```text - 警告:需要安装 toml 库来读取配置文件 - ``` - - ```bash - pip install toml - ``` - -4. **权限问题** - - ```bash - # 确保 task.py 有执行权限 - chmod +x task.py - ``` - -### 调试技巧 - -1. **查看生成的构建命令** - - ```python - from scripts.config import AxvisorConfig - config = AxvisorConfig() - print(config.format_make_command("build")) - ``` - -2. **检查配置合并结果** - - ```python - from scripts.config import AxvisorConfig - config = AxvisorConfig(plat="aarch64-generic") - print(f"Platform: {config.plat}") - print(f"Architecture: {config.arch}") - print(f"Package: {config.package}") - print(f"Make vars: {config.get_make_variables()}") - ``` - -## 开发扩展 - -如果需要添加新的命令或功能: - -1. **添加新命令**: - - 在 `scripts/` 目录下创建新的 Python 模块 - - 在 `task.py` 中添加对应的子解析器 - - 实现 `main(args)` 函数 - -2. **添加新参数**: - - 修改 `scripts/config.py` 中的 `add_common_arguments` 函数 - - 更新 `AxvisorConfig` 类的字段 - - 更新相关的处理逻辑 diff --git a/os/axvisor/rust-toolchain.toml b/os/axvisor/rust-toolchain.toml deleted file mode 100644 index 00f862502..000000000 --- a/os/axvisor/rust-toolchain.toml +++ /dev/null @@ -1,5 +0,0 @@ -[toolchain] -profile = "minimal" -channel = "nightly-2026-02-25" -components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] -targets = ["x86_64-unknown-none", "riscv64gc-unknown-none-elf", "aarch64-unknown-none-softfloat"] diff --git a/os/axvisor/scripts/ci_run_qemu_nimbos.py b/os/axvisor/scripts/ci_run_qemu_nimbos.py deleted file mode 100755 index 0e93e6e50..000000000 --- a/os/axvisor/scripts/ci_run_qemu_nimbos.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2025 The Axvisor Team -# -# Wrapper for CI: runs `cargo xtask qemu` for NimbOS and automatically sends -# "usertests\n" to the guest when the shell prompt appears, so the test can -# complete without interactive input. -# -# Uses a PTY so the child sees a real TTY; with subprocess.PIPE the child -# may treat stdin as non-interactive and not forward input to QEMU. - -import os -import select -import sys -import subprocess - -# Trigger strings (try in order; first match sends usertests) -SEND_AFTER = (b"Rust user shell", b">>") -SEND_LINE = b"usertests\n" -SUCCESS_MARKERS = (b"usertests passed!",) - - -def main(): - try: - sep = sys.argv.index("--") - except ValueError: - print("Usage: ci_run_qemu_nimbos.py -- [args...]", file=sys.stderr) - sys.exit(2) - cmd = sys.argv[sep + 1 :] - if not cmd: - print("No command after --", file=sys.stderr) - sys.exit(2) - - import pty - - master, slave = pty.openpty() - try: - proc = subprocess.Popen( - cmd, - stdin=slave, - stdout=slave, - stderr=slave, - close_fds=True, - ) - finally: - os.close(slave) - - sent = False - saw_success = False - buffer = b"" - try: - while True: - r, _, _ = select.select([master], [], [], 0.1) - if r: - try: - chunk = os.read(master, 4096) - except OSError: - break - if not chunk: - break - sys.stdout.buffer.write(chunk) - sys.stdout.buffer.flush() - buffer = (buffer + chunk)[-1024:] - if not saw_success and any(marker in buffer for marker in SUCCESS_MARKERS): - saw_success = True - if not sent and any(trigger in buffer for trigger in SEND_AFTER): - try: - os.write(master, SEND_LINE) - sent = True - except OSError: - pass - if proc.poll() is not None: - while True: - r, _, _ = select.select([master], [], [], 0.05) - if not r: - break - try: - chunk = os.read(master, 4096) - except OSError: - break - if not chunk: - break - sys.stdout.buffer.write(chunk) - sys.stdout.buffer.flush() - break - finally: - os.close(master) - - if saw_success: - sys.exit(0) - sys.exit(proc.returncode if proc.returncode is not None else 1) - - -if __name__ == "__main__": - main() diff --git a/os/axvisor/scripts/lds/linker.lds.S b/os/axvisor/scripts/lds/linker.lds.S deleted file mode 100644 index 2196ebe0e..000000000 --- a/os/axvisor/scripts/lds/linker.lds.S +++ /dev/null @@ -1,100 +0,0 @@ -OUTPUT_ARCH(%ARCH%) - -BASE_ADDRESS = %KERNEL_BASE%; -SMP = %SMP%; - -ENTRY(_start) -SECTIONS -{ - . = BASE_ADDRESS; - _skernel = .; - - .text : ALIGN(4K) { - _stext = .; - *(.text.boot) - *(.text .text.*) - . = ALIGN(4K); - _etext = .; - } - - .rodata : ALIGN(4K) { - _srodata = .; - *(.rodata .rodata.*) - *(.srodata .srodata.*) - *(.sdata2 .sdata2.*) - . = ALIGN(4K); - _erodata = .; - } - - .data : ALIGN(4K) { - _sdata = .; - *(.data.boot_page_table) - . = ALIGN(4K); - - __sdriver_register = .; - KEEP(*(.driver.register*)) - __edriver_register = .; - - *(.data .data.*) - *(.sdata .sdata.*) - *(.got .got.*) - } - - .tdata : ALIGN(0x10) { - _stdata = .; - *(.tdata .tdata.*) - _etdata = .; - } - - .tbss : ALIGN(0x10) { - _stbss = .; - *(.tbss .tbss.*) - *(.tcommon) - _etbss = .; - } - - . = ALIGN(4K); - _percpu_start = .; - _percpu_end = _percpu_start + SIZEOF(.percpu); - .percpu 0x0 : AT(_percpu_start) { - _percpu_load_start = .; - *(.percpu .percpu.*) - _percpu_load_end = .; - . = _percpu_load_start + ALIGN(64) * SMP; - } - . = _percpu_end; - - . = ALIGN(4K); - _edata = .; - - .bss : ALIGN(4K) { - boot_stack = .; - *(.bss.stack) - . = ALIGN(4K); - boot_stack_top = .; - - _sbss = .; - *(.bss .bss.*) - *(.sbss .sbss.*) - *(COMMON) - . = ALIGN(4K); - _ebss = .; - } - - _ekernel = .; - - /DISCARD/ : { - *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) - } -} - -SECTIONS { - linkme_IRQ : { *(linkme_IRQ) } - linkm2_IRQ : { *(linkm2_IRQ) } - linkme_PAGE_FAULT : { *(linkme_PAGE_FAULT) } - linkm2_PAGE_FAULT : { *(linkm2_PAGE_FAULT) } - linkme_SYSCALL : { *(linkme_SYSCALL) } - linkm2_SYSCALL : { *(linkm2_SYSCALL) } - scope_local : { *(scope_local) } -} -INSERT AFTER .tbss; diff --git a/os/axvisor/scripts/ostool/qemu-aarch64.toml b/os/axvisor/scripts/ostool/qemu-aarch64.toml deleted file mode 100644 index defd03d8d..000000000 --- a/os/axvisor/scripts/ostool/qemu-aarch64.toml +++ /dev/null @@ -1,21 +0,0 @@ -args = [ - "-nographic", - "-cpu", - "cortex-a72", - "-machine", - "virt,virtualization=on,gic-version=3", - "-smp", - "4", - "-device", - "virtio-blk-device,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-append", - "root=/dev/vda rw init=/init", - "-m", - "8g", -] -fail_regex = [] -success_regex = [] -to_bin = true -uefi = false diff --git a/os/axvisor/scripts/ostool/qemu-x86_64.toml b/os/axvisor/scripts/ostool/qemu-x86_64.toml deleted file mode 100644 index 11e3d15ce..000000000 --- a/os/axvisor/scripts/ostool/qemu-x86_64.toml +++ /dev/null @@ -1,21 +0,0 @@ -args = [ - "-nographic", - "-cpu", - "host", - "-machine", - "q35", - "-smp", - "1", - "-accel", - "kvm", - "-device", - "virtio-blk-pci,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-m", - "128M", -] -fail_regex = [] -success_regex = [] -to_bin = false -uefi = false diff --git a/os/axvisor/scripts/quick-start.sh b/os/axvisor/scripts/quick-start.sh deleted file mode 100755 index 6dda5f6b3..000000000 --- a/os/axvisor/scripts/quick-start.sh +++ /dev/null @@ -1,904 +0,0 @@ -#!/bin/bash -# -# AxVisor Environment Setup and Launch Script -# Supported platforms: qemu-aarch64, qemu-x86_64, phytiumpi, roc-rk3568-pc -# Documentation: https://arceos-hypervisor.github.io/axvisorbook/docs/quickstart -# - -set -e # Exit on error - -# Color output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -info() { - echo -e "${GREEN}[INFO]${NC} $1" -} - -warn() { - echo -e "${YELLOW}[WARN]${NC} $1" -} - -error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Execute command and display it -run_cmd() { - echo -e "${BLUE}$@${NC}" - "$@" -} - -# Check if running in AxVisor root directory -check_root_dir() { - if [ ! -f "Cargo.toml" ]; then - error "Cargo.toml not found. Please run this script in the AxVisor root directory." - exit 1 - fi - - if [ ! -d "configs" ]; then - error "configs directory not found. Please run this script in the AxVisor root directory." - exit 1 - fi - - # Check if package name in Cargo.toml is axvisor - if ! grep -q '^\[package\]' Cargo.toml; then - error "Invalid Cargo.toml format. Please run this script in the AxVisor root directory." - exit 1 - fi - - # Extract package name and check - local package_name - package_name=$(awk '/^\[package\]/{flag=1} flag && /^name\s*=/ {gsub(/["'\'']/, "", $3); print $3; exit}' Cargo.toml) - if [ "$package_name" != "axvisor" ]; then - error "Current project is not AxVisor (package name: $package_name). Please run this script in the AxVisor root directory." - exit 1 - fi -} - -# Show usage help -show_help() { - cat << EOF -Usage: $0 [options] - -AxVisor Environment Setup and Launch Script - -Platforms: - qemu-aarch64 QEMU AArch64 (ArceOS/Linux) - qemu-x86_64 QEMU x86_64 (NimbOS) - phytiumpi Phytium Pi Board (ArceOS/Linux) - roc-rk3568-pc ROC-RK3568-PC Board (ArceOS/Linux) - -Commands: - setup Prepare environment (download images, config files, etc.) - run Launch AxVisor (requires setup first) - start One-step setup + launch (recommended) - -Setup Options (for setup/start commands): - --serial Specify serial device - Required for board platforms (phytiumpi, roc-rk3568-pc) - If not specified, setup will prepare config but NOT launch - -Launch Options (for run/start commands): - QEMU AArch64: - -a, --arceos Launch single ArceOS guest (default) - -l, --linux Launch single Linux guest - -m, --multi Launch multiple guests (ArceOS+Linux) - QEMU x86_64: - -n, --nimbos Launch single NimbOS guest (default) - Phytium Pi: - -a, --arceos Launch single ArceOS guest (default) - -l, --linux Launch single Linux guest - -m, --multi Launch multiple guests (ArceOS+Linux) - ROC-RK3568-PC: - -a, --arceos Launch single ArceOS guest (default) - -l, --linux Launch single Linux guest - -m, --multi Launch multiple guests (ArceOS+Linux) - -h, --help Show this help message - -Examples: - # QEMU AArch64 - $0 qemu-aarch64 start --arceos # One-step: prepare + launch ArceOS - $0 qemu-aarch64 start --linux # One-step: prepare + launch Linux - $0 qemu-aarch64 start --multi # One-step: prepare + launch multiple guests - - # QEMU x86_64 - $0 qemu-x86_64 start --nimbos # One-step: prepare + launch NimbOS - $0 qemu-x86_64 start # Same as above (default nimbos) - - # Phytium Pi (requires --serial for start) - $0 phytiumpi setup # Prepare environment only - $0 phytiumpi setup --serial /dev/ttyUSB1 # Prepare with custom serial device - $0 phytiumpi run --arceos # Launch ArceOS (serial must be set in config) - $0 phytiumpi start --serial /dev/ttyUSB0 --arceos # One-step: prepare + launch - $0 phytiumpi start --serial /dev/ttyUSB1 --linux # One-step with custom serial device - - # ROC-RK3568-PC (requires --serial for start) - $0 roc-rk3568-pc setup # Prepare environment only - $0 roc-rk3568-pc setup --serial /dev/ttyACM0 # Prepare with custom serial device - $0 roc-rk3568-pc run --arceos # Launch ArceOS (serial must be set in config) - $0 roc-rk3568-pc start --serial /dev/ttyUSB0 --arceos # One-step: prepare + launch - $0 roc-rk3568-pc start --serial /dev/ttyACM0 --multi # One-step with custom serial device - - # Step-by-step execution - $0 qemu-aarch64 setup # Prepare environment only - $0 qemu-aarch64 run --arceos # Launch only - -EOF -} - -# ============================================================================ -# QEMU AArch64 Architecture Setup -# ============================================================================ - -setup_qemu_aarch64() { - info "=== QEMU AArch64 Preparation ===" - - run_cmd mkdir -p tmp/{configs,images} - - info "Downloading ArceOS image..." - run_cmd cargo xtask image download qemu_aarch64_arceos --output-dir tmp/images - - info "Downloading Linux image..." - run_cmd cargo xtask image download qemu_aarch64_linux --output-dir tmp/images - - info "Preparing board config file..." - run_cmd cp configs/board/qemu-aarch64.toml tmp/configs/ - - info "Preparing guest config files..." - run_cmd cp configs/vms/arceos-aarch64-qemu-smp1.toml tmp/configs/ - run_cmd cp configs/vms/linux-aarch64-qemu-smp1.toml tmp/configs/ - - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/qemu_aarch64_arceos/qemu-aarch64"|g' tmp/configs/arceos-aarch64-qemu-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/arceos-aarch64-qemu-smp1.toml - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/qemu_aarch64_linux/qemu-aarch64"|g' tmp/configs/linux-aarch64-qemu-smp1.toml - run_cmd sed -i 's/^id = 1$/id = 2/' tmp/configs/linux-aarch64-qemu-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/linux-aarch64-qemu-smp1.toml - - info "Preparing QEMU config file..." - run_cmd cp .github/workflows/qemu-aarch64.toml tmp/configs/qemu-aarch64-runtime.toml - - ROOTFS_PATH="$(pwd)/tmp/images/qemu_aarch64_linux/rootfs.img" - run_cmd sed -i 's|file=${workspaceFolder}/tmp/rootfs.img|file='"$ROOTFS_PATH"'|g' tmp/configs/qemu-aarch64-runtime.toml - run_cmd sed -i '/success_regex = \[/,/\]/c\success_regex = []' tmp/configs/qemu-aarch64-runtime.toml - - info "=== QEMU AArch64 Preparation Complete ===" -} - -run_qemu_aarch64_arceos() { - info "=== Launching QEMU AArch64 ArceOS Guest ===" - run_cmd cargo xtask qemu \ - --build-config tmp/configs/qemu-aarch64.toml \ - --qemu-config tmp/configs/qemu-aarch64-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-qemu-smp1.toml -} - -run_qemu_aarch64_linux() { - info "=== Launching QEMU AArch64 Linux Guest ===" - run_cmd cargo xtask qemu \ - --build-config tmp/configs/qemu-aarch64.toml \ - --qemu-config tmp/configs/qemu-aarch64-runtime.toml \ - --vmconfigs tmp/configs/linux-aarch64-qemu-smp1.toml -} - -run_qemu_aarch64_multi() { - info "=== Launching QEMU AArch64 Multiple Guests (ArceOS + Linux) ===" - run_cmd cargo xtask qemu \ - --build-config tmp/configs/qemu-aarch64.toml \ - --qemu-config tmp/configs/qemu-aarch64-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-qemu-smp1.toml \ - --vmconfigs tmp/configs/linux-aarch64-qemu-smp1.toml -} - -# ============================================================================ -# QEMU x86_64 Architecture Setup -# ============================================================================ - -setup_qemu_x86_64() { - info "=== QEMU x86_64 Preparation ===" - - run_cmd mkdir -p tmp/{configs,images} - - info "Downloading NimbOS image..." - run_cmd cargo xtask image download qemu_x86_64_nimbos --output-dir tmp/images - - info "Preparing board config file..." - run_cmd cp configs/board/qemu-x86_64.toml tmp/configs/ - - info "Preparing guest config file..." - run_cmd cp configs/vms/nimbos-x86_64-qemu-smp1.toml tmp/configs/ - - info "Preparing QEMU config file..." - run_cmd cp .github/workflows/qemu-x86_64.toml tmp/configs/qemu-x86_64-runtime.toml - - ROOTFS_PATH="$(pwd)/tmp/images/qemu_x86_64_nimbos/rootfs.img" - run_cmd sed -i 's|file=${workspaceFolder}/tmp/rootfs.img|file='"$ROOTFS_PATH"'|g' tmp/configs/qemu-x86_64-runtime.toml - - info "=== QEMU x86_64 Preparation Complete ===" -} - -run_qemu_x86_64_nimbos() { - info "=== Launching QEMU x86_64 NimbOS Guest ===" - run_cmd cargo xtask qemu \ - --build-config tmp/configs/qemu-x86_64.toml \ - --qemu-config tmp/configs/qemu-x86_64-runtime.toml \ - --vmconfigs tmp/configs/nimbos-x86_64-qemu-smp1.toml -} - -# ============================================================================ -# Phytium Pi Board Setup -# ============================================================================ - -setup_phytiumpi() { - local serial_device="" - local serial_specified=false - - # Parse arguments - while [[ $# -gt 0 ]]; do - case $1 in - --serial) - serial_device="$2" - serial_specified=true - shift 2 - ;; - *) - error "Unknown option: $1" - echo "" - echo "Phytium Pi setup supports the following options:" - echo " --serial Specify serial device" - exit 1 - ;; - esac - done - - info "=== Phytium Pi Board Preparation ===" - - run_cmd mkdir -p tmp/{configs,images} - - info "Checking/installing ostool..." - if ! command -v ostool &> /dev/null; then - info "ostool not installed, installing..." - cargo install ostool - else - info "ostool already installed" - fi - - info "Downloading ArceOS image..." - run_cmd cargo xtask image download phytiumpi_arceos --output-dir tmp/images - - info "Downloading Linux image (including device tree)..." - run_cmd cargo xtask image download phytiumpi_linux --output-dir tmp/images - - info "Preparing board config file..." - run_cmd cp configs/board/phytiumpi.toml tmp/configs/ - - info "Preparing guest config files..." - run_cmd cp configs/vms/arceos-aarch64-e2000-smp1.toml tmp/configs/ - run_cmd cp configs/vms/linux-aarch64-e2000-smp1.toml tmp/configs/ - - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/phytiumpi_arceos/phytiumpi"|g' tmp/configs/arceos-aarch64-e2000-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/arceos-aarch64-e2000-smp1.toml - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/phytiumpi_linux/phytiumpi"|g' tmp/configs/linux-aarch64-e2000-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/linux-aarch64-e2000-smp1.toml - - info "Preparing uboot config file..." - run_cmd cp .github/workflows/uboot.toml tmp/configs/phytiumpi-runtime.toml - - # Remove unnecessary commands - run_cmd sed -i '/^board_power_off_cmd = "\${env:BOARD_POWER_OFF}"/d' tmp/configs/phytiumpi-runtime.toml - run_cmd sed -i '/^board_reset_cmd = "\${env:BOARD_POWER_RESET}"/d' tmp/configs/phytiumpi-runtime.toml - - # Remove [net] config section - run_cmd sed -i '/^\[net\]/d' tmp/configs/phytiumpi-runtime.toml - run_cmd sed -i '/^interface = "\${env:BOARD_COMM_NET_IFACE}"/d' tmp/configs/phytiumpi-runtime.toml - run_cmd sed -i '/^tftp_dir = "\${env:TFTP_DIR}"/d' tmp/configs/phytiumpi-runtime.toml - - # Set baud rate - run_cmd sed -i 's|^baud_rate = "\${env:BOARD_COMM_UART_BAUD}"|baud_rate = "115200"|g' tmp/configs/phytiumpi-runtime.toml - - # Set serial device only if specified - if [ "$serial_specified" = true ]; then - info "Setting serial device to: $serial_device" - run_cmd sed -i 's|^serial = "\${env:BOARD_COMM_UART}"|serial = "'"$serial_device"'"|g' tmp/configs/phytiumpi-runtime.toml - else - # Remove serial line to keep it as environment variable - run_cmd sed -i '/^serial = "\${env:BOARD_COMM_UART}"/d' tmp/configs/phytiumpi-runtime.toml - fi - - info "Adding device tree file path to uboot config..." - DTB_PATH="$(pwd)/tmp/images/phytiumpi_linux/phytiumpi.dtb" - run_cmd sed -i 's|^dtb_file = "\${env:BOARD_DTB}"|dtb_file = "'"$DTB_PATH"'"|g' tmp/configs/phytiumpi-runtime.toml - - info "=== Phytium Pi Board Preparation Complete ===" - if [ "$serial_specified" = true ]; then - info "Serial device set to: $serial_device" - else - warn "IMPORTANT: Please set the correct serial device in tmp/configs/phytiumpi-runtime.toml" - warn "Example: serial = \"/dev/ttyUSB0\"" - warn "Then run: $0 phytiumpi run --arceos" - fi -} - -run_phytiumpi_arceos() { - info "=== Launching Phytium Pi ArceOS Guest ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/phytiumpi.toml \ - --uboot-config tmp/configs/phytiumpi-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-e2000-smp1.toml -} - -run_phytiumpi_linux() { - info "=== Launching Phytium Pi Linux Guest ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/phytiumpi.toml \ - --uboot-config tmp/configs/phytiumpi-runtime.toml \ - --vmconfigs tmp/configs/linux-aarch64-e2000-smp1.toml -} - -run_phytiumpi_multi() { - info "=== Launching Phytium Pi Multiple Guests (ArceOS + Linux) ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/phytiumpi.toml \ - --uboot-config tmp/configs/phytiumpi-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-e2000-smp1.toml \ - --vmconfigs tmp/configs/linux-aarch64-e2000-smp1.toml -} - -# ============================================================================ -# ROC-RK3568-PC Board Setup -# ============================================================================ - -setup_roc_rk3568_pc() { - local serial_device="" - local serial_specified=false - - # Parse arguments - while [[ $# -gt 0 ]]; do - case $1 in - --serial) - serial_device="$2" - serial_specified=true - shift 2 - ;; - *) - error "Unknown option: $1" - echo "" - echo "ROC-RK3568-PC setup supports the following options:" - echo " --serial Specify serial device" - exit 1 - ;; - esac - done - - info "=== ROC-RK3568-PC Board Preparation ===" - - run_cmd mkdir -p tmp/{configs,images} - - info "Checking/installing ostool..." - if ! command -v ostool &> /dev/null; then - info "ostool not installed, installing..." - cargo install ostool - else - info "ostool already installed" - fi - - info "Downloading ArceOS image..." - run_cmd cargo xtask image download roc-rk3568-pc_arceos --output-dir tmp/images - - info "Downloading Linux image (including device tree)..." - run_cmd cargo xtask image download roc-rk3568-pc_linux --output-dir tmp/images - - info "Preparing board config file..." - run_cmd cp configs/board/roc-rk3568-pc.toml tmp/configs/ - - info "Preparing guest config files..." - run_cmd cp configs/vms/arceos-aarch64-rk3568-smp1.toml tmp/configs/ - run_cmd cp configs/vms/linux-aarch64-rk3568-smp1.toml tmp/configs/ - - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/roc-rk3568-pc_arceos/roc-rk3568-pc"|g' tmp/configs/arceos-aarch64-rk3568-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/arceos-aarch64-rk3568-smp1.toml - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/roc-rk3568-pc_linux/roc-rk3568-pc"|g' tmp/configs/linux-aarch64-rk3568-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/linux-aarch64-rk3568-smp1.toml - - info "Preparing uboot config file..." - run_cmd cp .github/workflows/uboot.toml tmp/configs/roc-rk3568-pc-runtime.toml - - # Remove unnecessary commands - run_cmd sed -i '/^board_power_off_cmd = "\${env:BOARD_POWER_OFF}"/d' tmp/configs/roc-rk3568-pc-runtime.toml - run_cmd sed -i '/^board_reset_cmd = "\${env:BOARD_POWER_RESET}"/d' tmp/configs/roc-rk3568-pc-runtime.toml - - # Remove [net] config section - run_cmd sed -i '/^\[net\]/d' tmp/configs/roc-rk3568-pc-runtime.toml - run_cmd sed -i '/^interface = "\${env:BOARD_COMM_NET_IFACE}"/d' tmp/configs/roc-rk3568-pc-runtime.toml - run_cmd sed -i '/^tftp_dir = "\${env:TFTP_DIR}"/d' tmp/configs/roc-rk3568-pc-runtime.toml - - info "Setting baud rate to 1500000..." - run_cmd sed -i 's|baud_rate\s*=.*|baud_rate = "1500000"|g' tmp/configs/roc-rk3568-pc-runtime.toml - - # Set serial device only if specified - if [ "$serial_specified" = true ]; then - info "Setting serial device to: $serial_device" - run_cmd sed -i 's|^serial = "\${env:BOARD_COMM_UART}"|serial = "'"$serial_device"'"|g' tmp/configs/roc-rk3568-pc-runtime.toml - else - # Remove serial line to keep it as environment variable - run_cmd sed -i '/^serial = "\${env:BOARD_COMM_UART}"/d' tmp/configs/roc-rk3568-pc-runtime.toml - fi - - info "Adding device tree file path to uboot config..." - DTB_PATH="$(pwd)/tmp/images/roc-rk3568-pc_linux/roc-rk3568-pc.dtb" - run_cmd sed -i 's|^dtb_file = "\${env:BOARD_DTB}"|dtb_file = "'"$DTB_PATH"'"|g' tmp/configs/roc-rk3568-pc-runtime.toml - - info "=== ROC-RK3568-PC Board Preparation Complete ===" - info "Baud rate has been set to 1500000" - if [ "$serial_specified" = true ]; then - info "Serial device set to: $serial_device" - else - warn "IMPORTANT: Please set the correct serial device in tmp/configs/roc-rk3568-pc-runtime.toml" - warn "Example: serial = \"/dev/ttyUSB0\"" - warn "Then run: $0 roc-rk3568-pc run --arceos" - fi -} - -run_roc_rk3568_pc_arceos() { - info "=== Launching ROC-RK3568-PC ArceOS Guest ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/roc-rk3568-pc.toml \ - --uboot-config tmp/configs/roc-rk3568-pc-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-rk3568-smp1.toml -} - -run_roc_rk3568_pc_linux() { - info "=== Launching ROC-RK3568-PC Linux Guest ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/roc-rk3568-pc.toml \ - --uboot-config tmp/configs/roc-rk3568-pc-runtime.toml \ - --vmconfigs tmp/configs/linux-aarch64-rk3568-smp1.toml -} - -run_roc_rk3568_pc_multi() { - info "=== Launching ROC-RK3568-PC Multiple Guests (ArceOS + Linux) ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/roc-rk3568-pc.toml \ - --uboot-config tmp/configs/roc-rk3568-pc-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-rk3568-smp1.toml \ - --vmconfigs tmp/configs/linux-aarch64-rk3568-smp1.toml -} - -# ============================================================================ -# QEMU AArch64 Command Handling -# ============================================================================ - -cmd_setup_qemu_aarch64() { - setup_qemu_aarch64 -} - -cmd_run_qemu_aarch64() { - local mode="$1" - - case "$mode" in - -a|--arceos|"") - run_qemu_aarch64_arceos - ;; - -l|--linux) - run_qemu_aarch64_linux - ;; - -m|--multi) - run_qemu_aarch64_multi - ;; - -n|--nimbos) - error "Unsupported combination: QEMU AArch64 does not support NimbOS" - echo "" - echo "QEMU AArch64 platform supports the following guest systems:" - echo " - ArceOS (use --arceos)" - echo " - Linux (use --linux)" - echo " - Multiple guests (use --multi)" - echo "" - echo "To run NimbOS, please use QEMU x86_64 platform:" - echo " $0 qemu-x86_64 start --nimbos" - exit 1 - ;; - *) - error "Unknown option: $mode" - echo "" - echo "QEMU AArch64 platform supports the following options:" - echo " -a, --arceos Launch ArceOS guest" - echo " -l, --linux Launch Linux guest" - echo " -m, --multi Launch multiple guests (ArceOS + Linux)" - exit 1 - ;; - esac -} - -cmd_start_qemu_aarch64() { - local mode="$1" - # Validate parameters first - case "$mode" in - -a|--arceos|-l|--linux|-m|--multi|"") - # Valid parameters, continue - ;; - *) - # Invalid parameters, report error without executing setup - cmd_run_qemu_aarch64 "$mode" - return - ;; - esac - setup_qemu_aarch64 - echo "" - cmd_run_qemu_aarch64 "$mode" -} - -# ============================================================================ -# QEMU x86_64 Command Handling -# ============================================================================ - -cmd_setup_qemu_x86_64() { - setup_qemu_x86_64 -} - -cmd_run_qemu_x86_64() { - local mode="$1" - - case "$mode" in - -n|--nimbos|"") - run_qemu_x86_64_nimbos - ;; - -a|--arceos) - error "Unsupported combination: QEMU x86_64 does not support ArceOS" - echo "" - echo "QEMU x86_64 platform only supports the following guest system:" - echo " - NimbOS (use --nimbos)" - echo "" - echo "To run ArceOS, please use one of the following platforms:" - echo " $0 qemu-aarch64 start --arceos" - echo " $0 phytiumpi setup && $0 phytiumpi run --arceos" - echo " $0 roc-rk3568-pc setup && $0 roc-rk3568-pc run --arceos" - exit 1 - ;; - -l|--linux) - error "Unsupported combination: QEMU x86_64 does not support Linux" - echo "" - echo "QEMU x86_64 platform only supports the following guest system:" - echo " - NimbOS (use --nimbos)" - echo "" - echo "To run Linux, please use one of the following platforms:" - echo " $0 qemu-aarch64 start --linux" - echo " $0 phytiumpi setup && $0 phytiumpi run --linux" - echo " $0 roc-rk3568-pc setup && $0 roc-rk3568-pc run --linux" - exit 1 - ;; - -m|--multi) - error "Unsupported combination: QEMU x86_64 does not support multi-guest mode" - echo "" - echo "QEMU x86_64 platform only supports the following guest system:" - echo " - NimbOS (use --nimbos)" - echo "" - echo "To run multiple guests, please use one of the following platforms:" - echo " $0 qemu-aarch64 start --multi" - echo " $0 phytiumpi setup && $0 phytiumpi run --multi" - echo " $0 roc-rk3568-pc setup && $0 roc-rk3568-pc run --multi" - exit 1 - ;; - *) - error "Unknown option: $mode" - echo "" - echo "QEMU x86_64 platform supports the following options:" - echo " -n, --nimbos Launch NimbOS guest" - exit 1 - ;; - esac -} - -cmd_start_qemu_x86_64() { - local mode="$1" - # Validate parameters first - case "$mode" in - -n|--nimbos|"") - # Valid parameters, continue - ;; - *) - # Invalid parameters, report error without executing setup - cmd_run_qemu_x86_64 "$mode" - return - ;; - esac - setup_qemu_x86_64 - echo "" - cmd_run_qemu_x86_64 "$mode" -} - -# ============================================================================ -# Phytium Pi Command Handling -# ============================================================================ - -cmd_setup_phytiumpi() { - setup_phytiumpi "$@" -} - -cmd_start_phytiumpi() { - local serial_args=() - local guest_mode="" - local serial_specified=false - - # Parse arguments to separate --serial from guest mode - while [[ $# -gt 0 ]]; do - case $1 in - --serial) - serial_args+=("$1" "$2") - serial_specified=true - shift 2 - ;; - -a|--arceos|-l|--linux|-m|--multi|"") - guest_mode="$1" - shift - ;; - *) - error "Unknown option: $1" - exit 1 - ;; - esac - done - - # First run setup with serial args - setup_phytiumpi "${serial_args[@]}" - - # If serial not specified, stop after setup - if [ "$serial_specified" = false ]; then - return - fi - - echo "" - # Then run with guest mode - cmd_run_phytiumpi "$guest_mode" -} - -cmd_run_phytiumpi() { - local mode="$1" - - case "$mode" in - -a|--arceos|"") - run_phytiumpi_arceos - ;; - -l|--linux) - run_phytiumpi_linux - ;; - -m|--multi) - run_phytiumpi_multi - ;; - -n|--nimbos) - error "Unsupported combination: Phytium Pi board does not support NimbOS" - echo "" - echo "Phytium Pi board supports the following guest systems:" - echo " - ArceOS (use --arceos)" - echo " - Linux (use --linux)" - echo " - Multiple guests (use --multi)" - echo "" - echo "To run NimbOS, please use QEMU x86_64 platform:" - echo " $0 qemu-x86_64 start --nimbos" - exit 1 - ;; - *) - error "Unknown option: $mode" - echo "" - echo "Phytium Pi board supports the following options:" - echo " -a, --arceos Launch ArceOS guest" - echo " -l, --linux Launch Linux guest" - echo " -m, --multi Launch multiple guests (ArceOS + Linux)" - exit 1 - ;; - esac -} - -# ============================================================================ -# ROC-RK3568-PC Command Handling -# ============================================================================ - -cmd_setup_roc_rk3568_pc() { - setup_roc_rk3568_pc "$@" -} - -cmd_start_roc_rk3568_pc() { - local serial_args=() - local guest_mode="" - local serial_specified=false - - # Parse arguments to separate --serial from guest mode - while [[ $# -gt 0 ]]; do - case $1 in - --serial) - serial_args+=("$1" "$2") - serial_specified=true - shift 2 - ;; - -a|--arceos|-l|--linux|-m|--multi|"") - guest_mode="$1" - shift - ;; - *) - error "Unknown option: $1" - exit 1 - ;; - esac - done - - # First run setup with serial args - setup_roc_rk3568_pc "${serial_args[@]}" - - # If serial not specified, stop after setup - if [ "$serial_specified" = false ]; then - return - fi - - echo "" - # Then run with guest mode - cmd_run_roc_rk3568_pc "$guest_mode" -} - -cmd_run_roc_rk3568_pc() { - local mode="$1" - - case "$mode" in - -a|--arceos|"") - run_roc_rk3568_pc_arceos - ;; - -l|--linux) - run_roc_rk3568_pc_linux - ;; - -m|--multi) - run_roc_rk3568_pc_multi - ;; - -n|--nimbos) - error "Unsupported combination: ROC-RK3568-PC board does not support NimbOS" - echo "" - echo "ROC-RK3568-PC board supports the following guest systems:" - echo " - ArceOS (use --arceos)" - echo " - Linux (use --linux)" - echo " - Multiple guests (use --multi)" - echo "" - echo "To run NimbOS, please use QEMU x86_64 platform:" - echo " $0 qemu-x86_64 start --nimbos" - exit 1 - ;; - *) - error "Unknown option: $mode" - echo "" - echo "ROC-RK3568-PC board supports the following options:" - echo " -a, --arceos Launch ArceOS guest" - echo " -l, --linux Launch Linux guest" - echo " -m, --multi Launch multiple guests (ArceOS + Linux)" - exit 1 - ;; - esac -} - -# ============================================================================ -# Main Program -# ============================================================================ - -check_root_dir - -if [ $# -eq 0 ]; then - # Default to QEMU AArch64 ArceOS - cmd_start_qemu_aarch64 "" - exit 0 -fi - -PLATFORM="$1" -shift - -case "$PLATFORM" in - qemu-aarch64) - if [ $# -eq 0 ]; then - show_help - exit 0 - fi - CMD="$1" - shift - case "$CMD" in - setup) - cmd_setup_qemu_aarch64 - ;; - run) - cmd_run_qemu_aarch64 "$@" - ;; - start) - cmd_start_qemu_aarch64 "$@" - ;; - *) - error "Unknown command: $CMD" - show_help - exit 1 - ;; - esac - ;; - qemu-x86_64) - if [ $# -eq 0 ]; then - show_help - exit 0 - fi - CMD="$1" - shift - case "$CMD" in - setup) - cmd_setup_qemu_x86_64 - ;; - run) - cmd_run_qemu_x86_64 "$@" - ;; - start) - cmd_start_qemu_x86_64 "$@" - ;; - *) - error "Unknown command: $CMD" - show_help - exit 1 - ;; - esac - ;; - phytiumpi) - if [ $# -eq 0 ]; then - show_help - exit 0 - fi - CMD="$1" - shift - case "$CMD" in - setup) - cmd_setup_phytiumpi "$@" - ;; - run) - cmd_run_phytiumpi "$@" - ;; - start) - cmd_start_phytiumpi "$@" - ;; - *) - error "Unknown command: $CMD" - echo "" - echo "Phytium Pi board supports the following commands:" - echo " setup [--serial ] Prepare board environment (download images, prepare config files)" - echo " run [--arceos|--linux|--multi] Launch guest" - echo " start [--serial ] [--arceos|--linux|--multi] One-step setup + launch" - exit 1 - ;; - esac - ;; - roc-rk3568-pc) - if [ $# -eq 0 ]; then - show_help - exit 0 - fi - CMD="$1" - shift - case "$CMD" in - setup) - cmd_setup_roc_rk3568_pc "$@" - ;; - run) - cmd_run_roc_rk3568_pc "$@" - ;; - start) - cmd_start_roc_rk3568_pc "$@" - ;; - *) - error "Unknown command: $CMD" - echo "" - echo "ROC-RK3568-PC board supports the following commands:" - echo " setup [--serial ] Prepare board environment (download images, prepare config files)" - echo " run [--arceos|--linux|--multi] Launch guest" - echo " start [--serial ] [--arceos|--linux|--multi] One-step setup + launch" - exit 1 - ;; - esac - ;; - -h|--help) - show_help - ;; - *) - error "Unknown platform: $PLATFORM" - show_help - exit 1 - ;; -esac diff --git a/os/axvisor/scripts/setup_qemu.sh b/os/axvisor/scripts/setup_qemu.sh deleted file mode 100755 index 730f7fe55..000000000 --- a/os/axvisor/scripts/setup_qemu.sh +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Unified QEMU guest setup script for AxVisor testing. -# Usage: -# ./scripts/setup_qemu.sh [--guest] -# ./scripts/setup_qemu.sh arceos -# ./scripts/setup_qemu.sh --guest linux -# ./scripts/setup_qemu.sh nimbos -# -# Supported guests: arceos, linux, nimbos - -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -IMAGE_STORAGE_ROOT="/tmp/.axvisor-images" -DEFAULT_REGISTRY_URL="https://raw.githubusercontent.com/arceos-hypervisor/axvisor-guest/refs/heads/main/registry/default.toml" -# Keep this version aligned with the guest release used in this branch. -BUILTIN_FALLBACK_REGISTRY_URL="https://raw.githubusercontent.com/arceos-hypervisor/axvisor-guest/refs/heads/main/registry/v0.0.22.toml" -IMAGE_DOWNLOAD_MAX_ATTEMPTS=2 - -bootstrap_image_registry() { - local storage_dir="${IMAGE_STORAGE_ROOT}" - local default_registry_url="${DEFAULT_REGISTRY_URL}" - # Built-in fallback keeps setup resilient when default/include URLs are flaky. - local fallback_registry_url="${AXVISOR_REGISTRY_FALLBACK_URL:-${BUILTIN_FALLBACK_REGISTRY_URL}}" - local default_registry_file - local include_url - local source_kind - local source_url - - mkdir -p "${storage_dir}" - if [ -f "${storage_dir}/images.toml" ]; then - return 0 - fi - - default_registry_file="$(mktemp)" - if ! curl -4 --retry 5 --retry-delay 2 -fL "${default_registry_url}" -o "${default_registry_file}"; then - rm -f "${default_registry_file}" - echo " -> Warning: failed to fetch default registry: ${default_registry_url}" >&2 - source_kind="fallback registry" - source_url="${fallback_registry_url}" - else - include_url="$(sed -n 's/^[[:space:]]*url[[:space:]]*=[[:space:]]*"\([^"]*\)".*$/\1/p' "${default_registry_file}" | sed -n '1p')" - rm -f "${default_registry_file}" - if [ -n "${include_url}" ]; then - source_kind="included registry from default.toml" - source_url="${include_url}" - else - source_kind="default registry" - source_url="${default_registry_url}" - fi - fi - - echo " -> Bootstrapping local image registry from ${source_kind}: ${source_url}" - if ! curl -4 --retry 5 --retry-delay 2 -fL "${source_url}" -o "${storage_dir}/images.toml"; then - if [ "${source_url}" != "${fallback_registry_url}" ]; then - echo " -> Warning: failed to fetch ${source_kind}, retrying fallback registry: ${fallback_registry_url}" >&2 - curl -4 --retry 5 --retry-delay 2 -fL "${fallback_registry_url}" -o "${storage_dir}/images.toml" - else - echo " -> Error: failed to bootstrap local image registry from fallback registry." >&2 - return 1 - fi - fi - date +%s > "${storage_dir}/.last_sync" || true -} - -usage() { - echo "Usage: $0 [--guest] " - echo "" - echo " arceos - aarch64 ArceOS guest" - echo " linux - aarch64 Linux guest" - echo " nimbos - x86_64 NimbOS guest (requires VT-x/KVM)" - echo "" - echo "Examples:" - echo " $0 arceos" - echo " $0 --guest linux" - exit 1 -} - -GUEST="" -while [[ $# -gt 0 ]]; do - case "$1" in - --guest) - shift - [[ $# -gt 0 ]] || usage - GUEST="$1" - shift - break - ;; - arceos|linux|nimbos) - GUEST="$1" - shift - break - ;; - -h|--help) - usage - ;; - *) - echo "Unknown option: $1" >&2 - usage - ;; - esac -done - -[[ -n "${GUEST}" ]] || usage - -# Guest configuration: image_name|vmconfig|build_config|qemu_config|kernel_file|success_msg -case "$GUEST" in - arceos) CFG="qemu_aarch64_arceos|arceos-aarch64-qemu-smp1.toml|qemu-aarch64.toml|qemu-aarch64.toml|qemu-aarch64|Hello, world!" ;; - linux) CFG="qemu_aarch64_linux|linux-aarch64-qemu-smp1.toml|qemu-aarch64.toml|qemu-aarch64.toml|qemu-aarch64|test pass!" ;; - nimbos) CFG="qemu_x86_64_nimbos|nimbos-x86_64-qemu-smp1.toml|qemu-x86_64.toml|qemu-x86_64-kvm.toml|qemu-x86_64|usertests passed!" ;; - *) echo "Unknown guest: $GUEST" >&2; usage ;; -esac - -IFS='|' read -r IMAGE_NAME VMCONFIG BUILD_CONFIG QEMU_CONFIG KERNEL_FILE SUCCESS_MSG <<< "$CFG" -# NOTE: -# - `cargo xtask image download` 默认把镜像解压到 `/tmp/.axvisor-images/` -# - 这里直接使用该目录作为镜像来源,避免路径不一致 -IMAGE_DIR="${IMAGE_STORAGE_ROOT}/${IMAGE_NAME}" -VMCONFIG_TEMPLATE_PATH="${REPO_ROOT}/configs/vms/${VMCONFIG}" -VMCONFIG_TMP_DIR="${REPO_ROOT}/tmp/vmconfigs" -GENERATED_VMCONFIG_PATH="${VMCONFIG_TMP_DIR}/${VMCONFIG%.toml}.generated.toml" -ROOTFS_TARGET="${REPO_ROOT}/tmp/rootfs.img" -KERNEL_IMAGE="${IMAGE_DIR}/${KERNEL_FILE}" -ROOTFS_IMAGE="${IMAGE_DIR}/rootfs.img" -ABS_KERNEL_PATH="${IMAGE_DIR}/${KERNEL_FILE}" - -echo "[setup_qemu] Guest: ${GUEST} | Repo: ${REPO_ROOT}" - -echo "[setup_qemu] Step 1: ensure guest image is downloaded..." -if [ ! -d "${IMAGE_DIR}" ]; then - echo " -> Image directory ${IMAGE_DIR} not found, downloading via cargo xtask image..." - echo " -> Download attempt 1/${IMAGE_DOWNLOAD_MAX_ATTEMPTS}" - if ! (cd "${REPO_ROOT}" && cargo xtask image download "${IMAGE_NAME}"); then - echo " -> Attempt 1/${IMAGE_DOWNLOAD_MAX_ATTEMPTS} failed. Trying to bootstrap registry..." - bootstrap_image_registry - echo " -> Download attempt 2/${IMAGE_DOWNLOAD_MAX_ATTEMPTS}" - (cd "${REPO_ROOT}" && cargo xtask image download "${IMAGE_NAME}") - fi -else - echo " -> Found existing image directory: ${IMAGE_DIR}" -fi - -if [ ! -f "${KERNEL_IMAGE}" ]; then - echo "ERROR: kernel image not found at ${KERNEL_IMAGE}" >&2 - exit 1 -fi - -if [ ! -f "${ROOTFS_IMAGE}" ]; then - echo "ERROR: rootfs image not found at ${ROOTFS_IMAGE}" >&2 - exit 1 -fi - -# NimbOS x86_64 requires axvm-bios for bootstrapping -if [[ "$GUEST" == "nimbos" ]]; then - BIOS_IMAGE="${IMAGE_DIR}/axvm-bios.bin" - if [ ! -f "${BIOS_IMAGE}" ]; then - echo "ERROR: axvm-bios.bin not found at ${BIOS_IMAGE}" >&2 - echo " -> Please re-download the NimbOS image via 'cargo xtask image download qemu_x86_64_nimbos'." >&2 - exit 1 - fi -fi - -echo "[setup_qemu] Step 2: patch VM config kernel_path..." -if [ ! -f "${VMCONFIG_TEMPLATE_PATH}" ]; then - echo "ERROR: VM config file not found at ${VMCONFIG_TEMPLATE_PATH}" >&2 - exit 1 -fi - -mkdir -p "${VMCONFIG_TMP_DIR}" -cp "${VMCONFIG_TEMPLATE_PATH}" "${GENERATED_VMCONFIG_PATH}" -sed -i 's|^kernel_path *=.*|kernel_path = "'"${ABS_KERNEL_PATH}"'"|' "${GENERATED_VMCONFIG_PATH}" -echo " -> Generated VM config: ${GENERATED_VMCONFIG_PATH}" -echo " -> Updated kernel_path to ${ABS_KERNEL_PATH}" - -if [[ "$GUEST" == "nimbos" ]]; then - ABS_BIOS_PATH="${IMAGE_DIR}/axvm-bios.bin" - sed -i 's|^bios_path *=.*|bios_path = "'"${ABS_BIOS_PATH}"'"|' "${GENERATED_VMCONFIG_PATH}" - echo " -> Updated bios_path to ${ABS_BIOS_PATH}" -fi - -echo "[setup_qemu] Step 3: prepare rootfs..." -mkdir -p "${REPO_ROOT}/tmp" -cp "${ROOTFS_IMAGE}" "${ROOTFS_TARGET}" - -echo " -> Copied ${ROOTFS_IMAGE} -> ${ROOTFS_TARGET}" - -cat < usize { - memory_addr::PAGE_SIZE_4K - } - - unsafe fn map_single( - &self, - dma_mask: u64, - addr: NonNull, - size: core::num::NonZeroUsize, - align: usize, - _direction: rdif_block::dma_api::DmaDirection, - ) -> Result { - let layout = core::alloc::Layout::from_size_align(size.get(), align)?; - let dma_addr = - axvisor_api::memory::virt_to_phys((addr.as_ptr() as usize).into()).as_usize() as u64; - - if dma_addr > dma_mask || dma_addr.wrapping_add(size.get() as u64) > dma_mask { - return Err(rdif_block::dma_api::DmaError::DmaMaskNotMatch { - addr: dma_addr.into(), - mask: dma_mask, - }); - } - - if !dma_addr.is_multiple_of(align as u64) { - return Err(rdif_block::dma_api::DmaError::AlignMismatch { - required: align, - address: dma_addr.into(), - }); - } - - Ok(unsafe { rdif_block::dma_api::DmaMapHandle::new(addr, dma_addr.into(), layout, None) }) - } - - unsafe fn unmap_single(&self, _handle: rdif_block::dma_api::DmaMapHandle) {} - - unsafe fn alloc_coherent( - &self, - dma_mask: u64, - layout: core::alloc::Layout, - ) -> Option { - let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) }; - let cpu_addr = NonNull::new(ptr)?; - - let dma_addr = axvisor_api::memory::virt_to_phys((ptr as usize).into()).as_usize() as u64; - if dma_addr > dma_mask || dma_addr.wrapping_add(layout.size() as u64) > dma_mask { - unsafe { alloc::alloc::dealloc(cpu_addr.as_ptr(), layout) }; - return None; - } - - Some(unsafe { rdif_block::dma_api::DmaHandle::new(cpu_addr, dma_addr.into(), layout) }) - } - - unsafe fn dealloc_coherent(&self, handle: rdif_block::dma_api::DmaHandle) { - unsafe { alloc::alloc::dealloc(handle.as_ptr().as_ptr(), handle.layout()) } - } -} - -#[cfg(any(feature = "sdmmc", feature = "phytium-blk"))] -pub trait PlatformDeviceBlock { - fn register_block(self, dev: T); -} - -#[cfg(any(feature = "sdmmc", feature = "phytium-blk"))] -impl PlatformDeviceBlock for PlatformDevice { - fn register_block(self, dev: T) { - // Use rd_block::Block to wrap the Interface for axdriver compatibility - self.register(rd_block::Block::new(dev, &DmaImpl)); - } -} diff --git a/os/axvisor/src/driver/blk/phytium.rs b/os/axvisor/src/driver/blk/phytium.rs deleted file mode 100644 index a46fdcc90..000000000 --- a/os/axvisor/src/driver/blk/phytium.rs +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axklib::{mem::iomap, time::busy_wait}; - -use core::{ - cmp, - marker::{Send, Sync}, - ptr::NonNull, - time::Duration, -}; - -use log::{debug, info}; -use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo}; - -use phytium_mci::sd::SdCard; -use phytium_mci::{IoPad, PAD_ADDRESS, mci_host::err::MCIHostError}; -pub use phytium_mci::{Kernel, set_impl}; - -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use log::trace; - -use rdif_block::{BlkError, IQueue, Interface, Request, RequestId}; -use rdrive::DriverGeneric; - -use spin::Mutex; - -// pub use dma_api::{Direction, Impl as DmaImpl}; -// pub use dma_api::set_impl as set_dma_impl; - -const OFFSET: usize = 0x400_0000; -const BLOCK_SIZE: usize = 512; - -pub struct KernelImpl; - -impl Kernel for KernelImpl { - fn sleep(us: Duration) { - busy_wait(us); - } -} - -set_impl!(KernelImpl); - -use crate::driver::blk::PlatformDeviceBlock; - -module_driver!( - name: "Phytium SdCard", - level: ProbeLevel::PostKernel, - priority: ProbePriority::DEFAULT, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["phytium,mci"], - on_probe: probe_sdcard - } - ], -); - -fn probe_sdcard(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - info!("Probing Phytium SDCard..."); - let mci_reg = info - .node - .regs() - .into_iter() - .next() - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - info!( - "MCI reg: addr={:#x}, size={:#x}", - mci_reg.address as usize, - mci_reg.size.unwrap_or(0) - ); - - let mci_reg_base = iomap( - (mci_reg.address as usize).into(), - mci_reg.size.unwrap_or(0x10000) as usize, - ) - .expect("Failed to iomap mci reg"); - - let iopad_reg_base = - iomap((PAD_ADDRESS as usize).into(), 0x2000).expect("Failed to iomap iopad reg"); - - info!("MCI reg base mapped at {:#x}", mci_reg_base.as_usize()); - - let mci_reg = - NonNull::new(mci_reg_base.as_usize() as *mut u8).expect("Failed to create NonNull pointer"); - - let iopad_reg = NonNull::new(iopad_reg_base.as_usize() as *mut u8) - .expect("Failed to create NonNull pointer for iopad"); - - let iopad = IoPad::new(iopad_reg); - - info!("MCI reg mapped at {:p}", mci_reg); - - let sdcard = SdCardDriver::new(mci_reg, iopad); - plat_dev.register_block(sdcard); - - debug!("phytium block device registered successfully"); - - Ok(()) -} - -pub struct SdCardDriver { - sd_card: Arc>>, -} - -impl SdCardDriver { - pub fn new(sd_addr: NonNull, iopad: IoPad) -> Self { - let sd_card = Arc::new(Mutex::new(Box::new(SdCard::new(sd_addr, iopad)))); - SdCardDriver { sd_card } - } -} - -unsafe impl Send for SdCardDriver {} -unsafe impl Sync for SdCardDriver {} - -unsafe impl Send for SdCardQueue {} -unsafe impl Sync for SdCardQueue {} - -impl DriverGeneric for SdCardDriver { - fn name(&self) -> &str { - "phytium-sdcard" - } -} - -impl Interface for SdCardDriver { - fn create_queue(&mut self) -> Option> { - Some(Box::new(SdCardQueue { - sd_card: Arc::clone(&self.sd_card), - })) - } - - fn enable_irq(&mut self) { - todo!() - } - - fn disable_irq(&mut self) { - todo!() - } - - fn is_irq_enabled(&self) -> bool { - false - } - - fn handle_irq(&mut self) -> rdif_block::Event { - rdif_block::Event::none() - } -} - -pub struct SdCardQueue { - sd_card: Arc>>, -} - -impl IQueue for SdCardQueue { - /// Returns the number of blocks on the SD card. - fn num_blocks(&self) -> usize { - self.sd_card.lock().block_count() as usize - } - - /// Returns the block size in bytes. - fn block_size(&self) -> usize { - self.sd_card.lock().block_size() as usize - } - - fn id(&self) -> usize { - 0 - } - - fn buff_config(&self) -> rdif_block::BuffConfig { - rdif_block::BuffConfig { - dma_mask: u64::MAX, - align: 0x1000, - size: self.block_size(), - } - } - - fn submit_request(&mut self, request: Request<'_>) -> Result { - let actual_block_id = request.block_id + OFFSET / 512; - - match request.kind { - rdif_block::RequestKind::Read(mut buffer) => { - trace!("read block {}", actual_block_id); - - Self::validate_buffer(&buffer)?; - - let (_, aligned_buf, _) = unsafe { buffer.align_to_mut::() }; - let mut temp_buf: Vec = Vec::with_capacity(aligned_buf.len()); - - self.sd_card - .lock() - .read_blocks(&mut temp_buf, actual_block_id as u32, 1) - .map_err(|err| map_mci_error_to_blk_error(err))?; - - let copy_len = cmp::min(temp_buf.len(), aligned_buf.len()); - aligned_buf[..copy_len].copy_from_slice(&temp_buf[..copy_len]); - - Ok(RequestId::new(0)) - } - rdif_block::RequestKind::Write(buffer) => { - trace!("write block {}", actual_block_id); - - Self::validate_buffer(&buffer)?; - - let (_, aligned_buf, _) = unsafe { buffer.align_to::() }; - let mut write_buf: Vec = aligned_buf.to_vec(); - - self.sd_card - .lock() - .write_blocks(&mut write_buf, actual_block_id as u32, 1) - .map_err(|err| map_mci_error_to_blk_error(err))?; - - Ok(RequestId::new(0)) - } - } - } - - fn poll_request( - &mut self, - _request: rdif_block::RequestId, - ) -> Result<(), rdif_block::BlkError> { - Ok(()) - } -} - -impl SdCardQueue { - fn validate_buffer(buffer: &[u8]) -> Result<(), BlkError> { - if buffer.len() < BLOCK_SIZE { - return Err(BlkError::Other(Box::new(BufferError::InvalidSize { - expected: BLOCK_SIZE, - actual: buffer.len(), - }))); - } - - let (prefix, _, suffix) = unsafe { buffer.align_to::() }; - if !prefix.is_empty() || !suffix.is_empty() { - return Err(BlkError::Other(Box::new(BufferError::InvalidAlignment))); - } - - Ok(()) - } -} - -#[derive(Debug)] -enum BufferError { - InvalidSize { expected: usize, actual: usize }, - InvalidAlignment, -} - -impl core::fmt::Display for BufferError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - BufferError::InvalidSize { expected, actual } => { - write!( - f, - "Invalid buffer size: expected at least {}, got {}", - expected, actual - ) - } - BufferError::InvalidAlignment => { - write!(f, "Buffer is not properly aligned for u32 access") - } - } - } -} - -impl core::error::Error for BufferError {} - -#[derive(Debug)] -struct MCIErrorWrapper(MCIHostError); - -impl core::fmt::Display for MCIErrorWrapper { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "MCI Host Error: {:?}", self.0) - } -} - -impl core::error::Error for MCIErrorWrapper {} - -// 错误映射函数 -fn map_mci_error_to_blk_error(err: MCIHostError) -> BlkError { - match err { - MCIHostError::Timeout => BlkError::Retry, - - MCIHostError::OutOfRange | MCIHostError::InvalidArgument => { - BlkError::Other(Box::new(MCIErrorWrapper(err))) - } - - MCIHostError::CardDetectFailed | MCIHostError::CardInitFailed => BlkError::NotSupported, - - MCIHostError::InvalidVoltage - | MCIHostError::SwitchVoltageFail - | MCIHostError::SwitchVoltage18VFail33VSuccess => BlkError::NotSupported, - - MCIHostError::TransferFailed - | MCIHostError::StopTransmissionFailed - | MCIHostError::WaitWriteCompleteFailed => BlkError::Retry, - - // 其他所有错误包装为Other - _ => BlkError::Other(Box::new(MCIErrorWrapper(err))), - } -} diff --git a/os/axvisor/src/driver/blk/rockchip.rs b/os/axvisor/src/driver/blk/rockchip.rs deleted file mode 100644 index 14ec536e9..000000000 --- a/os/axvisor/src/driver/blk/rockchip.rs +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::{format, string::ToString, vec::Vec}; -use core::time::Duration; - -use rdif_clk::ClockId; -use rdrive::{ - Device, DriverGeneric, PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo, -}; -use sdmmc::emmc::{self, EMmcHost}; -use spin::Once; - -use crate::driver::{blk::PlatformDeviceBlock, iomap}; - -module_driver!( - name: "Rockchip sdhci", - level: ProbeLevel::PostKernel, - priority: ProbePriority::DEFAULT, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["rockchip,dwcmshc-sdhci"], - on_probe: probe - } - ], -); - -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - let base_reg = info - .node - .regs() - .into_iter() - .next() - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - let mmio_size = base_reg.size.unwrap_or(0x1000); - - let mmio_base = iomap(base_reg.address, mmio_size as usize)?; - - let clock: Vec<_> = info.node.clocks().into_iter().collect(); - - info!("perparing to init emmc with clock"); - - for clk in &clock { - info!( - "clock: phandle {}, name: {:?}, cells: {:?}", - clk.phandle, clk.name, clk.cells - ); - - if clk.name == Some("core".to_string()) { - let id = info - .phandle_to_device_id(clk.phandle) - .expect("no device id"); - - let clk_dev = rdrive::get::(id).expect("clk not found"); - - let clk_dev = ClkDev { - inner: clk_dev, - id: (clk.select().unwrap_or(0) as usize).into(), - // TODO: verify the id - // id: 300.into(), - }; - CLK_DEV.call_once(|| clk_dev); - - emmc::clock::init_global_clk(CLK_DEV.wait()); - } - } - - let mut emmc = EMmcHost::new(mmio_base.as_ptr() as usize); - emmc.init().map_err(|e| { - OnProbeError::other(format!( - "failed to initialize eMMC device at [PA:{:?}, SZ:0x{:x}): {e:?}", - base_reg.address, mmio_size - )) - })?; - let info = emmc.get_card_info().map_err(|e| { - OnProbeError::other(format!( - "failed to get eMMC card info at [PA:{:?}, SZ:0x{:x}): {e:?}", - base_reg.address, mmio_size - )) - })?; - info!("eMMC card info: {:#?}", info); - - let dev = BlockDivce { dev: Some(emmc) }; - plat_dev.register_block(dev); - debug!("virtio block device registered successfully"); - Ok(()) -} - -struct BlockDivce { - dev: Option, -} - -struct BlockQueue { - raw: EMmcHost, -} - -impl DriverGeneric for BlockDivce { - fn name(&self) -> &str { - "rockchip-emmc" - } -} - -impl rdif_block::Interface for BlockDivce { - fn create_queue(&mut self) -> Option> { - self.dev - .take() - .map(|dev| alloc::boxed::Box::new(BlockQueue { raw: dev }) as _) - } - - fn enable_irq(&mut self) { - todo!() - } - - fn disable_irq(&mut self) { - todo!() - } - - fn is_irq_enabled(&self) -> bool { - false - } - - fn handle_irq(&mut self) -> rdif_block::Event { - rdif_block::Event::none() - } -} - -impl rdif_block::IQueue for BlockQueue { - fn num_blocks(&self) -> usize { - self.raw.get_block_num() as _ - } - - fn block_size(&self) -> usize { - self.raw.get_block_size() - } - - fn id(&self) -> usize { - 0 - } - - fn buff_config(&self) -> rdif_block::BuffConfig { - rdif_block::BuffConfig { - dma_mask: u64::MAX, - align: 0x1000, - size: self.block_size(), - } - } - - fn submit_request( - &mut self, - request: rdif_block::Request<'_>, - ) -> Result { - let id = request.block_id; - match request.kind { - rdif_block::RequestKind::Read(mut buffer) => { - let blocks = buffer.len() / self.block_size(); - self.raw - .read_blocks(id as _, blocks as _, &mut buffer) - .map_err(maping_dev_err_to_blk_err)?; - Ok(rdif_block::RequestId::new(0)) - } - rdif_block::RequestKind::Write(items) => { - let blocks = items.len() / self.block_size(); - self.raw - .write_blocks(id as _, blocks as _, items) - .map_err(maping_dev_err_to_blk_err)?; - Ok(rdif_block::RequestId::new(0)) - } - } - } - - fn poll_request( - &mut self, - _request: rdif_block::RequestId, - ) -> Result<(), rdif_block::BlkError> { - Ok(()) - } -} - -fn maping_dev_err_to_blk_err(err: sdmmc::err::SdError) -> rdif_block::BlkError { - match err { - sdmmc::err::SdError::Timeout | sdmmc::err::SdError::DataTimeout => { - // transient timeout, ask caller to retry - rdif_block::BlkError::Retry - } - sdmmc::err::SdError::Crc - | sdmmc::err::SdError::DataCrc - | sdmmc::err::SdError::EndBit - | sdmmc::err::SdError::Index - | sdmmc::err::SdError::DataEndBit - | sdmmc::err::SdError::BadMessage - | sdmmc::err::SdError::InvalidResponse - | sdmmc::err::SdError::InvalidResponseType - | sdmmc::err::SdError::CommandError - | sdmmc::err::SdError::TransferError - | sdmmc::err::SdError::DataError - | sdmmc::err::SdError::CardError(..) => { - // CRC/response/transfer related errors => I/O error - rdif_block::BlkError::Other("SD/MMC I/O error".into()) - } - sdmmc::err::SdError::IoError => rdif_block::BlkError::Other("I/O error".into()), - sdmmc::err::SdError::NoCard | sdmmc::err::SdError::UnsupportedCard => { - // No card or unsupported card — treat as not supported - rdif_block::BlkError::NotSupported - } - sdmmc::err::SdError::BusPower - | sdmmc::err::SdError::Acmd12Error - | sdmmc::err::SdError::AdmaError - | sdmmc::err::SdError::CurrentLimit - | sdmmc::err::SdError::TuningFailed - | sdmmc::err::SdError::VoltageSwitchFailed - | sdmmc::err::SdError::BusWidth => { - rdif_block::BlkError::Other("SD/MMC controller error".into()) - } - sdmmc::err::SdError::InvalidArgument => { - rdif_block::BlkError::Other("Invalid argument".into()) - } - sdmmc::err::SdError::BufferOverflow | sdmmc::err::SdError::MemoryError => { - rdif_block::BlkError::NoMemory - } - } -} - -static CLK_DEV: Once = Once::new(); - -struct ClkDev { - inner: Device, - id: ClockId, -} - -impl emmc::clock::Clk for ClkDev { - fn emmc_get_clk(&self) -> Result { - let g = self.inner.lock().unwrap(); - g.get_rate(self.id) - .map_err(|_| emmc::clock::ClkError::InvalidPeripheralId) - } - - fn emmc_set_clk(&self, rate: u64) -> Result { - let mut g = self.inner.lock().unwrap(); - g.set_rate(self.id, rate) - .map_err(|_| emmc::clock::ClkError::InvalidPeripheralId)?; - g.get_rate(self.id) - .map_err(|_| emmc::clock::ClkError::InvalidPeripheralId) - } -} - -struct Osal {} - -impl sdmmc::Kernel for Osal { - fn sleep(us: u64) { - axklib::time::busy_wait(Duration::from_micros(us)); - } -} - -sdmmc::set_impl!(Osal); diff --git a/os/axvisor/src/driver/mod.rs b/os/axvisor/src/driver/mod.rs deleted file mode 100644 index 2e50ef78a..000000000 --- a/os/axvisor/src/driver/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Axvisor Driver Module -//! -//! This module provides hardware driver support for the Axvisor hypervisor. - -use core::ptr::NonNull; - -use rdrive::probe::OnProbeError; - -mod blk; -mod soc; -// mod serial; - -#[allow(unused)] -fn iomap(base: u64, size: usize) -> Result, OnProbeError> { - axklib::mem::iomap((base as usize).into(), size) - .map(|ptr| unsafe { NonNull::new_unchecked(ptr.as_mut_ptr()) }) - .map_err(|e| OnProbeError::Other(format!("{e}:?").into())) -} diff --git a/os/axvisor/src/driver/serial/mod.rs b/os/axvisor/src/driver/serial/mod.rs deleted file mode 100644 index 30d79ba8c..000000000 --- a/os/axvisor/src/driver/serial/mod.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::boxed::Box; - -use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo}; -use some_serial::{BSerial, ns16550, pl011}; - -use crate::driver::iomap; - -module_driver!( - name: "common serial", - level: ProbeLevel::PreKernel, - priority: ProbePriority::DEFAULT, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["arm,pl011", "snps,dw-apb-uart"], - on_probe: probe - } - ], -); - -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - info!("Probing serial device: {}", info.node.name()); - let base_reg = info - .node - .reg() - .and_then(|mut regs| regs.next()) - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - let mmio_size = base_reg.size.unwrap_or(0x1000); - let mmio_base = iomap(base_reg.address, mmio_size)?; - - let clock_freq = info.node.clock_frequency().unwrap_or(24_000_000); - - let mut serial: Option = None; - for c in info.node.compatibles() { - if c == "arm,pl011" { - serial = Some(Box::new(pl011::Pl011::new(mmio_base, clock_freq))); - break; - } - - if c == "snps,dw-apb-uart" { - serial = Some(Box::new(ns16550::Ns16550::new_mmio(mmio_base, clock_freq))); - break; - } - } - if let Some(s) = serial { - info!("Serial@{:#x} registered successfully", s.base()); - plat_dev.register(s); - } - - Ok(()) -} diff --git a/os/axvisor/src/driver/soc/mod.rs b/os/axvisor/src/driver/soc/mod.rs deleted file mode 100644 index 7b8ca49f8..000000000 --- a/os/axvisor/src/driver/soc/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod rockchip; diff --git a/os/axvisor/src/driver/soc/rockchip/clk/rk3568-clk.rs b/os/axvisor/src/driver/soc/rockchip/clk/rk3568-clk.rs deleted file mode 100644 index ed03517b9..000000000 --- a/os/axvisor/src/driver/soc/rockchip/clk/rk3568-clk.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axklib::mem::iomap; -use rdif_clk::{ClockId, Interface}; -use rdrive::{DriverGeneric, KError}; -use rk3568_clk::CRU; -use rk3568_clk::cru_clksel_con28_bits::*; - -use rdrive::{PlatformDevice, probe::OnProbeError}; -use rdrive::{module_driver, register::FdtInfo}; - -/// 频率常量 -const MHZ: u32 = 1_000_000; -const KHZ: u32 = 1_000; - -use core::convert::Into; -use core::result::Result::{self, *}; -use log::{debug, info, warn}; - -pub struct ClkDriver(CRU); - -pub const EMMC_CLK_ID: usize = 0x7c; - -impl ClkDriver { - pub fn new(cru_address: u64) -> Self { - ClkDriver(CRU::new(cru_address as *mut _)) - } -} - -impl DriverGeneric for ClkDriver { - fn name(&self) -> &str { - "rk3568-clk" - } -} - -impl Interface for ClkDriver { - fn perper_enable(&mut self) { - debug!("perper_enable"); - } - - fn get_rate(&self, id: ClockId) -> Result { - let rate = match id.into() { - EMMC_CLK_ID => { - let con = self.0.cru_clksel_get_cclk_emmc(); - con >> CRU_CLKSEL_CCLK_EMMC_POS - } - _ => { - warn!("Unsupported clock ID: {:?}", id); - Err(KError::InvalidArg { name: "clock_id" })? - } - }; - Ok(rate as u64) - } - - fn set_rate(&mut self, id: ClockId, rate: u64) -> Result<(), KError> { - match id.into() { - EMMC_CLK_ID => { - info!("Setting eMMC clock to {} Hz", rate); - let src_clk = match rate as u32 { - r if r == 24 * MHZ => CRU_CLKSEL_CCLK_EMMC_XIN_SOC0_MUX, - r if r == 52 * MHZ || r == 50 * MHZ => CRU_CLKSEL_CCLK_EMMC_CPL_DIV_50M, - r if r == 100 * MHZ => CRU_CLKSEL_CCLK_EMMC_CPL_DIV_100M, - r if r == 150 * MHZ => CRU_CLKSEL_CCLK_EMMC_GPL_DIV_150M, - r if r == 200 * MHZ => CRU_CLKSEL_CCLK_EMMC_GPL_DIV_200M, - r if r == 400 * KHZ || r == 375 * KHZ => CRU_CLKSEL_CCLK_EMMC_SOC0_375K, - _ => panic!("Unsupported eMMC clock rate: {} Hz", rate), - }; - self.0.cru_clksel_set_cclk_emmc(src_clk); - } - _ => { - warn!("Unsupported clock ID: {:?}", id); - return Err(KError::InvalidArg { name: "clock_id" }); - } - } - Ok(()) - } -} - -module_driver!( - name: "Rockchip CRU", - level: ProbeLevel::PostKernel, - priority: ProbePriority::CLK, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["rockchip,rk3568-cru"], - on_probe: probe - } - ], -); - -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - info!("Probing Rockchip RK3568 Clock..."); - - let cru_reg = info - .node - .regs() - .into_iter() - .next() - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - info!( - "CRU reg: addr={:#x}, size={:#x}", - cru_reg.address as usize, - cru_reg.size.unwrap_or(0) - ); - - let cru_reg_base = iomap( - (cru_reg.address as usize).into(), - cru_reg.size.unwrap_or(0x1000) as usize, - ) - .expect("Failed to iomap CRU"); - - let cru_address = cru_reg_base.as_ptr() as u64; - - debug!("cru address: {:#x}", cru_address); - - let clk = rdif_clk::Clk::new(ClkDriver::new(cru_address)); - - plat_dev.register(clk); - - Ok(()) -} diff --git a/os/axvisor/src/driver/soc/rockchip/clk/rk3588-clk.rs b/os/axvisor/src/driver/soc/rockchip/clk/rk3588-clk.rs deleted file mode 100644 index e6dd0c32b..000000000 --- a/os/axvisor/src/driver/soc/rockchip/clk/rk3588-clk.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use rdrive::{ - DriverGeneric, PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo, -}; -use rk3588_clk::Rk3588Cru; - -use crate::driver::iomap; - -module_driver!( - name: "Rockchip CRU", - level: ProbeLevel::PostKernel, - priority: ProbePriority::CLK, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["rockchip,rk3588-cru"], - on_probe: probe - } - ], -); - -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - let base_reg = info - .node - .reg() - .and_then(|mut regs| regs.next()) - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - let mmio_size = base_reg.size.unwrap_or(0x1000); - - let mmio_base = iomap(base_reg.address, mmio_size)?; - - let cru = Rk3588Cru::new(mmio_base); - let clk = rdif_clk::Clk::new(ClkDrv::new(cru)); - - plat_dev.register(clk); - info!("clk registered successfully"); - Ok(()) -} - -pub struct ClkDrv { - inner: Rk3588Cru, -} - -impl ClkDrv { - pub fn new(cru: Rk3588Cru) -> Self { - cru.init(); - Self { inner: cru } - } -} - -unsafe impl Send for ClkDrv {} - -impl DriverGeneric for ClkDrv { - fn name(&self) -> &str { - "rk3588-clk" - } -} - -impl rdif_clk::Interface for ClkDrv { - fn perper_enable(&mut self) { - // self.inner.npu_gate_enable(gate_id) - } - - fn get_rate(&self, id: rdif_clk::ClockId) -> Result { - let id: usize = id.into(); - let rate = self - .inner - .mmc_get_clk(id as _) - .map_err(|_| rdrive::KError::InvalidArg { name: "id" })?; - Ok(rate as _) - } - - fn set_rate(&mut self, id: rdif_clk::ClockId, rate: u64) -> Result<(), rdrive::KError> { - // todo!() - let id: usize = id.into(); - self.inner - .mmc_set_clk(id as _, rate as _) - .map_err(|_| rdrive::KError::InvalidArg { name: "id" })?; - Ok(()) - } -} diff --git a/os/axvisor/src/driver/soc/rockchip/mod.rs b/os/axvisor/src/driver/soc/rockchip/mod.rs deleted file mode 100644 index 5f7b79641..000000000 --- a/os/axvisor/src/driver/soc/rockchip/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[cfg(all(feature = "rk3588-clk", not(feature = "rk3568-clk")))] -#[path = "clk/rk3588-clk.rs"] -mod clk; - -#[cfg(all(feature = "rk3568-clk", not(feature = "rk3588-clk")))] -#[path = "clk/rk3568-clk.rs"] -mod clk; diff --git a/os/axvisor/src/driver/soc/rockchip/pm.rs b/os/axvisor/src/driver/soc/rockchip/pm.rs deleted file mode 100644 index 9e7256e58..000000000 --- a/os/axvisor/src/driver/soc/rockchip/pm.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo}; -use rockchip_pm::{RkBoard, RockchipPM}; - -use crate::driver::iomap; - -module_driver!( - name: "Rockchip Pm", - level: ProbeLevel::PostKernel, - priority: ProbePriority::CLK, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["rockchip,rk3588-pmu"], - on_probe: probe - } - ], -); - -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - let base_reg = info - .node - .reg() - .and_then(|mut regs| regs.next()) - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - let mmio_size = base_reg.size.unwrap_or(0x1000); - let board = RkBoard::Rk3588; - - let mmio_base = iomap(base_reg.address, mmio_size)?; - - let pm = RockchipPM::new(mmio_base, board); - - plat_dev.register(pm); - info!("Rockchip power manager registered successfully"); - Ok(()) -} diff --git a/os/axvisor/src/hal/arch/aarch64/api.rs b/os/axvisor/src/hal/arch/aarch64/api.rs deleted file mode 100644 index d61652427..000000000 --- a/os/axvisor/src/hal/arch/aarch64/api.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[axvisor_api::api_mod_impl(axvisor_api::arch)] -mod arch_api_impl { - use core::panic; - - use axvisor_api::memory::virt_to_phys; - - extern fn hardware_inject_virtual_interrupt(irq: axvisor_api::vmm::InterruptVector) { - crate::hal::arch::inject_interrupt(irq as _); - } - - extern fn read_vgicd_typer() -> u32 { - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - return gic.typer_raw(); - } - - if let Some(gic) = gic.typed_mut::() { - // Use the GICv3 driver to read the typer register - return gic.typer_raw(); - } - panic!("No GIC driver found"); - } - - extern fn read_vgicd_iidr() -> u32 { - // use axstd::os::arceos::modules::axhal::irq::MyVgic; - // MyVgic::get_gicd().lock().get_iidr() - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - return gic.iidr_raw(); - } - - if let Some(gic) = gic.typed_mut::() { - // Use the GICv3 driver to read the typer register - return gic.iidr_raw(); - } - - panic!("No GIC driver found"); - } - - extern fn get_host_gicd_base() -> memory_addr::PhysAddr { - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - let ptr: *mut u8 = gic.gicd_addr().as_ptr(); - return virt_to_phys((ptr as usize).into()); - } - - if let Some(gic) = gic.typed_mut::() { - let ptr: *mut u8 = gic.gicd_addr().as_ptr(); - // Use the GICv3 driver to read the typer register - return virt_to_phys((ptr as usize).into()); - } - panic!("No GIC driver found"); - } - - extern fn get_host_gicr_base() -> memory_addr::PhysAddr { - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - let ptr: *mut u8 = gic.gicr_addr().as_ptr(); - return virt_to_phys((ptr as usize).into()); - } - panic!("No GICv3 driver found"); - } -} diff --git a/os/axvisor/src/hal/arch/aarch64/cache.rs b/os/axvisor/src/hal/arch/aarch64/cache.rs deleted file mode 100644 index 2407557fa..000000000 --- a/os/axvisor/src/hal/arch/aarch64/cache.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use memory_addr::VirtAddr; - -use crate::hal::CacheOp; - -impl From for aarch64_cpu_ext::cache::CacheOp { - fn from(op: CacheOp) -> Self { - match op { - CacheOp::Clean => aarch64_cpu_ext::cache::CacheOp::Clean, - CacheOp::Invalidate => aarch64_cpu_ext::cache::CacheOp::Invalidate, - CacheOp::CleanAndInvalidate => aarch64_cpu_ext::cache::CacheOp::CleanAndInvalidate, - } - } -} - -pub fn dcache_range(op: CacheOp, addr: VirtAddr, size: usize) { - aarch64_cpu_ext::cache::dcache_range(op.into(), addr.as_usize(), size); -} diff --git a/os/axvisor/src/hal/arch/aarch64/mod.rs b/os/axvisor/src/hal/arch/aarch64/mod.rs deleted file mode 100644 index e78ceb5bd..000000000 --- a/os/axvisor/src/hal/arch/aarch64/mod.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use aarch64_cpu_ext::registers::*; - -mod api; -pub mod cache; - -pub fn inject_interrupt(irq: usize) { - debug!("Injecting virtual interrupt: {irq}"); - - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - use arm_gic_driver::{ - IntId, - v2::{VirtualInterruptConfig, VirtualInterruptState}, - }; - - let gich = gic.hypervisor_interface().expect("Failed to get GICH"); - gich.enable(); - gich.set_virtual_interrupt( - 0, - VirtualInterruptConfig::software( - unsafe { IntId::raw(irq as _) }, - None, - 0, - VirtualInterruptState::Pending, - false, - true, - ), - ); - return; - } - - if let Some(_gic) = gic.typed_mut::() { - inject_interrupt_gic_v3(irq as _); - return; - } - - panic!("no gic driver found") -} - -pub fn inject_interrupt_gic_v3(vector: usize) { - use arm_gic_driver::v3::*; - - debug!("Injecting virtual interrupt: vector={vector}"); - let elsr = ICH_ELRSR_EL2.read(ICH_ELRSR_EL2::STATUS); - let lr_num = ICH_VTR_EL2.read(ICH_VTR_EL2::LISTREGS) as usize + 1; - - let mut free_lr = -1_isize; - - // First, check if this interrupt is already pending/active - for i in 0..lr_num { - // find a free list register - if (1 << i) & elsr > 0 { - if free_lr == -1 { - free_lr = i as isize; - } - continue; - } - let lr_val = ich_lr_el2_get(i); - - if lr_val.read(ICH_LR_EL2::VINTID) == vector as u64 - && lr_val.matches_any(&[ICH_LR_EL2::STATE::Pending, ICH_LR_EL2::STATE::Active]) - { - debug!("Virtual interrupt {vector} already pending/active in LR{i}, skipping"); - // If the interrupt is already pending or active, we can skip injecting it again. - // This is important to avoid duplicate injections. - continue; - } - } - - debug!("use free lr {free_lr} to inject irq {vector}"); - - if free_lr == -1 { - warn!("No free list register to inject IRQ {vector}, checking ICH_HCR_EL2"); - - // Try to find and reuse an inactive LR - for i in 0..lr_num { - let lr_val = ich_lr_el2_get(i); - if lr_val.matches_any(&[ICH_LR_EL2::STATE::Invalid]) { - debug!("Reusing inactive LR{i} for IRQ {vector}"); - free_lr = i as isize; - - break; - } - } - - if free_lr == -1 { - panic!("No free list register to inject IRQ {}", vector); - } - } - - ich_lr_el2_write( - free_lr as _, - ICH_LR_EL2::VINTID.val(vector as u64) + ICH_LR_EL2::STATE::Pending + ICH_LR_EL2::GROUP::SET, - ); - - // Ensure the virtual interrupt interface is enabled - let en = ICH_HCR_EL2.is_set(ICH_HCR_EL2::EN); - if !en { - // Check EN bit - warn!("Virtual interrupt interface not enabled, enabling now"); - ICH_HCR_EL2.modify(ICH_HCR_EL2::EN::SET); - } - - debug!("Virtual interrupt {vector} injected successfully in LR{free_lr}"); -} - -pub fn hardware_check() { - let pa_bits = match ID_AA64MMFR0_EL1.read_as_enum(ID_AA64MMFR0_EL1::PARange) { - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_32) => 32, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_36) => 36, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_40) => 40, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_42) => 42, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_44) => 44, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_48) => 48, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_52) => 52, - _ => 32, - }; - - let level = match pa_bits { - 44.. => 4, - _ => 3, - }; - - #[cfg(feature = "ept-level-4")] - { - if level < 4 { - panic!( - "4-level EPT feature is enabled, but the hardware only supports {}-level page tables. Please disable the 4-level EPT feature or use hardware that supports 4-level page tables.", - level - ); - } - } - #[cfg(not(feature = "ept-level-4"))] - { - if level > 3 { - panic!( - "The hardware supports {}-level page tables, but the 4-level EPT feature is not enabled. Please enable the 4-level EPT feature to utilize the hardware's full capabilities.", - level - ); - } - } -} diff --git a/os/axvisor/src/hal/arch/x86_64/cache.rs b/os/axvisor/src/hal/arch/x86_64/cache.rs deleted file mode 100644 index d8b6985fd..000000000 --- a/os/axvisor/src/hal/arch/x86_64/cache.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use memory_addr::VirtAddr; - -use crate::hal::CacheOp; - -pub fn dcache_range(_op: CacheOp, _addr: VirtAddr, _size: usize) {} diff --git a/os/axvisor/src/hal/arch/x86_64/mod.rs b/os/axvisor/src/hal/arch/x86_64/mod.rs deleted file mode 100644 index 01e0ec5e7..000000000 --- a/os/axvisor/src/hal/arch/x86_64/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub mod cache; - -pub fn hardware_check() {} -pub fn inject_interrupt(_vector: u8) {} diff --git a/os/axvisor/src/hal/mod.rs b/os/axvisor/src/hal/mod.rs deleted file mode 100644 index afc0070b4..000000000 --- a/os/axvisor/src/hal/mod.rs +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::os::arceos::{ - self, - modules::{axhal::percpu::this_cpu_id, axtask}, -}; - -use axerrno::{AxResult, ax_err_type}; -use memory_addr::PAGE_SIZE_4K; -use page_table_multiarch::PagingHandler; - -use arceos::modules::axhal; -use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; -use axvcpu::AxVCpuHal; -use axvm::{AxVMHal, AxVMPerCpu}; - -#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/mod.rs")] -#[cfg_attr(target_arch = "x86_64", path = "arch/x86_64/mod.rs")] -pub mod arch; - -use crate::{hal::arch::hardware_check, task::AsVCpuTask, vmm}; - -#[allow(unused)] -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub enum CacheOp { - /// Write back to memory - Clean, - /// Invalidate cache - Invalidate, - /// Clean and invalidate - CleanAndInvalidate, -} - -/// Implementation for `AxVMHal` trait. -pub struct AxVMHalImpl; - -impl AxVMHal for AxVMHalImpl { - type PagingHandler = axhal::paging::PagingHandlerImpl; - - fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr { - axhal::mem::virt_to_phys(vaddr) - } - - fn current_time_nanos() -> u64 { - axhal::time::monotonic_time_nanos() - } - - fn current_vm_id() -> usize { - axtask::current().as_vcpu_task().vm().id() - } - - fn current_vcpu_id() -> usize { - axtask::current().as_vcpu_task().vcpu.id() - } - - fn current_pcpu_id() -> usize { - axhal::percpu::this_cpu_id() - } - - fn vcpu_resides_on(vm_id: usize, vcpu_id: usize) -> AxResult { - vmm::with_vcpu_task(vm_id, vcpu_id, |task| task.cpu_id() as usize) - .ok_or_else(|| ax_err_type!(NotFound)) - } - - fn inject_irq_to_vcpu(vm_id: usize, vcpu_id: usize, irq: usize) -> AxResult { - vmm::with_vm_and_vcpu_on_pcpu(vm_id, vcpu_id, move |_, vcpu| { - vcpu.inject_interrupt(irq).unwrap(); - }) - } -} - -pub struct AxMmHalImpl; - -impl AxMmHal for AxMmHalImpl { - fn alloc_frame() -> Option { - ::PagingHandler::alloc_frame() - } - - fn dealloc_frame(paddr: HostPhysAddr) { - ::PagingHandler::dealloc_frame(paddr) - } - - #[inline] - fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr { - ::PagingHandler::phys_to_virt(paddr) - } - - fn virt_to_phys(vaddr: axaddrspace::HostVirtAddr) -> axaddrspace::HostPhysAddr { - std::os::arceos::modules::axhal::mem::virt_to_phys(vaddr) - } -} - -pub struct AxVCpuHalImpl; - -impl AxVCpuHal for AxVCpuHalImpl { - type MmHal = AxMmHalImpl; - - fn irq_hanlder() { - axhal::irq::irq_handler(0); - } -} - -#[percpu::def_percpu] -static mut AXVM_PER_CPU: AxVMPerCpu = AxVMPerCpu::::new_uninit(); - -/// Init hardware virtualization support in each core. -pub(crate) fn enable_virtualization() { - use core::sync::atomic::AtomicUsize; - use core::sync::atomic::Ordering; - - use std::thread; - - use arceos::api::task::{AxCpuMask, ax_set_current_affinity}; - - static CORES: AtomicUsize = AtomicUsize::new(0); - - info!("Enabling hardware virtualization support on all cores..."); - - hardware_check(); - - let cpu_count = std::os::arceos::modules::axhal::cpu_num(); - - for cpu_id in 0..cpu_count { - thread::spawn(move || { - info!("Core {cpu_id} is initializing hardware virtualization support..."); - // Initialize cpu affinity here. - assert!( - ax_set_current_affinity(AxCpuMask::one_shot(cpu_id)).is_ok(), - "Initialize CPU affinity failed!" - ); - - info!("Enabling hardware virtualization support on core {cpu_id}"); - - vmm::init_timer_percpu(); - - // SAFETY: We are initializing the percpu state for the first time - #[allow(static_mut_refs)] - let percpu = unsafe { AXVM_PER_CPU.current_ref_mut_raw() }; - percpu - .init(this_cpu_id()) - .expect("Failed to initialize percpu state"); - percpu - .hardware_enable() - .expect("Failed to enable virtualization"); - - info!("Hardware virtualization support enabled on core {cpu_id}"); - - let _ = CORES.fetch_add(1, Ordering::Release); - }); - } - - info!("Waiting for all cores to enable hardware virtualization..."); - - // Wait for all cores to enable virtualization. - while CORES.load(Ordering::Acquire) != cpu_count { - // Use `yield_now` instead of `core::hint::spin_loop` to avoid deadlock. - thread::yield_now(); - } - - info!("All cores have enabled hardware virtualization support."); -} - -#[axvisor_api::api_mod_impl(axvisor_api::memory)] -mod memory_api_impl { - use core::{alloc::Layout, ptr::NonNull}; - - use super::*; - - extern fn alloc_frame() -> Option { - ::alloc_frame() - } - - extern fn alloc_contiguous_frames( - num_frames: usize, - frame_align_pow2: usize, - ) -> Option { - arceos::modules::axalloc::global_allocator() - .alloc( - Layout::from_size_align( - num_frames * PAGE_SIZE_4K, - PAGE_SIZE_4K << frame_align_pow2, - ) - .unwrap(), - ) - // .alloc_pages(num_frames, PAGE_SIZE_4K << frame_align_pow2) - // .map(|vaddr| ::virt_to_phys(vaddr.into())) - .map(|vaddr| HostPhysAddr::from(vaddr.as_ptr() as usize)) - .ok() - } - - extern fn dealloc_frame(paddr: HostPhysAddr) { - ::dealloc_frame(paddr) - } - - extern fn dealloc_contiguous_frames(paddr: HostPhysAddr, num_frames: usize) { - // arceos::modules::axalloc::global_allocator().dealloc_pages(paddr.as_usize(), num_frames); - arceos::modules::axalloc::global_allocator().dealloc( - unsafe { NonNull::new_unchecked(paddr.as_usize() as _) }, - Layout::from_size_align(num_frames * PAGE_SIZE_4K, PAGE_SIZE_4K).unwrap(), - ); - } - - extern fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr { - ::phys_to_virt(paddr) - } - - extern fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr { - ::virt_to_phys(vaddr) - } -} - -#[axvisor_api::api_mod_impl(axvisor_api::time)] -mod time_api_impl { - use super::*; - use axvisor_api::time::{CancelToken, Nanos, Ticks, TimeValue}; - - extern fn current_ticks() -> Ticks { - axhal::time::current_ticks() - } - - extern fn ticks_to_nanos(ticks: Ticks) -> Nanos { - axhal::time::ticks_to_nanos(ticks) - } - - extern fn nanos_to_ticks(nanos: Nanos) -> Ticks { - axhal::time::nanos_to_ticks(nanos) - } - - extern fn register_timer( - deadline: TimeValue, - handler: alloc::boxed::Box, - ) -> CancelToken { - vmm::timer::register_timer(deadline.as_nanos() as u64, handler) - } - - extern fn cancel_timer(token: CancelToken) { - vmm::timer::cancel_timer(token) - } -} - -#[axvisor_api::api_mod_impl(axvisor_api::vmm)] -mod vmm_api_impl { - use super::*; - use axvisor_api::vmm::{InterruptVector, VCpuId, VMId}; - - extern fn current_vm_id() -> usize { - ::current_vm_id() - } - - extern fn current_vcpu_id() -> usize { - ::current_vcpu_id() - } - - extern fn vcpu_num(vm_id: VMId) -> Option { - vmm::with_vm(vm_id, |vm| vm.vcpu_num()) - } - - extern fn active_vcpus(_vm_id: VMId) -> Option { - todo!("active_vcpus") - } - - extern fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector) { - ::inject_irq_to_vcpu(vm_id, vcpu_id, vector as usize).unwrap(); - } - - extern fn notify_vcpu_timer_expired(_vm_id: VMId, _vcpu_id: VCpuId) { - todo!("notify_vcpu_timer_expired") - // vmm::timer::notify_timer_expired(vm_id, vcpu_id); - } -} - -#[axvisor_api::api_mod_impl(axvisor_api::host)] -mod host_api_impl { - extern fn get_host_cpu_num() -> usize { - // std::os::arceos::modules::axconfig::plat::CPU_NUM - std::os::arceos::modules::axhal::cpu_num() - } -} diff --git a/os/axvisor/src/logo.rs b/os/axvisor/src/logo.rs deleted file mode 100644 index 31abaaa57..000000000 --- a/os/axvisor/src/logo.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::os::arceos::api::time::ax_wall_time; -use std::println; - -const LOGO: [&str; 2] = [ - r#" - d8888 888 888 d8b - d88888 888 888 Y8P - d88P888 888 888 - d88P 888 888 888 Y88b d88P 888 .d8888b .d88b. 888d888 - d88P 888 `Y8bd8P' Y88b d88P 888 88K d88""88b 888P" - d88P 888 X88K Y88o88P 888 "Y8888b. 888 888 888 - d8888888888 .d8""8b. Y888P 888 X88 Y88..88P 888 -d88P 888 888 888 Y8P 888 88888P' "Y88P" 888 -"#, - r#" - _ __ ___ - / \ __ _\ \ / (_)___ ___ _ __ - / _ \ \ \/ /\ \ / /| / __|/ _ \| '__| - / ___ \ > < \ V / | \__ \ (_) | | -/_/ \_\/_/\_\ \_/ |_|___/\___/|_| -"#, -]; - -/// Chooses a logo based on the current time. -fn choose_logo() -> &'static str { - let elapsed = ax_wall_time().as_micros() as usize; - LOGO[elapsed % LOGO.len()] -} - -/// Prints the logo to the console. -pub fn print_logo() { - println!(); - println!("{}", choose_logo()); - println!(); - println!("by AxVisor Team"); - println!(); -} diff --git a/os/axvisor/src/main.rs b/os/axvisor/src/main.rs deleted file mode 100644 index f9093226f..000000000 --- a/os/axvisor/src/main.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Axvisor Kernel -//! -//! The main kernel binary for the Axvisor hypervisor. - -#![no_std] -#![no_main] -#![feature(used_with_arg)] -#![cfg(target_os = "none")] - -#[macro_use] -extern crate log; - -#[macro_use] -extern crate alloc; - -extern crate axstd as std; - -#[cfg(target_arch = "x86_64")] -extern crate axplat_x86_qemu_q35; - -mod driver; -mod hal; -mod logo; -mod shell; -mod task; -mod vmm; - -#[unsafe(no_mangle)] -fn main() { - logo::print_logo(); - - info!("Starting virtualization..."); - info!("Hardware support: {:?}", axvm::has_hardware_support()); - hal::enable_virtualization(); - - vmm::init(); - vmm::start(); - - info!("[OK] Default guest initialized"); - - shell::console_init(); -} diff --git a/os/axvisor/src/shell/command/base.rs b/os/axvisor/src/shell/command/base.rs deleted file mode 100644 index 6e302b747..000000000 --- a/os/axvisor/src/shell/command/base.rs +++ /dev/null @@ -1,796 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::collections::BTreeMap; -#[cfg(feature = "fs")] -use std::fs::{self, File, FileType}; -#[cfg(feature = "fs")] -use std::io::{self, Read, Write}; -use std::println; -use std::string::{String, ToString}; - -use crate::shell::command::{CommandNode, FlagDef, ParsedCommand}; - -#[cfg(feature = "fs")] -macro_rules! print_err { - ($cmd: literal, $msg: expr) => { - println!("{}: {}", $cmd, $msg); - }; - ($cmd: literal, $arg: expr, $err: expr) => { - println!("{}: {}: {}", $cmd, $arg, $err); - }; -} - -// Helper function: split whitespace -#[cfg(feature = "fs")] -fn split_whitespace(s: &str) -> (&str, &str) { - let s = s.trim(); - if let Some(pos) = s.find(char::is_whitespace) { - let (first, rest) = s.split_at(pos); - (first, rest.trim()) - } else { - (s, "") - } -} - -#[cfg(feature = "fs")] -fn do_ls(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let show_long = cmd.flags.get("long").unwrap_or(&false); - let show_all = cmd.flags.get("all").unwrap_or(&false); - - let _current_dir = std::env::current_dir().unwrap(); - - fn show_entry_info(path: &str, entry: &str, show_long: bool) -> io::Result<()> { - if show_long { - let metadata = fs::metadata(path)?; - let size = metadata.len(); - let file_type = metadata.file_type(); - let file_type_char = file_type_to_char(file_type); - let rwx = file_perm_to_rwx(metadata.permissions().mode()); - let rwx = unsafe { core::str::from_utf8_unchecked(&rwx) }; - println!("{}{} {:>8} {}", file_type_char, rwx, size, entry); - } else { - println!("{}", entry); - } - Ok(()) - } - - fn list_one(name: &str, print_name: bool, show_long: bool, show_all: bool) -> io::Result<()> { - use std::vec::Vec; - - let is_dir = fs::metadata(name)?.is_dir(); - if !is_dir { - return show_entry_info(name, name, show_long); - } - - if print_name { - println!("{}:", name); - } - - let mut entries = fs::read_dir(name)? - .filter_map(|e| e.ok()) - .map(|e| e.file_name()) - .filter(|name| show_all || !name.starts_with('.')) - .collect::>(); - entries.sort(); - - for entry in entries { - let path = format!("{name}/{entry}"); - if let Err(e) = show_entry_info(&path, &entry, show_long) { - print_err!("ls", path, e); - } - } - Ok(()) - } - - let targets = if args.is_empty() { - vec![".".to_string()] - } else { - args.clone() - }; - - for (i, name) in targets.iter().enumerate() { - if i > 0 { - println!(); - } - if let Err(e) = list_one(name, targets.len() > 1, *show_long, *show_all) { - print_err!("ls", name, e); - } - } -} - -#[cfg(feature = "fs")] -fn do_cat(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.is_empty() { - print_err!("cat", "no file specified"); - return; - } - - fn cat_one(fname: &str) -> io::Result<()> { - let mut buf = [0; 1024]; - let mut file = File::open(fname)?; - loop { - let n = file.read(&mut buf)?; - if n > 0 { - io::stdout().write_all(&buf[..n])?; - } else { - return Ok(()); - } - } - } - - for fname in args { - if let Err(e) = cat_one(fname) { - print_err!("cat", fname, e); - } - } -} - -#[cfg(feature = "fs")] -fn do_echo(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let no_newline = cmd.flags.get("no-newline").unwrap_or(&false); - - let args_str = args.join(" "); - - fn echo_file(fname: &str, text_list: &[&str]) -> io::Result<()> { - let mut file = File::create(fname)?; - for text in text_list { - file.write_all(text.as_bytes())?; - } - Ok(()) - } - - if let Some(pos) = args_str.rfind('>') { - let text_before = args_str[..pos].trim(); - let (fname, text_after) = split_whitespace(&args_str[pos + 1..]); - if fname.is_empty() { - print_err!("echo", "no file specified"); - return; - }; - - let text_list = [ - text_before, - if !text_after.is_empty() { " " } else { "" }, - text_after, - if !no_newline { "\n" } else { "" }, - ]; - if let Err(e) = echo_file(fname, &text_list) { - print_err!("echo", fname, e); - } - } else if *no_newline { - use std::print; - - print!("{}", args_str); - } else { - println!("{}", args_str); - } -} - -#[cfg(feature = "fs")] -fn do_mkdir(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let create_parents = cmd.flags.get("parents").unwrap_or(&false); - - if args.is_empty() { - print_err!("mkdir", "missing operand"); - return; - } - - fn mkdir_one(path: &str, create_parents: bool) -> io::Result<()> { - if create_parents { - fs::create_dir_all(path) - } else { - fs::create_dir(path) - } - } - - for path in args { - if let Err(e) = mkdir_one(path, *create_parents) { - print_err!("mkdir", format_args!("cannot create directory '{path}'"), e); - } - } -} - -#[cfg(feature = "fs")] -fn do_rm(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let rm_dir = cmd.flags.get("dir").unwrap_or(&false); - let recursive = cmd.flags.get("recursive").unwrap_or(&false); - let force = cmd.flags.get("force").unwrap_or(&false); - - if args.is_empty() { - print_err!("rm", "missing operand"); - return; - } - - fn rm_one(path: &str, rm_dir: bool, recursive: bool, force: bool) -> io::Result<()> { - let metadata = fs::metadata(path); - - if force && metadata.is_err() { - return Ok(()); // Ignore non-existent files when in force mode - } - - let metadata = metadata?; - - if metadata.is_dir() { - if recursive { - remove_dir_recursive(path, force) - } else if rm_dir { - fs::remove_dir(path) - } else { - Err(io::Error::Unsupported) - } - } else { - fs::remove_file(path) - } - } - - for path in args { - if let Err(e) = rm_one(path, *rm_dir, *recursive, *force) - && !force - { - print_err!("rm", format_args!("cannot remove '{path}'"), e); - } - } -} - -// Implementation of recursively deleting directories (manual recursion) -#[cfg(feature = "fs")] -fn remove_dir_recursive(path: &str, _force: bool) -> io::Result<()> { - // Read directory contents - let entries = fs::read_dir(path)?; - - // Remove all child items - for entry_result in entries { - let entry = entry_result?; - let entry_path = format!("{}/{}", path, entry.file_name()); - let metadata = entry.file_type(); - - if metadata.is_dir() { - // Recursively delete subdirectory - remove_dir_recursive(&entry_path, _force)?; - } else { - // Delete file - fs::remove_file(&entry_path)?; - } - } - - // Delete empty directory - fs::remove_dir(path) -} - -#[cfg(feature = "fs")] -fn do_cd(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - let target = if args.is_empty() { - "/" - } else if args.len() == 1 { - &args[0] - } else { - print_err!("cd", "too many arguments"); - return; - }; - - if let Err(e) = std::env::set_current_dir(target) { - print_err!("cd", target, e); - } -} - -#[cfg(feature = "fs")] -fn do_pwd(cmd: &ParsedCommand) { - let _logical = cmd.flags.get("logical").unwrap_or(&false); - - let pwd = std::env::current_dir().unwrap(); - println!("{}", pwd); -} - -fn do_uname(cmd: &ParsedCommand) { - let show_all = cmd.flags.get("all").unwrap_or(&false); - let show_kernel = cmd.flags.get("kernel-name").unwrap_or(&false); - let show_arch = cmd.flags.get("machine").unwrap_or(&false); - - let arch = option_env!("AX_ARCH").unwrap_or(""); - let platform = option_env!("AX_PLATFORM").unwrap_or(""); - let smp = match option_env!("AX_SMP") { - None | Some("1") => "", - _ => " SMP", - }; - let version = option_env!("CARGO_PKG_VERSION").unwrap_or("0.1.0"); - - if *show_all { - println!( - "ArceOS {ver}{smp} {arch} {plat}", - ver = version, - smp = smp, - arch = arch, - plat = platform, - ); - } else if *show_kernel { - println!("ArceOS"); - } else if *show_arch { - println!("{}", arch); - } else { - println!( - "ArceOS {ver}{smp} {arch} {plat}", - ver = version, - smp = smp, - arch = arch, - plat = platform, - ); - } -} - -fn do_exit(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let exit_code = if args.is_empty() { - 0 - } else { - args[0].parse::().unwrap_or(0) - }; - - println!("Bye~"); - std::process::exit(exit_code); -} - -fn do_log(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.is_empty() { - println!("Current log level: {:?}", log::max_level()); - return; - } - - match args[0].as_str() { - "on" | "enable" => log::set_max_level(log::LevelFilter::Info), - "off" | "disable" => log::set_max_level(log::LevelFilter::Off), - "error" => log::set_max_level(log::LevelFilter::Error), - "warn" => log::set_max_level(log::LevelFilter::Warn), - "info" => log::set_max_level(log::LevelFilter::Info), - "debug" => log::set_max_level(log::LevelFilter::Debug), - "trace" => log::set_max_level(log::LevelFilter::Trace), - level => { - println!("Unknown log level: {}", level); - println!("Available levels: off, error, warn, info, debug, trace"); - return; - } - } - println!("Log level set to: {:?}", log::max_level()); -} - -#[cfg(feature = "fs")] -fn do_mv(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.len() < 2 { - print_err!("mv", "missing operand"); - return; - } - - // If only two arguments, handle single file/dir move - if args.len() == 2 { - let source = &args[0]; - let dest = &args[1]; - - // Check if destination exists and is a directory - if let Ok(dest_meta) = fs::metadata(dest) - && dest_meta.is_dir() - { - // Move source into destination directory - let mut file_dir = fs::read_dir(dest).unwrap(); - let source_name = match file_dir.next() { - Some(name) => { - let dir_name = name.expect("Failed to read directory"); - let file = dir_name.file_name(); - format!("{dest}/{file}") - } - None => { - print_err!("mv", format_args!("invalid source path '{source}'")); - return; - } - }; - let dest_path = format!("{dest}/{source_name}"); - if let Err(e) = move_file_or_dir(source, &dest_path) { - print_err!( - "mv", - format_args!("cannot move '{source}' to '{dest_path}'"), - e - ); - } - return; - } - - // Direct rename/move - if let Err(e) = move_file_or_dir(source, dest) { - print_err!("mv", format_args!("cannot move '{source}' to '{dest}'"), e); - } - } else { - // Multiple sources - destination must be a directory - let dest = &args[args.len() - 1]; - let sources = &args[..args.len() - 1]; - - // Check if destination is a directory - match fs::metadata(dest) { - Ok(meta) if meta.is_dir() => { - // Move each source into destination directory - for source in sources { - let mut file_dir = fs::read_dir(source).unwrap(); - let source_name = match file_dir.next() { - Some(name) => { - let dir_name = name.expect("Failed to read directory"); - let file = dir_name.file_name(); - format!("{dest}/{file}") - } - None => { - print_err!("mv", format_args!("invalid source path '{source}'")); - return; - } - }; - let dest_path = format!("{dest}/{source_name}"); - if let Err(e) = move_file_or_dir(source, &dest_path) { - print_err!( - "mv", - format_args!("cannot move '{source}' to '{dest_path}'"), - e - ); - } - } - } - Ok(_) => { - print_err!("mv", format_args!("target '{dest}' is not a directory")); - } - Err(e) => { - print_err!("mv", format_args!("cannot access '{dest}'"), e); - } - } - } -} - -// Helper function to move file or directory (handles cross-filesystem moves) -#[cfg(feature = "fs")] -fn move_file_or_dir(source: &str, dest: &str) -> io::Result<()> { - // Try simple rename first (works within same filesystem) - match fs::rename(source, dest) { - Ok(()) => Ok(()), - Err(_) => { - // If rename fails, try copy + delete (for cross-filesystem moves) - let src_meta = fs::metadata(source)?; - - if src_meta.is_dir() { - // For directories, use recursive copy then remove - copy_dir_recursive(source, dest)?; - remove_dir_recursive(source, false)?; - } else { - // For files, copy then remove - copy_file(source, dest)?; - fs::remove_file(source)?; - } - Ok(()) - } - } -} - -#[cfg(feature = "fs")] -fn do_touch(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.is_empty() { - print_err!("touch", "missing operand"); - return; - } - - for filename in args { - if let Err(e) = File::create(filename) { - print_err!("touch", filename, e); - } - } -} - -#[cfg(feature = "fs")] -fn do_cp(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let recursive = cmd.flags.get("recursive").unwrap_or(&false); - - if args.len() < 2 { - print_err!("cp", "missing operand"); - return; - } - - let source = &args[0]; - let dest = &args[1]; - - // Check if source file/directory exists - let src_metadata = match fs::metadata(source) { - Ok(metadata) => metadata, - Err(e) => { - print_err!("cp", format_args!("cannot access '{source}'"), e); - return; - } - }; - - let result = if src_metadata.is_dir() { - if *recursive { - copy_dir_recursive(source, dest) - } else { - Err(io::Error::Unsupported) - } - } else { - copy_file(source, dest) - }; - - if let Err(e) = result { - print_err!("cp", format_args!("cannot copy '{source}' to '{dest}'"), e); - } -} - -// Manually implement file copy -#[cfg(feature = "fs")] -fn copy_file(src: &str, dst: &str) -> io::Result<()> { - let mut src_file = File::open(src)?; - let mut dst_file = File::create(dst)?; - - let mut buffer = [0; 4096]; - loop { - let bytes_read = src_file.read(&mut buffer)?; - if bytes_read == 0 { - break; - } - dst_file.write_all(&buffer[..bytes_read])?; - } - Ok(()) -} - -// Recursively copy directory -#[cfg(feature = "fs")] -fn copy_dir_recursive(src: &str, dst: &str) -> io::Result<()> { - // Create target directory - fs::create_dir(dst)?; - - // Read source directory contents - let entries = fs::read_dir(src)?; - - for entry_result in entries { - let entry = entry_result?; - let file_name = entry.file_name(); - let src_path = format!("{src}/{file_name}"); - let dst_path = format!("{dst}/{file_name}"); - - let metadata = entry.file_type(); - if metadata.is_dir() { - copy_dir_recursive(&src_path, &dst_path)?; - } else { - copy_file(&src_path, &dst_path)?; - } - } - Ok(()) -} - -#[cfg(feature = "fs")] -fn file_type_to_char(ty: FileType) -> char { - if ty.is_char_device() { - 'c' - } else if ty.is_block_device() { - 'b' - } else if ty.is_socket() { - 's' - } else if ty.is_fifo() { - 'p' - } else if ty.is_symlink() { - 'l' - } else if ty.is_dir() { - 'd' - } else if ty.is_file() { - '-' - } else { - '?' - } -} - -#[rustfmt::skip] -#[cfg(feature = "fs")] -const fn file_perm_to_rwx(mode: u32) -> [u8; 9] { - let mut perm = [b'-'; 9]; - macro_rules! set { - ($bit:literal, $rwx:literal) => { - if mode & (1 << $bit) != 0 { - perm[8 - $bit] = $rwx - } - }; - } - - set!(2, b'r'); set!(1, b'w'); set!(0, b'x'); - set!(5, b'r'); set!(4, b'w'); set!(3, b'x'); - set!(8, b'r'); set!(7, b'w'); set!(6, b'x'); - perm -} - -pub fn build_base_cmd(tree: &mut BTreeMap) { - // ls Command - #[cfg(feature = "fs")] - tree.insert( - "ls".to_string(), - CommandNode::new("List directory contents") - .with_handler(do_ls) - .with_usage("ls [OPTIONS] [DIRECTORY...]") - .with_flag( - FlagDef::new("long", "Use long listing format") - .with_short('l') - .with_long("long"), - ) - .with_flag( - FlagDef::new("all", "Show hidden files") - .with_short('a') - .with_long("all"), - ), - ); - - // cat Command - #[cfg(feature = "fs")] - tree.insert( - "cat".to_string(), - CommandNode::new("Display file contents") - .with_handler(do_cat) - .with_usage("cat [FILE2...]"), - ); - - // echo Command - #[cfg(feature = "fs")] - tree.insert( - "echo".to_string(), - CommandNode::new("Display text") - .with_handler(do_echo) - .with_usage("echo [OPTIONS] [TEXT...]") - .with_flag( - FlagDef::new("no-newline", "Do not output trailing newline") - .with_short('n') - .with_long("no-newline"), - ), - ); - - // mkdir Command - #[cfg(feature = "fs")] - tree.insert( - "mkdir".to_string(), - CommandNode::new("Create directories") - .with_handler(do_mkdir) - .with_usage("mkdir [OPTIONS] [DIRECTORY2...]") - .with_flag( - FlagDef::new("parents", "Create parent directories as needed") - .with_short('p') - .with_long("parents"), - ), - ); - - // rm Command - #[cfg(feature = "fs")] - tree.insert( - "rm".to_string(), - CommandNode::new("Remove files and directories") - .with_handler(do_rm) - .with_usage("rm [OPTIONS] [FILE2...]") - .with_flag( - FlagDef::new("dir", "Remove empty directories") - .with_short('d') - .with_long("dir"), - ) - .with_flag( - FlagDef::new("recursive", "Remove directories recursively") - .with_short('r') - .with_long("recursive"), - ) - .with_flag( - FlagDef::new("force", "Force removal, ignore nonexistent files") - .with_short('f') - .with_long("force"), - ), - ); - - // cd Command - #[cfg(feature = "fs")] - tree.insert( - "cd".to_string(), - CommandNode::new("Change directory") - .with_handler(do_cd) - .with_usage("cd [DIRECTORY]"), - ); - - // pwd Command - #[cfg(feature = "fs")] - tree.insert( - "pwd".to_string(), - CommandNode::new("Print working directory") - .with_handler(do_pwd) - .with_usage("pwd [OPTIONS]") - .with_flag( - FlagDef::new("logical", "Use logical path") - .with_short('L') - .with_long("logical"), - ), - ); - - // uname Command - tree.insert( - "uname".to_string(), - CommandNode::new("System information") - .with_handler(do_uname) - .with_usage("uname [OPTIONS]") - .with_flag( - FlagDef::new("all", "Show all information") - .with_short('a') - .with_long("all"), - ) - .with_flag( - FlagDef::new("kernel-name", "Show kernel name") - .with_short('s') - .with_long("kernel-name"), - ) - .with_flag( - FlagDef::new("machine", "Show machine architecture") - .with_short('m') - .with_long("machine"), - ), - ); - - // exit Command - tree.insert( - "exit".to_string(), - CommandNode::new("Exit the shell") - .with_handler(do_exit) - .with_usage("exit [EXIT_CODE]"), - ); - - // log Command - tree.insert( - "log".to_string(), - CommandNode::new("Change log level") - .with_handler(do_log) - .with_usage("log [LEVEL]"), - ); - - // touch Command - #[cfg(feature = "fs")] - tree.insert( - "touch".to_string(), - CommandNode::new("Create empty files") - .with_handler(do_touch) - .with_usage("touch [FILE2...]"), - ); - - // cp Command - #[cfg(feature = "fs")] - tree.insert( - "cp".to_string(), - CommandNode::new("Copy files") - .with_handler(do_cp) - .with_usage("cp [OPTIONS] ") - .with_flag( - FlagDef::new("recursive", "Copy directories recursively") - .with_short('r') - .with_long("recursive"), - ), - ); - - // mv Command - #[cfg(feature = "fs")] - tree.insert( - "mv".to_string(), - CommandNode::new("Move/rename files") - .with_handler(do_mv) - .with_usage("mv | mv [SOURCE2...] "), - ); -} diff --git a/os/axvisor/src/shell/command/history.rs b/os/axvisor/src/shell/command/history.rs deleted file mode 100644 index 37257d394..000000000 --- a/os/axvisor/src/shell/command/history.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::io::prelude::*; -use std::{string::String, vec::Vec}; - -pub struct CommandHistory { - history: Vec, - current_index: usize, - max_size: usize, -} - -impl CommandHistory { - pub fn new(max_size: usize) -> Self { - Self { - history: Vec::new(), - current_index: 0, - max_size, - } - } - - pub fn add_command(&mut self, cmd: String) { - if !cmd.trim().is_empty() && self.history.last() != Some(&cmd) { - if self.history.len() >= self.max_size { - self.history.remove(0); - } - self.history.push(cmd); - } - self.current_index = self.history.len(); - } - - #[allow(dead_code)] - pub fn previous(&mut self) -> Option<&String> { - if self.current_index > 0 { - self.current_index -= 1; - self.history.get(self.current_index) - } else { - None - } - } - - #[allow(dead_code)] - pub fn next(&mut self) -> Option<&String> { - if self.current_index < self.history.len() { - self.current_index += 1; - if self.current_index < self.history.len() { - self.history.get(self.current_index) - } else { - None - } - } else { - None - } - } -} - -#[allow(unused_must_use)] -pub fn clear_line_and_redraw( - stdout: &mut dyn Write, - prompt: &str, - content: &str, - cursor_pos: usize, -) { - write!(stdout, "\r"); - write!(stdout, "\x1b[2K"); - write!(stdout, "{prompt}{content}"); - if cursor_pos < content.len() { - write!(stdout, "\x1b[{}D", content.len() - cursor_pos); - } - stdout.flush(); -} diff --git a/os/axvisor/src/shell/command/mod.rs b/os/axvisor/src/shell/command/mod.rs deleted file mode 100644 index 969d8fc67..000000000 --- a/os/axvisor/src/shell/command/mod.rs +++ /dev/null @@ -1,580 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod base; -mod history; -mod vm; - -pub use base::*; -pub use history::*; -pub use vm::*; - -use std::io::prelude::*; -use std::string::String; -use std::vec::Vec; -use std::{collections::BTreeMap, string::ToString}; -use std::{print, println}; - -lazy_static::lazy_static! { - pub static ref COMMAND_TREE: BTreeMap = build_command_tree(); -} - -#[derive(Debug, Clone)] -pub struct CommandNode { - handler: Option, - subcommands: BTreeMap, - description: &'static str, - usage: Option<&'static str>, - #[allow(dead_code)] - log_level: log::LevelFilter, - options: Vec, - flags: Vec, -} - -#[derive(Debug, Clone)] -pub struct OptionDef { - name: &'static str, - short: Option, - long: Option<&'static str>, - description: &'static str, - required: bool, -} - -#[derive(Debug, Clone)] -pub struct FlagDef { - name: &'static str, - short: Option, - long: Option<&'static str>, - description: &'static str, -} - -#[derive(Debug, Clone)] -pub struct ParsedCommand { - pub command_path: Vec, - pub options: BTreeMap, - pub flags: BTreeMap, - pub positional_args: Vec, -} - -#[derive(Debug)] -pub enum ParseError { - UnknownCommand(String), - UnknownOption(String), - MissingValue(String), - MissingRequiredOption(String), - NoHandler(String), -} - -impl CommandNode { - pub fn new(description: &'static str) -> Self { - Self { - handler: None, - subcommands: BTreeMap::new(), - description, - usage: None, - log_level: log::LevelFilter::Off, - options: Vec::new(), - flags: Vec::new(), - } - } - - pub fn with_handler(mut self, handler: fn(&ParsedCommand)) -> Self { - self.handler = Some(handler); - self - } - - pub fn with_usage(mut self, usage: &'static str) -> Self { - self.usage = Some(usage); - self - } - - #[allow(dead_code)] - pub fn with_log_level(mut self, level: log::LevelFilter) -> Self { - self.log_level = level; - self - } - - pub fn with_option(mut self, option: OptionDef) -> Self { - self.options.push(option); - self - } - - pub fn with_flag(mut self, flag: FlagDef) -> Self { - self.flags.push(flag); - self - } - - pub fn add_subcommand>(mut self, name: S, node: CommandNode) -> Self { - self.subcommands.insert(name.into(), node); - self - } -} - -impl OptionDef { - pub fn new(name: &'static str, description: &'static str) -> Self { - Self { - name, - short: None, - long: None, - description, - required: false, - } - } - - #[allow(dead_code)] - pub fn with_short(mut self, short: char) -> Self { - self.short = Some(short); - self - } - - pub fn with_long(mut self, long: &'static str) -> Self { - self.long = Some(long); - self - } - - #[allow(dead_code)] - pub fn required(mut self) -> Self { - self.required = true; - self - } -} - -impl FlagDef { - pub fn new(name: &'static str, description: &'static str) -> Self { - Self { - name, - short: None, - long: None, - description, - } - } - - pub fn with_short(mut self, short: char) -> Self { - self.short = Some(short); - self - } - - pub fn with_long(mut self, long: &'static str) -> Self { - self.long = Some(long); - self - } -} - -// Command Parser -pub struct CommandParser; - -impl CommandParser { - pub fn parse(input: &str) -> Result { - let tokens = Self::tokenize(input); - if tokens.is_empty() { - return Err(ParseError::UnknownCommand("empty command".to_string())); - } - - // Find the command path - let (command_path, command_node, remaining_tokens) = Self::find_command(&tokens)?; - - // Parse the arguments - let (options, flags, positional_args) = Self::parse_args(remaining_tokens, command_node)?; - - // Validate required options - Self::validate_required_options(command_node, &options)?; - - Ok(ParsedCommand { - command_path, - options, - flags, - positional_args, - }) - } - - fn tokenize(input: &str) -> Vec { - let mut tokens = Vec::new(); - let mut current_token = String::new(); - let mut in_quotes = false; - let mut escape_next = false; - - for ch in input.chars() { - if escape_next { - current_token.push(ch); - escape_next = false; - } else if ch == '\\' { - escape_next = true; - } else if ch == '"' { - in_quotes = !in_quotes; - } else if ch.is_whitespace() && !in_quotes { - if !current_token.is_empty() { - tokens.push(current_token.clone()); - current_token.clear(); - } - } else { - current_token.push(ch); - } - } - - if !current_token.is_empty() { - tokens.push(current_token); - } - - tokens - } - - fn find_command( - tokens: &[String], - ) -> Result<(Vec, &CommandNode, &[String]), ParseError> { - let mut current_node = COMMAND_TREE - .get(&tokens[0]) - .ok_or_else(|| ParseError::UnknownCommand(tokens[0].clone()))?; - - let mut command_path = vec![tokens[0].clone()]; - let mut token_index = 1; - - // Traverse to find the deepest command node - while token_index < tokens.len() { - if let Some(subcommand) = current_node.subcommands.get(&tokens[token_index]) { - current_node = subcommand; - command_path.push(tokens[token_index].clone()); - token_index += 1; - } else { - break; - } - } - - Ok((command_path, current_node, &tokens[token_index..])) - } - - #[allow(clippy::type_complexity)] - fn parse_args( - tokens: &[String], - command_node: &CommandNode, - ) -> Result< - ( - BTreeMap, - BTreeMap, - Vec, - ), - ParseError, - > { - let mut options = BTreeMap::new(); - let mut flags = BTreeMap::new(); - let mut positional_args = Vec::new(); - let mut i = 0; - - while i < tokens.len() { - let token = &tokens[i]; - - if let Some(name) = token.strip_prefix("--") { - // Long options/flags - if let Some(eq_pos) = name.find('=') { - // --option=value format - let (opt_name, value) = name.split_at(eq_pos); - let value = &value[1..]; // Skip '=' - if Self::is_option(opt_name, command_node) { - options.insert(opt_name.to_string(), value.to_string()); - } else { - return Err(ParseError::UnknownOption(format!("--{opt_name}"))); - } - } else if Self::is_flag(name, command_node) { - flags.insert(name.to_string(), true); - } else if Self::is_option(name, command_node) { - // --option value format - if i + 1 >= tokens.len() { - return Err(ParseError::MissingValue(format!("--{name}"))); - } - options.insert(name.to_string(), tokens[i + 1].clone()); - i += 1; // Skip value - } else { - return Err(ParseError::UnknownOption(format!("--{name}"))); - } - } else if token.starts_with('-') && token.len() > 1 { - // Short options/flags - let chars: Vec = token[1..].chars().collect(); - for (j, &ch) in chars.iter().enumerate() { - if Self::is_short_flag(ch, command_node) { - flags.insert( - Self::get_flag_name_by_short(ch, command_node) - .unwrap() - .to_string(), - true, - ); - } else if Self::is_short_option(ch, command_node) { - let opt_name = Self::get_option_name_by_short(ch, command_node).unwrap(); - if j == chars.len() - 1 && i + 1 < tokens.len() { - // Last character and there is a next token as value - options.insert(opt_name.to_string(), tokens[i + 1].clone()); - i += 1; // Skip value - } else { - return Err(ParseError::MissingValue(format!("-{ch}"))); - } - } else { - return Err(ParseError::UnknownOption(format!("-{ch}"))); - } - } - } else { - // Positional arguments - positional_args.push(token.clone()); - } - i += 1; - } - - Ok((options, flags, positional_args)) - } - - fn is_option(name: &str, node: &CommandNode) -> bool { - node.options - .iter() - .any(|opt| (opt.long == Some(name)) || opt.name == name) - } - - fn is_flag(name: &str, node: &CommandNode) -> bool { - node.flags - .iter() - .any(|flag| (flag.long == Some(name)) || flag.name == name) - } - - fn is_short_option(ch: char, node: &CommandNode) -> bool { - node.options.iter().any(|opt| opt.short == Some(ch)) - } - - fn is_short_flag(ch: char, node: &CommandNode) -> bool { - node.flags.iter().any(|flag| flag.short == Some(ch)) - } - - fn get_option_name_by_short(ch: char, node: &CommandNode) -> Option<&str> { - node.options - .iter() - .find(|opt| opt.short == Some(ch)) - .map(|opt| opt.name) - } - - fn get_flag_name_by_short(ch: char, node: &CommandNode) -> Option<&str> { - node.flags - .iter() - .find(|flag| flag.short == Some(ch)) - .map(|flag| flag.name) - } - - fn validate_required_options( - node: &CommandNode, - options: &BTreeMap, - ) -> Result<(), ParseError> { - for option in &node.options { - if option.required && !options.contains_key(option.name) { - return Err(ParseError::MissingRequiredOption(option.name.to_string())); - } - } - Ok(()) - } -} - -// Command execution function -pub fn execute_command(input: &str) -> Result<(), ParseError> { - let parsed = CommandParser::parse(input)?; - - // Find the corresponding command node - let mut current_node = COMMAND_TREE.get(&parsed.command_path[0]).unwrap(); - for cmd in &parsed.command_path[1..] { - current_node = current_node.subcommands.get(cmd).unwrap(); - } - - // Execute the command - if let Some(handler) = current_node.handler { - handler(&parsed); - Ok(()) - } else { - Err(ParseError::NoHandler(parsed.command_path.join(" "))) - } -} - -// Build command tree -fn build_command_tree() -> BTreeMap { - let mut tree = BTreeMap::new(); - - build_base_cmd(&mut tree); - build_vm_cmd(&mut tree); - - tree -} - -// Helper function: Display command help -pub fn show_help(command_path: &[String]) -> Result<(), ParseError> { - let mut current_node = COMMAND_TREE - .get(&command_path[0]) - .ok_or_else(|| ParseError::UnknownCommand(command_path[0].clone()))?; - - for cmd in &command_path[1..] { - current_node = current_node - .subcommands - .get(cmd) - .ok_or_else(|| ParseError::UnknownCommand(cmd.clone()))?; - } - - println!("Command: {}", command_path.join(" ")); - println!("Description: {}", current_node.description); - - if let Some(usage) = current_node.usage { - println!("Usage: {}", usage); - } - - if !current_node.options.is_empty() { - println!("\nOptions:"); - for option in ¤t_node.options { - let mut opt_str = String::new(); - if let Some(short) = option.short { - opt_str.push_str(&format!("-{short}")); - } - if let Some(long) = option.long { - if !opt_str.is_empty() { - opt_str.push_str(", "); - } - opt_str.push_str(&format!("--{long}")); - } - if opt_str.is_empty() { - opt_str = option.name.to_string(); - } - - let required_str = if option.required { " (required)" } else { "" }; - println!(" {:<20} {}{}", opt_str, option.description, required_str); - } - } - - if !current_node.flags.is_empty() { - println!("\nFlags:"); - for flag in ¤t_node.flags { - let mut flag_str = String::new(); - if let Some(short) = flag.short { - flag_str.push_str(&format!("-{short}")); - } - if let Some(long) = flag.long { - if !flag_str.is_empty() { - flag_str.push_str(", "); - } - flag_str.push_str(&format!("--{long}")); - } - if flag_str.is_empty() { - flag_str = flag.name.to_string(); - } - - println!(" {:<20} {}", flag_str, flag.description); - } - } - - if !current_node.subcommands.is_empty() { - println!("\nSubcommands:"); - for (name, node) in ¤t_node.subcommands { - println!(" {:<20} {}", name, node.description); - } - } - - Ok(()) -} - -pub fn print_prompt() { - #[cfg(feature = "fs")] - print!("axvisor:{}$ ", std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - print!("axvisor:$ "); - std::io::stdout().flush().unwrap(); -} - -pub fn run_cmd_bytes(cmd_bytes: &[u8]) { - match str::from_utf8(cmd_bytes) { - Ok(cmd_str) => { - let trimmed = cmd_str.trim(); - if trimmed.is_empty() { - return; - } - - match execute_command(trimmed) { - Ok(_) => { - // Command executed successfully - } - Err(ParseError::UnknownCommand(cmd)) => { - println!("Error: Unknown command '{}'", cmd); - println!("Type 'help' to see available commands"); - } - Err(ParseError::UnknownOption(opt)) => { - println!("Error: Unknown option '{}'", opt); - } - Err(ParseError::MissingValue(opt)) => { - println!("Error: Option '{}' is missing a value", opt); - } - Err(ParseError::MissingRequiredOption(opt)) => { - println!("Error: Missing required option '{}'", opt); - } - Err(ParseError::NoHandler(cmd)) => { - println!("Error: Command '{}' has no handler function", cmd); - } - } - } - Err(_) => { - println!("Error: Input contains invalid UTF-8 characters"); - } - } -} - -// Built-in command handler -pub fn handle_builtin_commands(input: &str) -> bool { - match input.trim() { - "help" => { - show_available_commands(); - true - } - "exit" | "quit" => { - println!("Goodbye!"); - std::process::exit(0); - } - "clear" => { - print!("\x1b[2J\x1b[H"); // ANSI clear screen sequence - std::io::stdout().flush().unwrap(); - true - } - _ if input.starts_with("help ") => { - let cmd_parts: Vec = input[5..] - .split_whitespace() - .map(|s| s.to_string()) - .collect(); - if let Err(e) = show_help(&cmd_parts) { - println!("Error: {:?}", e); - } - true - } - _ => false, - } -} - -pub fn show_available_commands() { - println!("ArceOS Shell - Available Commands:"); - println!(); - - // Display all top-level commands - for (name, node) in COMMAND_TREE.iter() { - println!(" {:<15} {}", name, node.description); - - // Display subcommands - if !node.subcommands.is_empty() { - for (sub_name, sub_node) in &node.subcommands { - println!(" {:<13} {}", sub_name, sub_node.description); - } - } - } - - println!(); - println!("Built-in Commands:"); - println!(" help Show help information"); - println!(" help Show help for a specific command"); - println!(" clear Clear the screen"); - println!(" exit/quit Exit the shell"); - println!(); - println!("Tip: Use 'help ' to see detailed usage of a command"); -} diff --git a/os/axvisor/src/shell/command/vm.rs b/os/axvisor/src/shell/command/vm.rs deleted file mode 100644 index c3d827405..000000000 --- a/os/axvisor/src/shell/command/vm.rs +++ /dev/null @@ -1,1409 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::{ - collections::btree_map::BTreeMap, - println, - string::{String, ToString}, - vec::Vec, -}; - -use axvm::VMStatus; -#[cfg(feature = "fs")] -use std::fs::read_to_string; - -use crate::{ - shell::command::{CommandNode, FlagDef, OptionDef, ParsedCommand}, - vmm::{add_running_vm_count, vcpus, vm_list, with_vm}, -}; - -/// Check if a VM can transition to Running state. -/// Returns Ok(()) if the transition is valid, Err with a message otherwise. -fn can_start_vm(status: VMStatus) -> Result<(), &'static str> { - match status { - VMStatus::Loaded | VMStatus::Stopped => Ok(()), - VMStatus::Running => Err("VM is already running"), - VMStatus::Suspended => Err("VM is suspended, use 'vm resume' instead"), - VMStatus::Stopping => Err("VM is stopping, wait for it to fully stop"), - VMStatus::Loading => Err("VM is still loading"), - } -} - -/// Check if a VM can transition to Stopping state. -/// Returns Ok(()) if the transition is valid, Err with a message otherwise. -fn can_stop_vm(status: VMStatus, force: bool) -> Result<(), &'static str> { - match status { - VMStatus::Running | VMStatus::Suspended => Ok(()), - VMStatus::Stopping => { - if force { - Ok(()) - } else { - Err("VM is already stopping") - } - } - VMStatus::Stopped => Err("VM is already stopped"), - VMStatus::Loading | VMStatus::Loaded => Ok(()), // Allow stopping VMs in these states - } -} - -/// Check if a VM can be suspended. -fn can_suspend_vm(status: VMStatus) -> Result<(), &'static str> { - match status { - VMStatus::Running => Ok(()), - VMStatus::Suspended => Err("VM is already suspended"), - VMStatus::Stopped => Err("VM is stopped, cannot suspend"), - VMStatus::Stopping => Err("VM is stopping, cannot suspend"), - VMStatus::Loading => Err("VM is loading, cannot suspend"), - VMStatus::Loaded => Err("VM is not running, cannot suspend"), - } -} - -/// Check if a VM can be resumed. -fn can_resume_vm(status: VMStatus) -> Result<(), &'static str> { - match status { - VMStatus::Suspended => Ok(()), - VMStatus::Running => Err("VM is already running"), - VMStatus::Stopped => Err("VM is stopped, use 'vm start' instead"), - VMStatus::Stopping => Err("VM is stopping, cannot resume"), - VMStatus::Loading => Err("VM is loading, cannot resume"), - VMStatus::Loaded => Err("VM is not started yet, use 'vm start' instead"), - } -} - -/// Format memory size in a human-readable way. -fn format_memory_size(bytes: usize) -> String { - if bytes < 1024 { - format!("{}B", bytes) - } else if bytes < 1024 * 1024 { - format!("{}KB", bytes / 1024) - } else if bytes < 1024 * 1024 * 1024 { - format!("{}MB", bytes / (1024 * 1024)) - } else { - format!("{}GB", bytes / (1024 * 1024 * 1024)) - } -} - -// ============================================================================ -// Command Handlers -// ============================================================================ - -fn vm_help(_cmd: &ParsedCommand) { - println!("VM - virtual machine management"); - println!(); - println!("Most commonly used vm commands:"); - println!(" create Create a new virtual machine"); - println!(" start Start a virtual machine"); - println!(" stop Stop a virtual machine"); - println!(" suspend Suspend (pause) a running virtual machine"); - println!(" resume Resume a suspended virtual machine"); - println!(" restart Restart a virtual machine"); - println!(" delete Delete a virtual machine"); - println!(); - println!("Information commands:"); - println!(" list Show table of all VMs"); - println!(" show Show VM details (requires VM_ID)"); - println!(" - Default: basic information"); - println!(" - --full: complete detailed information"); - println!(" - --config: show configuration"); - println!(" - --stats: show statistics"); - println!(); - println!("Use 'vm --help' for more information on a specific command."); -} - -#[cfg(feature = "fs")] -fn vm_create(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - println!("Positional args: {:?}", args); - - if args.is_empty() { - println!("Error: No VM configuration file specified"); - println!("Usage: vm create [CONFIG_FILE]"); - return; - } - - let initial_vm_count = vm_list::get_vm_list().len(); - - for config_path in args.iter() { - println!("Creating VM from config: {}", config_path); - - use crate::vmm::config::init_guest_vm; - match read_to_string(config_path) { - Ok(raw_cfg) => match init_guest_vm(&raw_cfg) { - Ok(vm_id) => { - println!( - "✓ Successfully created VM[{}] from config: {}", - vm_id, config_path - ); - } - Err(_) => { - println!( - "✗ Failed to create VM from {}: Configuration error or panic occurred", - config_path - ); - } - }, - Err(e) => { - println!("✗ Failed to read config file {}: {:?}", config_path, e); - } - } - } - - // Check the actual number of VMs created - let final_vm_count = vm_list::get_vm_list().len(); - let created_count = final_vm_count - initial_vm_count; - - if created_count > 0 { - println!("Successfully created {} VM(s)", created_count); - println!("Use 'vm start ' to start the created VMs."); - } else { - println!("No VMs were created."); - } -} - -#[cfg(feature = "fs")] -fn vm_start(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let detach = cmd.flags.get("detach").unwrap_or(&false); - - if args.is_empty() { - // start all VMs - info!("VMM starting, booting all VMs..."); - let mut started_count = 0; - - for vm in vm_list::get_vm_list() { - // Check current status before starting - let status: VMStatus = vm.vm_status(); - if status == VMStatus::Running { - println!("⚠ VM[{}] is already running, skipping", vm.id()); - continue; - } - - if status != VMStatus::Loaded && status != VMStatus::Stopped { - println!("⚠ VM[{}] is in {:?} state, cannot start", vm.id(), status); - continue; - } - - if let Err(e) = start_single_vm(vm.clone()) { - println!("✗ VM[{}] failed to start: {:?}", vm.id(), e); - } else { - println!("✓ VM[{}] started successfully", vm.id()); - started_count += 1; - } - } - println!("Started {} VM(s)", started_count); - } else { - // Start specified VMs - for vm_name in args { - // Try to parse as VM ID or lookup VM name - if let Ok(vm_id) = vm_name.parse::() { - start_vm_by_id(vm_id); - } else { - println!("Error: VM name lookup not implemented. Use VM ID instead."); - println!("Available VMs:"); - vm_list_simple(); - } - } - } - - if *detach { - println!("VMs started in background mode"); - } -} - -/// Start a single VM by setting up vCPUs and calling boot. -/// Returns Ok(()) if successful, Err otherwise. -fn start_single_vm(vm: crate::vmm::VMRef) -> Result<(), &'static str> { - let vm_id = vm.id(); - let status = vm.vm_status(); - - // Validate state transition using helper function - can_start_vm(status)?; - - // Set up primary virtual CPU before starting - vcpus::setup_vm_primary_vcpu(vm.clone()); - - // Boot the VM - match vm.boot() { - Ok(_) => { - // Transition to Running state and notify the primary VCpu - // Note: Since the VCpu task is created directly in the wait queue (blocked state), - // we can immediately notify it without waiting for it to be scheduled first. - vcpus::notify_primary_vcpu(vm_id); - add_running_vm_count(1); - Ok(()) - } - Err(err) => { - // Revert status on failure - error!("Failed to boot VM[{}]: {:?}", vm_id, err); - Err("Failed to boot VM") - } - } -} - -fn start_vm_by_id(vm_id: usize) { - match with_vm(vm_id, |vm| start_single_vm(vm.clone())) { - Some(Ok(_)) => { - println!("✓ VM[{}] started successfully", vm_id); - } - Some(Err(err)) => { - println!("✗ VM[{}] failed to start: {}", vm_id, err); - } - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -fn vm_stop(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let force = cmd.flags.get("force").unwrap_or(&false); - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm stop [OPTIONS] "); - return; - } - - for vm_name in args { - if let Ok(vm_id) = vm_name.parse::() { - stop_vm_by_id(vm_id, *force); - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } - } -} - -fn stop_vm_by_id(vm_id: usize, force: bool) { - match with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - // Validate state transition using helper function - if let Err(err) = can_stop_vm(status, force) { - println!("⚠ VM[{}] {}", vm_id, err); - return Err(err); - } - - // Print appropriate message based on status - match status { - VMStatus::Stopping if force => { - println!("Force stopping VM[{}]...", vm_id); - } - VMStatus::Running => { - if force { - println!("Force stopping VM[{}]...", vm_id); - } else { - println!("Gracefully stopping VM[{}]...", vm_id); - } - } - VMStatus::Loading | VMStatus::Loaded => { - println!( - "⚠ VM[{}] is in {:?} state, stopping anyway...", - vm_id, status - ); - } - _ => {} - } - - // Call shutdown - match vm.shutdown() { - Ok(_) => Ok(()), - Err(_err) => { - // Revert status on failure - Err("Failed to shutdown VM") - } - } - }) { - Some(Ok(_)) => { - println!("✓ VM[{}] stop signal sent successfully", vm_id); - println!( - " Note: vCPU threads will exit gracefully, VM status will transition to Stopped" - ); - } - Some(Err(err)) => { - println!("✗ Failed to stop VM[{}]: {:?}", vm_id, err); - } - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -/// Restart a VM by stopping it (if running) and then starting it again.(functionality incomplete) -fn vm_restart(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let force = cmd.flags.get("force").unwrap_or(&false); - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm restart [OPTIONS] "); - return; - } - - for vm_name in args { - if let Ok(vm_id) = vm_name.parse::() { - restart_vm_by_id(vm_id, *force); - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } - } -} - -fn restart_vm_by_id(vm_id: usize, force: bool) { - println!("Restarting VM[{}]...", vm_id); - - // Check current status - let current_status = with_vm(vm_id, |vm| vm.vm_status()); - if current_status.is_none() { - println!("✗ VM[{}] not found", vm_id); - return; - } - - let status = current_status.unwrap(); - match status { - VMStatus::Stopped | VMStatus::Loaded => { - // VM is already stopped, just start it - println!("VM[{}] is already stopped, starting...", vm_id); - start_vm_by_id(vm_id); - } - VMStatus::Suspended | VMStatus::Running => { - // Stop the VM (this will wake up suspended VCpus automatically) - println!("Stopping VM[{}]...", vm_id); - stop_vm_by_id(vm_id, force); - - // Wait for VM to fully stop - println!("Waiting for VM[{}] to stop completely...", vm_id); - let max_wait_iterations = 50; // 5 seconds timeout (50 * 100ms) - let mut iterations = 0; - - loop { - if let Some(vm_status) = with_vm(vm_id, |vm| vm.vm_status()) { - match vm_status { - VMStatus::Stopped => { - println!("✓ VM[{}] stopped successfully", vm_id); - break; - } - VMStatus::Stopping => { - // Still stopping, wait a bit - iterations += 1; - if iterations >= max_wait_iterations { - println!( - "⚠ VM[{}] stop timeout, it may still be shutting down", - vm_id - ); - println!(" Use 'vm status {}' to check status manually", vm_id); - return; - } - // Sleep for 100ms - std::os::arceos::modules::axhal::time::busy_wait( - core::time::Duration::from_millis(100), - ); - } - _ => { - println!("⚠ VM[{}] in unexpected state: {:?}", vm_id, vm_status); - return; - } - } - } else { - println!("✗ VM[{}] no longer exists", vm_id); - return; - } - } - - // Now restart the VM - println!("Starting VM[{}]...", vm_id); - start_vm_by_id(vm_id); - } - VMStatus::Stopping => { - if force { - println!( - "⚠ VM[{}] is currently stopping, waiting for shutdown to complete...", - vm_id - ); - // Could implement similar wait logic here if needed - } else { - println!("⚠ VM[{}] is currently stopping", vm_id); - println!( - " Wait for shutdown to complete, then use 'vm start {}'", - vm_id - ); - println!(" Or use --force to wait and then restart"); - } - } - VMStatus::Loading => { - println!("✗ VM[{}] is still loading, cannot restart", vm_id); - } - } -} - -/// Suspend a running VM (functionality incomplete) -fn vm_suspend(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm suspend ..."); - return; - } - - for vm_name in args { - if let Ok(vm_id) = vm_name.parse::() { - suspend_vm_by_id(vm_id); - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } - } -} - -fn suspend_vm_by_id(vm_id: usize) { - println!("Suspending VM[{}]...", vm_id); - - let result: Option> = with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - // Check if VM can be suspended - can_suspend_vm(status)?; - - // Set VM status to Suspended - vm.set_vm_status(VMStatus::Suspended); - info!("VM[{}] status set to Suspended", vm_id); - - Ok(()) - }); - - match result { - Some(Ok(_)) => { - println!("✓ VM[{}] suspend signal sent", vm_id); - - // Get VM to check VCpu count - let vcpu_count = with_vm(vm_id, |vm| vm.vcpu_num()).unwrap_or(0); - println!( - " Note: {} VCpu task(s) will enter wait queue at next VMExit", - vcpu_count - ); - - // Wait a brief moment for VCpus to enter suspended state - println!(" Waiting for VCpus to suspend..."); - let max_wait_iterations = 10; // 1 second timeout (10 * 100ms) - let mut iterations = 0; - let mut all_suspended = false; - - while iterations < max_wait_iterations { - // Check if all VCpus are in blocked state - if let Some(vm) = crate::vmm::vm_list::get_vm_by_id(vm_id) { - let vcpu_states: Vec<_> = - vm.vcpu_list().iter().map(|vcpu| vcpu.state()).collect(); - - let blocked_count = vcpu_states - .iter() - .filter(|s| matches!(s, axvcpu::VCpuState::Blocked)) - .count(); - - if blocked_count == vcpu_states.len() { - all_suspended = true; - break; - } - - // Show progress for the first few iterations - if iterations < 3 { - debug!(" VCpus blocked: {}/{}", blocked_count, vcpu_states.len()); - } - } - - iterations += 1; - std::os::arceos::modules::axhal::time::busy_wait( - core::time::Duration::from_millis(100), - ); - } - - if all_suspended { - println!("✓ All VCpu tasks are now suspended"); - } else { - println!("⚠ Some VCpu tasks may still be transitioning to suspended state"); - println!(" VCpus will suspend at next VMExit (timer interrupt, I/O, etc.)"); - println!(" This is normal for VMs with low interrupt rates"); - } - - println!(" Use 'vm resume {}' to resume the VM", vm_id); - } - Some(Err(err)) => { - println!("✗ Failed to suspend VM[{}]: {}", vm_id, err); - } - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -// Resume a suspended VM (functionality incomplete) -fn vm_resume(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm resume ..."); - return; - } - - for vm_name in args { - if let Ok(vm_id) = vm_name.parse::() { - resume_vm_by_id(vm_id); - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } - } -} - -fn resume_vm_by_id(vm_id: usize) { - println!("Resuming VM[{}]...", vm_id); - - let result: Option> = with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - // Check if VM can be resumed - can_resume_vm(status)?; - - // Set VM status back to Running - vm.set_vm_status(VMStatus::Running); - - // Notify all VCpus to wake up - vcpus::notify_all_vcpus(vm_id); - - info!("VM[{}] resumed", vm_id); - Ok(()) - }); - - match result { - Some(Ok(_)) => { - println!("✓ VM[{}] resumed successfully", vm_id); - } - Some(Err(err)) => { - println!("✗ Failed to resume VM[{}]: {}", vm_id, err); - } - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -fn vm_delete(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let force = cmd.flags.get("force").unwrap_or(&false); - let keep_data = cmd.flags.get("keep-data").unwrap_or(&false); - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm delete [OPTIONS] "); - return; - } - - let vm_name = &args[0]; - - if let Ok(vm_id) = vm_name.parse::() { - // Check if VM exists and get its status first - let vm_status = with_vm(vm_id, |vm| vm.vm_status()); - - if vm_status.is_none() { - println!("✗ VM[{}] not found", vm_id); - return; - } - - let status = vm_status.unwrap(); - - // Check if VM is running - match status { - VMStatus::Running => { - if !force { - println!("✗ VM[{}] is currently running", vm_id); - println!( - " Use 'vm stop {}' first, or use '--force' to force delete", - vm_id - ); - return; - } - println!("⚠ Force deleting running VM[{}]...", vm_id); - } - VMStatus::Stopping => { - if !force { - println!("⚠ VM[{}] is currently stopping", vm_id); - println!(" Wait for it to stop completely, or use '--force' to force delete"); - return; - } - println!("⚠ Force deleting stopping VM[{}]...", vm_id); - } - VMStatus::Stopped => { - println!("Deleting stopped VM[{}]...", vm_id); - } - _ => { - println!("⚠ VM[{}] is in {:?} state", vm_id, status); - if !force { - println!("Use --force to force delete"); - return; - } - } - } - - delete_vm_by_id(vm_id, *keep_data); - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } -} - -fn delete_vm_by_id(vm_id: usize, keep_data: bool) { - // First check VM status and try to stop it if running - let vm_status = with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - // If VM is running, suspended, or stopping, send shutdown signal - match status { - VMStatus::Running | VMStatus::Suspended | VMStatus::Stopping => { - println!( - " VM[{}] is {:?}, sending shutdown signal...", - vm_id, status - ); - vm.set_vm_status(VMStatus::Stopping); - let _ = vm.shutdown(); - } - VMStatus::Loaded => { - // Transition from Loaded to Stopped - vm.set_vm_status(VMStatus::Stopped); - } - _ => {} - } - - use alloc::sync::Arc; - let count = Arc::strong_count(&vm); - println!(" [Debug] VM Arc strong_count: {}", count); - - status - }); - - if vm_status.is_none() { - println!("✗ VM[{}] not found or already removed", vm_id); - return; - } - - let status = vm_status.unwrap(); - - // Remove VM from global list - // Note: This drops the reference from the global list, but the VM object - // will only be fully destroyed when all vCPU threads exit and drop their references - match crate::vmm::vm_list::remove_vm(vm_id) { - Some(vm) => { - println!("✓ VM[{}] removed from VM list", vm_id); - - // Wait for vCPU threads to exit if VM has VCpu tasks - match status { - VMStatus::Running - | VMStatus::Suspended - | VMStatus::Stopping - | VMStatus::Stopped => { - println!(" Waiting for vCPU threads to exit..."); - - // Debug: Check Arc count before cleanup - use alloc::sync::Arc; - println!( - " [Debug] VM Arc count before cleanup: {}", - Arc::strong_count(&vm) - ); - - // Clean up VCpu resources after threads have exited - println!(" Cleaning up VCpu resources..."); - vcpus::cleanup_vm_vcpus(vm_id); - - // Debug: Check Arc count after final wait - println!( - " [Debug] VM Arc count after final wait: {}", - Arc::strong_count(&vm) - ); - } - _ => { - // VM not running, no vCPU threads to wait for - // But still need to clean up VCpu queue entry if it exists - vcpus::cleanup_vm_vcpus(vm_id); - } - } - - if keep_data { - println!("✓ VM[{}] deleted (configuration and data preserved)", vm_id); - } else { - println!("✓ VM[{}] deleted completely", vm_id); - - // Debug: Check Arc count - should be 1 now (only this variable) - // TaskExt uses Weak reference, so it doesn't count - use alloc::sync::Arc; - let count = Arc::strong_count(&vm); - println!(" [Debug] VM Arc strong_count: {}", count); - - if count == 1 { - println!(" ✓ Perfect! VM will be freed immediately when function returns"); - } else { - println!( - " ⚠ Warning: Unexpected Arc count {}, possible reference leak!", - count - ); - } - - // TODO: Clean up VM-related data files - // - Remove disk images - // - Remove configuration files - // - Remove log files - } - - // When function returns, the 'vm' variable is dropped - // Since Arc count is 1, AxVM::drop() is called immediately - println!(" VM[{}] will be freed now", vm_id); - } - None => { - println!( - "✗ Failed to remove VM[{}] from list (may have been removed already)", - vm_id - ); - } - } - - // When function returns, the 'vm' Arc is dropped - // If all vCPU threads have exited (ref_count was 1), AxVM::drop() is called here - println!("✓ VM[{}] deletion completed", vm_id); -} - -#[cfg(feature = "fs")] -fn vm_list_simple() { - let vms = vm_list::get_vm_list(); - println!("ID NAME STATE VCPU MEMORY"); - println!("---- ----------- ------- ---- ------"); - for vm in vms { - let status = vm.vm_status(); - - // Calculate total memory size - let total_memory: usize = vm.memory_regions().iter().map(|region| region.size()).sum(); - - println!( - "{:<4} {:<11} {:<7} {:<4} {}", - vm.id(), - vm.with_config(|cfg| cfg.name()), - status.as_str(), - vm.vcpu_num(), - format_memory_size(total_memory) - ); - } -} - -fn vm_list(cmd: &ParsedCommand) { - let binding = "table".to_string(); - let format = cmd.options.get("format").unwrap_or(&binding); - - let display_vms = vm_list::get_vm_list(); - - if display_vms.is_empty() { - println!("No virtual machines found."); - return; - } - - if format == "json" { - // JSON output - println!("{{"); - println!(" \"vms\": ["); - for (i, vm) in display_vms.iter().enumerate() { - let status = vm.vm_status(); - let total_memory: usize = vm.memory_regions().iter().map(|region| region.size()).sum(); - - println!(" {{"); - println!(" \"id\": {},", vm.id()); - println!(" \"name\": \"{}\",", vm.with_config(|cfg| cfg.name())); - println!(" \"state\": \"{}\",", status.as_str()); - println!(" \"vcpu\": {},", vm.vcpu_num()); - println!(" \"memory\": \"{}\"", format_memory_size(total_memory)); - - if i < display_vms.len() - 1 { - println!(" }},"); - } else { - println!(" }}"); - } - } - println!(" ]"); - println!("}}"); - } else { - // Table output (default) - println!( - "{:<6} {:<15} {:<12} {:<15} {:<10} {:<20}", - "VM ID", "NAME", "STATUS", "VCPU", "MEMORY", "VCPU STATE" - ); - println!( - "{:-<6} {:-<15} {:-<12} {:-<15} {:-<10} {:-<20}", - "", "", "", "", "", "" - ); - - for vm in display_vms { - let status = vm.vm_status(); - let total_memory: usize = vm.memory_regions().iter().map(|region| region.size()).sum(); - - // Get VCpu ID list - let vcpu_ids: Vec = vm - .vcpu_list() - .iter() - .map(|vcpu| vcpu.id().to_string()) - .collect(); - let vcpu_id_list = vcpu_ids.join(","); - - // Get VCpu state summary - let mut state_counts = std::collections::BTreeMap::new(); - for vcpu in vm.vcpu_list() { - let state = match vcpu.state() { - axvcpu::VCpuState::Free => "Free", - axvcpu::VCpuState::Running => "Run", - axvcpu::VCpuState::Blocked => "Blk", - axvcpu::VCpuState::Invalid => "Inv", - axvcpu::VCpuState::Created => "Cre", - axvcpu::VCpuState::Ready => "Rdy", - }; - *state_counts.entry(state).or_insert(0) += 1; - } - - // Format: Run:2,Blk:1 - let summary: Vec = state_counts - .iter() - .map(|(state, count)| format!("{}:{}", state, count)) - .collect(); - let vcpu_state_summary = summary.join(","); - - println!( - "{:<6} {:<15} {:<12} {:<15} {:<10} {:<20}", - vm.id(), - vm.with_config(|cfg| cfg.name()), - status.as_str(), - vcpu_id_list, - format_memory_size(total_memory), - vcpu_state_summary - ); - } - } -} - -fn vm_show(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let show_config = cmd.flags.get("config").unwrap_or(&false); - let show_stats = cmd.flags.get("stats").unwrap_or(&false); - let show_full = cmd.flags.get("full").unwrap_or(&false); - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm show [OPTIONS] "); - println!(); - println!("Options:"); - println!(" -f, --full Show full detailed information"); - println!(" -c, --config Show configuration details"); - println!(" -s, --stats Show statistics"); - println!(); - println!("Use 'vm list' to see all VMs"); - return; - } - - // Show specific VM details - let vm_name = &args[0]; - if let Ok(vm_id) = vm_name.parse::() { - if *show_full { - show_vm_full_details(vm_id); - } else { - show_vm_basic_details(vm_id, *show_config, *show_stats); - } - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } -} - -/// Show basic VM information (default view) -fn show_vm_basic_details(vm_id: usize, show_config: bool, show_stats: bool) { - match with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - println!("VM Details: {}", vm_id); - println!(); - - // Basic Information - println!(" VM ID: {}", vm.id()); - println!(" Name: {}", vm.with_config(|cfg| cfg.name())); - println!(" Status: {}", status.as_str_with_icon()); - println!(" VCPUs: {}", vm.vcpu_num()); - - // Calculate total memory - let total_memory: usize = vm.memory_regions().iter().map(|region| region.size()).sum(); - println!(" Memory: {}", format_memory_size(total_memory)); - - // Add state-specific information - match status { - VMStatus::Suspended => { - println!(); - println!(" ℹ VM is paused. Use 'vm resume {}' to continue.", vm_id); - } - VMStatus::Stopped => { - println!(); - println!(" ℹ VM is stopped. Use 'vm delete {}' to clean up.", vm_id); - } - VMStatus::Loaded => { - println!(); - println!(" ℹ VM is ready. Use 'vm start {}' to boot.", vm_id); - } - _ => {} - } - - // VCPU Summary - println!(); - println!("VCPU Summary:"); - let mut state_counts = std::collections::BTreeMap::new(); - for vcpu in vm.vcpu_list() { - let state = match vcpu.state() { - axvcpu::VCpuState::Free => "Free", - axvcpu::VCpuState::Running => "Running", - axvcpu::VCpuState::Blocked => "Blocked", - axvcpu::VCpuState::Invalid => "Invalid", - axvcpu::VCpuState::Created => "Created", - axvcpu::VCpuState::Ready => "Ready", - }; - *state_counts.entry(state).or_insert(0) += 1; - } - - for (state, count) in state_counts { - println!(" {}: {}", state, count); - } - - // Memory Summary - println!(); - println!("Memory Summary:"); - println!(" Total Regions: {}", vm.memory_regions().len()); - println!(" Total Size: {}", format_memory_size(total_memory)); - - // Configuration Summary - if show_config { - println!(); - println!("Configuration:"); - vm.with_config(|cfg| { - println!(" BSP Entry: {:#x}", cfg.bsp_entry().as_usize()); - println!(" AP Entry: {:#x}", cfg.ap_entry().as_usize()); - println!(" Interrupt Mode: {:?}", cfg.interrupt_mode()); - if let Some(dtb_addr) = cfg.image_config().dtb_load_gpa { - println!(" DTB Address: {:#x}", dtb_addr.as_usize()); - } - }); - } - - // Device Summary - if show_stats { - println!(); - println!("Device Summary:"); - println!( - " MMIO Devices: {}", - vm.get_devices().iter_mmio_dev().count() - ); - println!( - " SysReg Devices: {}", - vm.get_devices().iter_sys_reg_dev().count() - ); - } - - println!(); - println!("Use 'vm show {} --full' for detailed information", vm_id); - }) { - Some(_) => {} - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -/// Show full detailed information about a specific VM (--full flag) -fn show_vm_full_details(vm_id: usize) { - match with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - println!("=== VM Details: {} ===", vm_id); - println!(); - - // Basic Information - println!("Basic Information:"); - println!(" VM ID: {}", vm.id()); - println!(" Name: {}", vm.with_config(|cfg| cfg.name())); - println!(" Status: {}", status.as_str_with_icon()); - println!(" VCPUs: {}", vm.vcpu_num()); - - // Calculate total memory - let total_memory: usize = vm.memory_regions().iter().map(|region| region.size()).sum(); - println!(" Memory: {}", format_memory_size(total_memory)); - println!(" EPT Root: {:#x}", vm.ept_root().as_usize()); - - // Add state-specific information - match status { - VMStatus::Suspended => { - println!( - " ℹ VM is paused, VCpu tasks are waiting. Use 'vm resume {}' to continue.", - vm_id - ); - } - VMStatus::Stopping => { - println!(" ℹ VM is shutting down, VCpu tasks are exiting."); - } - VMStatus::Stopped => { - println!( - " ℹ VM is stopped, all VCpu tasks have exited. Use 'vm delete {}' to clean up.", - vm_id - ); - } - VMStatus::Loaded => { - println!( - " ℹ VM is ready to start. Use 'vm start {}' to boot.", - vm_id - ); - } - _ => {} - } - - // VCPU Details - println!(); - println!("VCPU Details:"); - - // Count VCpu states for summary - let mut state_counts = std::collections::BTreeMap::new(); - for vcpu in vm.vcpu_list() { - let state = match vcpu.state() { - axvcpu::VCpuState::Free => "Free", - axvcpu::VCpuState::Running => "Running", - axvcpu::VCpuState::Blocked => "Blocked", - axvcpu::VCpuState::Invalid => "Invalid", - axvcpu::VCpuState::Created => "Created", - axvcpu::VCpuState::Ready => "Ready", - }; - *state_counts.entry(state).or_insert(0) += 1; - } - - // Show summary first - let summary: Vec = state_counts - .iter() - .map(|(state, count)| format!("{}: {}", state, count)) - .collect(); - println!(" Summary: {}", summary.join(", ")); - println!(); - - for vcpu in vm.vcpu_list() { - let vcpu_state = match vcpu.state() { - axvcpu::VCpuState::Free => "Free", - axvcpu::VCpuState::Running => "Running", - axvcpu::VCpuState::Blocked => "Blocked", - axvcpu::VCpuState::Invalid => "Invalid", - axvcpu::VCpuState::Created => "Created", - axvcpu::VCpuState::Ready => "Ready", - }; - - if let Some(phys_cpu_set) = vcpu.phys_cpu_set() { - println!( - " VCPU {}: {} (Affinity: {:#x})", - vcpu.id(), - vcpu_state, - phys_cpu_set - ); - } else { - println!(" VCPU {}: {} (No affinity)", vcpu.id(), vcpu_state); - } - } - - // Add note for Suspended VMs - if status == VMStatus::Suspended { - println!(); - println!( - " Note: VCpu tasks are blocked in wait queue and will resume when VM is unpaused." - ); - } - - // Memory Regions - println!(); - println!( - "Memory Regions: ({} region(s), {} total)", - vm.memory_regions().len(), - format_memory_size(total_memory) - ); - for (i, region) in vm.memory_regions().iter().enumerate() { - let region_type = if region.needs_dealloc { - "Allocated" - } else { - "Reserved" - }; - let identical = if region.is_identical() { - " [identical]" - } else { - "" - }; - println!( - " Region {}: GPA={:#x} HVA={:#x} Size={} Type={}{}", - i, - region.gpa, - region.hva, - format_memory_size(region.size()), - region_type, - identical - ); - } - - // Configuration - println!(); - println!("Configuration:"); - vm.with_config(|cfg| { - println!(" BSP Entry: {:#x}", cfg.bsp_entry().as_usize()); - println!(" AP Entry: {:#x}", cfg.ap_entry().as_usize()); - println!(" Interrupt Mode: {:?}", cfg.interrupt_mode()); - - if let Some(dtb_addr) = cfg.image_config().dtb_load_gpa { - println!(" DTB Address: {:#x}", dtb_addr.as_usize()); - } - - // Show kernel info - println!( - " Kernel GPA: {:#x}", - cfg.image_config().kernel_load_gpa.as_usize() - ); - - // Show passthrough devices - if !cfg.pass_through_devices().is_empty() { - println!(); - println!( - " Passthrough Devices: ({} device(s))", - cfg.pass_through_devices().len() - ); - for device in cfg.pass_through_devices() { - println!( - " - {}: GPA[{:#x}~{:#x}] -> HPA[{:#x}~{:#x}] ({})", - device.name, - device.base_gpa, - device.base_gpa + device.length, - device.base_hpa, - device.base_hpa + device.length, - format_memory_size(device.length) - ); - } - } - - // Show passthrough addresses - if !cfg.pass_through_addresses().is_empty() { - println!(); - println!( - " Passthrough Memory Regions: ({} region(s))", - cfg.pass_through_addresses().len() - ); - for pt_addr in cfg.pass_through_addresses() { - println!( - " - GPA[{:#x}~{:#x}] ({})", - pt_addr.base_gpa, - pt_addr.base_gpa + pt_addr.length, - format_memory_size(pt_addr.length) - ); - } - } - - // Show passthrough SPIs (ARM specific) - #[cfg(target_arch = "aarch64")] - { - let spis = cfg.pass_through_spis(); - if !spis.is_empty() { - println!(); - println!(" Passthrough SPIs: {:?}", spis); - } - } - - // Show emulated devices - if !cfg.emu_devices().is_empty() { - println!(); - println!( - " Emulated Devices: ({} device(s))", - cfg.emu_devices().len() - ); - for (idx, device) in cfg.emu_devices().iter().enumerate() { - println!(" {}. {:?}", idx + 1, device); - } - } - }); - - // Devices - println!(); - let mmio_dev_count = vm.get_devices().iter_mmio_dev().count(); - let sysreg_dev_count = vm.get_devices().iter_sys_reg_dev().count(); - println!("Devices:"); - println!(" MMIO Devices: {}", mmio_dev_count); - println!(" SysReg Devices: {}", sysreg_dev_count); - - // Additional Statistics - println!(); - println!("Additional Statistics:"); - println!(" Total Memory Regions: {}", vm.memory_regions().len()); - - // Show VCpu affinity details - println!(); - println!(" VCpu Affinity Details:"); - for (vcpu_id, affinity, pcpu_id) in vm.get_vcpu_affinities_pcpu_ids() { - if let Some(aff) = affinity { - println!( - " VCpu {}: Physical CPU mask {:#x}, PCpu ID {}", - vcpu_id, aff, pcpu_id - ); - } else { - println!( - " VCpu {}: No specific affinity, PCpu ID {}", - vcpu_id, pcpu_id - ); - } - } - }) { - Some(_) => {} - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -/// Build the VM command tree and register it. -pub fn build_vm_cmd(tree: &mut BTreeMap) { - #[cfg(feature = "fs")] - let create_cmd = CommandNode::new("Create a new virtual machine") - .with_handler(vm_create) - .with_usage("vm create [OPTIONS] ...") - .with_option( - OptionDef::new("name", "Virtual machine name") - .with_short('n') - .with_long("name"), - ) - .with_option( - OptionDef::new("cpu", "Number of CPU cores") - .with_short('c') - .with_long("cpu"), - ) - .with_option( - OptionDef::new("memory", "Amount of memory") - .with_short('m') - .with_long("memory"), - ) - .with_flag( - FlagDef::new("force", "Force creation without confirmation") - .with_short('f') - .with_long("force"), - ); - - #[cfg(feature = "fs")] - let start_cmd = CommandNode::new("Start a virtual machine") - .with_handler(vm_start) - .with_usage("vm start [OPTIONS] [VM_ID...]") - .with_flag( - FlagDef::new("detach", "Start in background") - .with_short('d') - .with_long("detach"), - ) - .with_flag( - FlagDef::new("console", "Attach to console") - .with_short('c') - .with_long("console"), - ); - - let stop_cmd = CommandNode::new("Stop a virtual machine") - .with_handler(vm_stop) - .with_usage("vm stop [OPTIONS] ...") - .with_flag( - FlagDef::new("force", "Force stop") - .with_short('f') - .with_long("force"), - ) - .with_flag( - FlagDef::new("graceful", "Graceful shutdown") - .with_short('g') - .with_long("graceful"), - ); - - let restart_cmd = CommandNode::new("Restart a virtual machine") - .with_handler(vm_restart) - .with_usage("vm restart [OPTIONS] ...") - .with_flag( - FlagDef::new("force", "Force restart") - .with_short('f') - .with_long("force"), - ); - - let suspend_cmd = CommandNode::new("Suspend (pause) a running virtual machine") - .with_handler(vm_suspend) - .with_usage("vm suspend ..."); - - let resume_cmd = CommandNode::new("Resume a suspended virtual machine") - .with_handler(vm_resume) - .with_usage("vm resume ..."); - - let delete_cmd = CommandNode::new("Delete a virtual machine") - .with_handler(vm_delete) - .with_usage("vm delete [OPTIONS] ") - .with_flag( - FlagDef::new("force", "Skip confirmation") - .with_short('f') - .with_long("force"), - ) - .with_flag(FlagDef::new("keep-data", "Keep VM data").with_long("keep-data")); - - let list_cmd = CommandNode::new("Show virtual machine lists") - .with_handler(vm_list) - .with_usage("vm list [OPTIONS]") - .with_flag( - FlagDef::new("all", "Show all VMs including stopped ones") - .with_short('a') - .with_long("all"), - ) - .with_option(OptionDef::new("format", "Output format (table, json)").with_long("format")); - - let show_cmd = CommandNode::new("Show detailed VM information") - .with_handler(vm_show) - .with_usage("vm show [OPTIONS] ") - .with_flag( - FlagDef::new("full", "Show full detailed information") - .with_short('f') - .with_long("full"), - ) - .with_flag( - FlagDef::new("config", "Show configuration details") - .with_short('c') - .with_long("config"), - ) - .with_flag( - FlagDef::new("stats", "Show device statistics") - .with_short('s') - .with_long("stats"), - ); - - // main VM command - let mut vm_node = CommandNode::new("Virtual machine management") - .with_handler(vm_help) - .with_usage("vm [options] [args...]") - .add_subcommand( - "help", - CommandNode::new("Show VM help").with_handler(vm_help), - ); - - #[cfg(feature = "fs")] - { - vm_node = vm_node - .add_subcommand("create", create_cmd) - .add_subcommand("start", start_cmd); - } - - vm_node = vm_node - .add_subcommand("stop", stop_cmd) - .add_subcommand("suspend", suspend_cmd) - .add_subcommand("resume", resume_cmd) - .add_subcommand("restart", restart_cmd) - .add_subcommand("delete", delete_cmd) - .add_subcommand("list", list_cmd) - .add_subcommand("show", show_cmd); - - tree.insert("vm".to_string(), vm_node); -} diff --git a/os/axvisor/src/shell/mod.rs b/os/axvisor/src/shell/mod.rs deleted file mode 100644 index 49aac346a..000000000 --- a/os/axvisor/src/shell/mod.rs +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod command; - -use std::io::prelude::*; -use std::println; -use std::string::ToString; - -use crate::shell::command::{ - CommandHistory, clear_line_and_redraw, handle_builtin_commands, print_prompt, run_cmd_bytes, -}; - -const LF: u8 = b'\n'; -const CR: u8 = b'\r'; -const DL: u8 = b'\x7f'; -const BS: u8 = b'\x08'; -const ESC: u8 = 0x1b; // ESC key - -const MAX_LINE_LEN: usize = 256; - -// Initialize the console shell. -pub fn console_init() { - let mut stdin = std::io::stdin(); - let mut stdout = std::io::stdout(); - let mut history = CommandHistory::new(100); - - let mut buf = [0; MAX_LINE_LEN]; - let mut cursor = 0; // cursor position in buffer - let mut line_len = 0; // actual length of current line - - enum InputState { - Normal, - Escape, - EscapeSeq, - } - - let mut input_state = InputState::Normal; - - println!("Welcome to AxVisor Shell!"); - println!("Type 'help' to see available commands"); - println!("Use UP/DOWN arrows to navigate command history"); - #[cfg(not(feature = "fs"))] - println!("Note: Running with limited features (filesystem support disabled)."); - println!(); - - print_prompt(); - - loop { - let mut temp_buf = [0u8; 1]; - - let ch = match stdin.read(&mut temp_buf) { - Ok(1) => temp_buf[0], - _ => { - continue; - } - }; - - match input_state { - InputState::Normal => { - match ch { - CR | LF => { - println!(); - if line_len > 0 { - let cmd_str = std::str::from_utf8(&buf[..line_len]).unwrap_or(""); - - // Add to history - history.add_command(cmd_str.to_string()); - - // Execute command - if !handle_builtin_commands(cmd_str) { - run_cmd_bytes(&buf[..line_len]); - } - - // reset buffer - buf[..line_len].fill(0); - cursor = 0; - line_len = 0; - } - print_prompt(); - } - BS | DL => { - // backspace: delete character before cursor / DEL key: delete character at cursor - if cursor > 0 { - // move characters after cursor forward - for i in cursor..line_len { - buf[i - 1] = buf[i]; - } - cursor -= 1; - line_len -= 1; - if line_len < buf.len() { - buf[line_len] = 0; - } - - let current_content = - std::str::from_utf8(&buf[..line_len]).unwrap_or(""); - #[cfg(feature = "fs")] - let prompt = format!("axvisor:{}$ ", &std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - let prompt = "axvisor:$ ".to_string(); - clear_line_and_redraw(&mut stdout, &prompt, current_content, cursor); - } - } - ESC => { - input_state = InputState::Escape; - } - 0..=31 => { - // ignore other control characters - } - c => { - // insert character - if line_len < MAX_LINE_LEN - 1 { - // move characters after cursor backward to make space for new character - for i in (cursor..line_len).rev() { - buf[i + 1] = buf[i]; - } - buf[cursor] = c; - cursor += 1; - line_len += 1; - - let current_content = - std::str::from_utf8(&buf[..line_len]).unwrap_or(""); - #[cfg(feature = "fs")] - let prompt = format!("axvisor:{}$ ", &std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - let prompt = "axvisor:$ ".to_string(); - clear_line_and_redraw(&mut stdout, &prompt, current_content, cursor); - } - } - } - } - InputState::Escape => match ch { - b'[' => { - input_state = InputState::EscapeSeq; - } - _ => { - input_state = InputState::Normal; - } - }, - InputState::EscapeSeq => { - match ch { - b'A' => { - // UP arrow - previous command - if let Some(prev_cmd) = history.previous() { - // clear current buffer - buf[..line_len].fill(0); - - let cmd_bytes = prev_cmd.as_bytes(); - let copy_len = cmd_bytes.len().min(MAX_LINE_LEN - 1); - buf[..copy_len].copy_from_slice(&cmd_bytes[..copy_len]); - cursor = copy_len; - line_len = copy_len; - #[cfg(feature = "fs")] - let prompt = format!("axvisor:{}$ ", &std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - let prompt = "axvisor:$ ".to_string(); - clear_line_and_redraw(&mut stdout, &prompt, prev_cmd, cursor); - } - input_state = InputState::Normal; - } - b'B' => { - // DOWN arrow - next command - match history.next() { - Some(next_cmd) => { - // clear current buffer - buf[..line_len].fill(0); - - let cmd_bytes = next_cmd.as_bytes(); - let copy_len = cmd_bytes.len().min(MAX_LINE_LEN - 1); - buf[..copy_len].copy_from_slice(&cmd_bytes[..copy_len]); - cursor = copy_len; - line_len = copy_len; - - #[cfg(feature = "fs")] - let prompt = - format!("axvisor:{}$ ", &std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - let prompt = "axvisor:$ ".to_string(); - clear_line_and_redraw(&mut stdout, &prompt, next_cmd, cursor); - } - None => { - // clear current line - buf[..line_len].fill(0); - cursor = 0; - line_len = 0; - #[cfg(feature = "fs")] - let prompt = - format!("axvisor:{}$ ", &std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - let prompt = "axvisor:$ ".to_string(); - clear_line_and_redraw(&mut stdout, &prompt, "", cursor); - } - } - input_state = InputState::Normal; - } - b'C' => { - // RIGHT arrow - move cursor right - if cursor < line_len { - cursor += 1; - stdout.write_all(b"\x1b[C").ok(); - stdout.flush().ok(); - } - input_state = InputState::Normal; - } - b'D' => { - // LEFT arrow - move cursor left - if cursor > 0 { - cursor -= 1; - stdout.write_all(b"\x1b[D").ok(); - stdout.flush().ok(); - } - input_state = InputState::Normal; - } - b'3' => { - // check if this is Delete key sequence (ESC[3~) - // need to read next character to confirm - input_state = InputState::Normal; - // can add additional state to handle complete Delete sequence - } - _ => { - // ignore other escape sequences - input_state = InputState::Normal; - } - } - } - } - } -} diff --git a/os/axvisor/src/task.rs b/os/axvisor/src/task.rs deleted file mode 100644 index 27bbda958..000000000 --- a/os/axvisor/src/task.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::sync::{Arc, Weak}; -use std::os::arceos::modules::axtask::{TaskExt, TaskInner}; - -use crate::vmm::{VCpuRef, VM, VMRef}; - -/// Task extended data for the hypervisor. -pub struct VCpuTask { - /// The VM (Weak reference to avoid keeping VM alive). - pub vm: Weak, - /// The virtual CPU. - pub vcpu: VCpuRef, -} - -impl VCpuTask { - /// Create a new [`VCpuTask`]. - pub fn new(vm: &VMRef, vcpu: VCpuRef) -> Self { - Self { - vm: Arc::downgrade(vm), - vcpu, - } - } - - /// Get a strong reference to the VM if it's still alive. - /// Returns None if the VM has been dropped. - pub fn vm(&self) -> VMRef { - self.vm.upgrade().expect("VM has been dropped") - } -} - -#[extern_trait::extern_trait] -impl TaskExt for VCpuTask {} - -pub trait AsVCpuTask { - fn as_vcpu_task(&self) -> &VCpuTask; -} - -impl AsVCpuTask for TaskInner { - fn as_vcpu_task(&self) -> &VCpuTask { - self.task_ext() - .expect("Not a VCpuTask") - .downcast_ref::() - } -} diff --git a/os/axvisor/src/vmm/config.rs b/os/axvisor/src/vmm/config.rs deleted file mode 100644 index 3b780f976..000000000 --- a/os/axvisor/src/vmm/config.rs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axaddrspace::GuestPhysAddr; -use axerrno::AxResult; -use axvm::{ - VMMemoryRegion, - config::{AxVMConfig, AxVMCrateConfig, VmMemMappingType}, -}; -use core::alloc::Layout; - -use crate::vmm::{VM, images::ImageLoader, vm_list::push_vm}; - -#[cfg(target_arch = "aarch64")] -use crate::vmm::fdt::*; - -use alloc::sync::Arc; - -#[allow(clippy::module_inception, dead_code)] -pub mod config { - use alloc::string::String; - use alloc::vec::Vec; - - /// Default static VM configs. Used when no VM config is provided. - pub fn default_static_vm_configs() -> Vec<&'static str> { - vec![] - } - - /// Read VM configs from filesystem - #[cfg(feature = "fs")] - pub fn filesystem_vm_configs() -> Vec { - use axstd::fs; - use axstd::io::{BufReader, Read}; - - let config_dir = "/guest/vm_default"; - - let mut configs = Vec::new(); - - debug!("Read VM config files from filesystem."); - - let entries = match fs::read_dir(config_dir) { - Ok(entries) => { - info!("Find dir: {}", config_dir); - entries - } - Err(_e) => { - info!("NOT find dir: {} in filesystem", config_dir); - return configs; - } - }; - - for entry in entries.flatten() { - let path = entry.path(); - // Check if the file has a .toml extension - let path_str = path.as_str(); - debug!("Considering file: {}", path_str); - if path_str.ends_with(".toml") { - let toml_file = fs::File::open(path_str).expect("Failed to open file"); - let file_size = toml_file - .metadata() - .expect("Failed to get file metadata") - .len() as usize; - - info!("File {} size: {}", path_str, file_size); - - if file_size == 0 { - warn!("File {} is empty", path_str); - continue; - } - - let mut file = BufReader::new(toml_file); - let mut buffer = vec![0u8; file_size]; - match file.read_exact(&mut buffer) { - Ok(()) => { - debug!( - "Successfully read config file {} as bytes, size: {}", - path_str, - buffer.len() - ); - // Convert to string - let content = alloc::string::String::from_utf8(buffer) - .expect("Failed to convert bytes to UTF-8 string"); - - if content.contains("[base]") - && content.contains("[kernel]") - && content.contains("[devices]") - { - configs.push(content); - info!( - "TOML config: {} is valid, start the virtual machine directly now. ", - path_str - ); - } else { - warn!( - "File {} does not appear to contain valid VM config structure", - path_str - ); - } - } - Err(e) => { - error!("Failed to read file {}: {:?}", path_str, e); - } - } - } - } - - configs - } - - /// Fallback function for when "fs" feature is not enabled - #[cfg(not(feature = "fs"))] - pub fn filesystem_vm_configs() -> Vec { - Vec::new() - } - - include!(concat!(env!("OUT_DIR"), "/vm_configs.rs")); -} - -pub fn get_vm_dtb_arc(_vm_cfg: &AxVMConfig) -> Option> { - #[cfg(target_arch = "aarch64")] - { - let cache_lock = dtb_cache().lock(); - if let Some(dtb) = cache_lock.get(&_vm_cfg.id()) { - return Some(Arc::from(dtb.as_slice())); - } - } - None -} - -pub fn init_guest_vms() { - // Initialize the DTB cache in the fdt module - #[cfg(target_arch = "aarch64")] - { - init_dtb_cache(); - } - - // First try to get configs from filesystem if fs feature is enabled - let mut gvm_raw_configs = config::filesystem_vm_configs(); - - // If no filesystem configs found, fallback to static configs - if gvm_raw_configs.is_empty() { - let static_configs = config::static_vm_configs(); - if static_configs.is_empty() { - info!("Static VM configs are empty."); - info!("Now axvisor will entry the shell..."); - } else { - info!("Using static VM configs."); - } - // Convert static configs to String type - gvm_raw_configs.extend(static_configs.into_iter().map(|s| s.into())); - } - - for raw_cfg_str in gvm_raw_configs { - debug!("Initializing guest VM with config: {:#?}", raw_cfg_str); - if let Err(e) = init_guest_vm(&raw_cfg_str) { - error!("Failed to initialize guest VM: {e:?}"); - } - } -} - -pub fn init_guest_vm(raw_cfg: &str) -> AxResult { - let vm_create_config = - AxVMCrateConfig::from_toml(raw_cfg).expect("Failed to resolve VM config"); - - if let Some(linux) = super::images::get_image_header(&vm_create_config) { - debug!( - "VM[{}] Linux header: {:#x?}", - vm_create_config.base.id, linux - ); - } - - #[cfg(target_arch = "aarch64")] - let mut vm_config = AxVMConfig::from(vm_create_config.clone()); - - #[cfg(not(target_arch = "aarch64"))] - let vm_config = AxVMConfig::from(vm_create_config.clone()); - - // Handle FDT-related operations for aarch64 - #[cfg(target_arch = "aarch64")] - handle_fdt_operations(&mut vm_config, &vm_create_config); - - // info!("after parse_vm_interrupt, crate VM[{}] with config: {:#?}", vm_config.id(), vm_config); - info!("Creating VM[{}] {:?}", vm_config.id(), vm_config.name()); - - // Create VM. - let vm = VM::new(vm_config).expect("Failed to create VM"); - let vm_id = vm.id(); - push_vm(vm.clone()); - - vm_alloc_memorys(&vm_create_config, &vm); - - let main_mem = vm - .memory_regions() - .first() - .cloned() - .expect("VM must have at least one memory region"); - - config_guest_address(&vm, &main_mem); - - // Load corresponding images for VM. - info!("VM[{}] created success, loading images...", vm.id()); - - let mut loader = ImageLoader::new(main_mem, vm_create_config, vm.clone()); - loader.load().expect("Failed to load VM images"); - - if let Err(e) = vm.init() { - panic!("VM[{}] setup failed: {:?}", vm.id(), e); - } - - vm.set_vm_status(axvm::VMStatus::Loaded); - - Ok(vm_id) -} - -fn config_guest_address(vm: &VM, main_memory: &VMMemoryRegion) { - const MB: usize = 1024 * 1024; - vm.with_config(|config| { - if main_memory.is_identical() { - debug!( - "Adjusting kernel load address from {:#x} to {:#x}", - config.image_config.kernel_load_gpa, main_memory.gpa - ); - let mut kernel_addr = main_memory.gpa; - if config.image_config.bios_load_gpa.is_some() { - kernel_addr += MB * 2; // leave 2MB for BIOS - } - - config.image_config.kernel_load_gpa = kernel_addr; - config.cpu_config.bsp_entry = kernel_addr; - config.cpu_config.ap_entry = kernel_addr; - } - }); -} - -fn vm_alloc_memorys(vm_create_config: &AxVMCrateConfig, vm: &VM) { - const MB: usize = 1024 * 1024; - const ALIGN: usize = 2 * MB; - - for memory in &vm_create_config.kernel.memory_regions { - match memory.map_type { - VmMemMappingType::MapAlloc => { - vm.alloc_memory_region( - Layout::from_size_align(memory.size, ALIGN).unwrap(), - Some(GuestPhysAddr::from(memory.gpa)), - ) - .expect("Failed to allocate memory region for VM"); - } - VmMemMappingType::MapIdentical => { - vm.alloc_memory_region(Layout::from_size_align(memory.size, ALIGN).unwrap(), None) - .expect("Failed to allocate memory region for VM"); - } - VmMemMappingType::MapReserved => { - info!("VM[{}] map same region: {:#x?}", vm.id(), memory); - let layout = Layout::from_size_align(memory.size, ALIGN).unwrap(); - vm.map_reserved_memory_region(layout, Some(GuestPhysAddr::from(memory.gpa))) - .expect("Failed to map memory region for VM"); - } - } - } -} diff --git a/os/axvisor/src/vmm/fdt/create.rs b/os/axvisor/src/vmm/fdt/create.rs deleted file mode 100644 index baeaddd39..000000000 --- a/os/axvisor/src/vmm/fdt/create.rs +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::{ - string::{String, ToString}, - vec::Vec, -}; -use core::ptr::NonNull; - -use super::vm_fdt::{FdtWriter, FdtWriterNode}; -use axaddrspace::GuestPhysAddr; -use axvm::{VMMemoryRegion, config::AxVMCrateConfig}; -use fdt_parser::{Fdt, Node}; -use memory_addr::MemoryAddr; - -use crate::vmm::{VMRef, images::load_vm_image_from_memory}; - -// use crate::vmm::fdt::print::{print_fdt, print_guest_fdt}; -/// Generate guest FDT and return DTB data -/// -/// # Parameters -/// * `fdt` - Source FDT data -/// * `passthrough_device_names` - Passthrough device name list -/// * `crate_config` - VM creation configuration -/// -/// # Return Value -/// Returns the generated DTB data -pub fn crate_guest_fdt( - fdt: &Fdt, - passthrough_device_names: &[String], - crate_config: &AxVMCrateConfig, -) -> Vec { - let mut fdt_writer = FdtWriter::new().unwrap(); - // Track the level of the previously processed node for level change handling - let mut previous_node_level = 0; - // Maintain a stack of FDT nodes to correctly start and end nodes - let mut node_stack: Vec = Vec::new(); - let phys_cpu_ids = crate_config - .base - .phys_cpu_ids - .clone() - .expect("ERROR: phys_cpu_ids is None"); - - let all_nodes: Vec = fdt.all_nodes().collect(); - - for (index, node) in all_nodes.iter().enumerate() { - let node_path = super::build_node_path(&all_nodes, index); - let node_action = determine_node_action(node, &node_path, passthrough_device_names); - - match node_action { - NodeAction::RootNode => { - node_stack.push(fdt_writer.begin_node("").unwrap()); - } - NodeAction::CpuNode => { - let need = need_cpu_node(&phys_cpu_ids, node, &node_path); - if need { - handle_node_level_change( - &mut fdt_writer, - &mut node_stack, - node.level, - previous_node_level, - ); - node_stack.push(fdt_writer.begin_node(node.name()).unwrap()); - } else { - continue; - } - } - NodeAction::Skip => { - continue; - } - _ => { - trace!( - "Found exact passthrough device node: {}, path: {}", - node.name(), - node_path - ); - handle_node_level_change( - &mut fdt_writer, - &mut node_stack, - node.level, - previous_node_level, - ); - node_stack.push(fdt_writer.begin_node(node.name()).unwrap()); - } - } - - previous_node_level = node.level; - - // Copy all properties of the node - for prop in node.propertys() { - fdt_writer.property(prop.name, prop.raw_value()).unwrap(); - } - } - - // End all unclosed nodes - while let Some(node) = node_stack.pop() { - previous_node_level -= 1; - fdt_writer.end_node(node).unwrap(); - } - assert_eq!(previous_node_level, 0); - - fdt_writer.finish().unwrap() -} - -/// Node processing action enumeration -enum NodeAction { - /// Skip node, not included in guest FDT - Skip, - /// Root node - RootNode, - /// CPU node - CpuNode, - /// Include node as passthrough device node - IncludeAsPassthroughDevice, - /// Include node as child node of passthrough device - IncludeAsChildNode, - /// Include node as ancestor node of passthrough device - IncludeAsAncestorNode, -} - -/// Determine node processing action -fn determine_node_action( - node: &Node, - node_path: &str, - passthrough_device_names: &[String], -) -> NodeAction { - if node.name() == "/" { - // Special handling for root node - NodeAction::RootNode - } else if node.name().starts_with("memory") { - // Skip memory nodes, will add them later - NodeAction::Skip - } else if node_path.starts_with("/cpus") { - NodeAction::CpuNode - } else if passthrough_device_names.contains(&node_path.to_string()) { - // Fully matched passthrough device node - NodeAction::IncludeAsPassthroughDevice - } - // Check if the node is a descendant of a passthrough device (by path inclusion and level validation) - else if is_descendant_of_passthrough_device(node_path, node.level, passthrough_device_names) { - NodeAction::IncludeAsChildNode - } - // Check if the node is an ancestor of a passthrough device (by path inclusion and level validation) - else if is_ancestor_of_passthrough_device(node_path, passthrough_device_names) { - NodeAction::IncludeAsAncestorNode - } else { - NodeAction::Skip - } -} - -/// Determine if node is a descendant of passthrough device -/// When node path contains a path from passthrough_device_names and is longer than it, it is its descendant node -/// Also use node_level as validation condition -fn is_descendant_of_passthrough_device( - node_path: &str, - node_level: usize, - passthrough_device_names: &[String], -) -> bool { - for passthrough_path in passthrough_device_names { - // Check if the current node is a descendant of a passthrough device - if node_path.starts_with(passthrough_path) && node_path.len() > passthrough_path.len() { - // Ensure it is a true descendant path (separated by /) - if passthrough_path == "/" || node_path.chars().nth(passthrough_path.len()) == Some('/') - { - // Use level relationship for validation: the level of a descendant node should be higher than its parent - // Note: The level of the root node is 1, its direct child node level is 2, and so on - let expected_parent_level = passthrough_path.matches('/').count(); - let current_node_level = node_level; - - // If passthrough_path is the root node "/", then its child node level should be 2 - // Otherwise, the child node level should be higher than the parent node level - if (passthrough_path == "/" && current_node_level >= 2) - || (passthrough_path != "/" && current_node_level > expected_parent_level) - { - return true; - } - } - } - } - false -} - -/// Handle node level changes to ensure correct FDT structure -fn handle_node_level_change( - fdt_writer: &mut FdtWriter, - node_stack: &mut Vec, - current_level: usize, - previous_level: usize, -) { - if current_level <= previous_level { - for _ in current_level..=previous_level { - if let Some(end_node) = node_stack.pop() { - fdt_writer.end_node(end_node).unwrap(); - } - } - } -} - -/// Determine if node is an ancestor of passthrough device -fn is_ancestor_of_passthrough_device(node_path: &str, passthrough_device_names: &[String]) -> bool { - for passthrough_path in passthrough_device_names { - // Check if the current node is an ancestor of a passthrough device - if passthrough_path.starts_with(node_path) && passthrough_path.len() > node_path.len() { - // Ensure it is a true ancestor path (separated by /) - let next_char = passthrough_path.chars().nth(node_path.len()).unwrap_or(' '); - if next_char == '/' || node_path == "/" { - return true; - } - } - } - false -} - -/// Determine if CPU node is needed -fn need_cpu_node(phys_cpu_ids: &[usize], node: &Node, node_path: &str) -> bool { - let mut should_include_node = false; - - if !node_path.starts_with("/cpus/cpu@") { - should_include_node = true; - } else if let Some(mut cpu_reg) = node.reg() - && let Some(reg_entry) = cpu_reg.next() - { - let cpu_address = reg_entry.address as usize; - debug!( - "Checking CPU node {} with address 0x{:x}", - node.name(), - cpu_address - ); - // Check if this CPU address is in the configured phys_cpu_ids - if phys_cpu_ids.contains(&cpu_address) { - should_include_node = true; - debug!( - "CPU node {} with address 0x{:x} is in phys_cpu_ids, including in guest FDT", - node.name(), - cpu_address - ); - } else { - debug!( - "CPU node {} with address 0x{:x} is NOT in phys_cpu_ids, skipping", - node.name(), - cpu_address - ); - } - } - should_include_node -} - -/// Add memory node -fn add_memory_node(new_memory: &[VMMemoryRegion], new_fdt: &mut FdtWriter) { - let mut new_value: Vec = Vec::new(); - for mem in new_memory { - let gpa = mem.gpa.as_usize() as u64; - let size = mem.size() as u64; - new_value.push((gpa >> 32) as u32); - new_value.push((gpa & 0xFFFFFFFF) as u32); - new_value.push((size >> 32) as u32); - new_value.push((size & 0xFFFFFFFF) as u32); - } - info!("Adding memory node with value: 0x{new_value:x?}"); - new_fdt - .property_array_u32("reg", new_value.as_ref()) - .unwrap(); - new_fdt.property_string("device_type", "memory").unwrap(); -} - -pub fn update_fdt(fdt_src: NonNull, dtb_size: usize, vm: VMRef) { - let mut new_fdt = FdtWriter::new().unwrap(); - let mut previous_node_level = 0; - let mut node_stack: Vec = Vec::new(); - - let fdt_bytes = unsafe { core::slice::from_raw_parts(fdt_src.as_ptr(), dtb_size) }; - let fdt = Fdt::from_bytes(fdt_bytes) - .map_err(|e| format!("Failed to parse FDT: {e:#?}")) - .expect("Failed to parse FDT"); - - for node in fdt.all_nodes() { - if node.name() == "/" { - node_stack.push(new_fdt.begin_node("").unwrap()); - } else if node.name().starts_with("memory") { - // Skip memory nodes, will add them later - continue; - } else { - handle_node_level_change( - &mut new_fdt, - &mut node_stack, - node.level, - previous_node_level, - ); - // Start new node - node_stack.push(new_fdt.begin_node(node.name()).unwrap()); - } - - previous_node_level = node.level; - - if node.name() == "chosen" { - for prop in node.propertys() { - if prop.name.starts_with("linux,initrd-") { - info!( - "Skipping property: {}, belonging to node: {}", - prop.name, - node.name() - ); - } else if prop.name == "bootargs" { - let bootargs_str = prop.str(); - let modified_bootargs = bootargs_str.replace(" ro ", " rw "); - - if modified_bootargs != bootargs_str { - info!( - "Modifying bootargs: {} -> {}", - bootargs_str, modified_bootargs - ); - } - - new_fdt - .property_string(prop.name, &modified_bootargs) - .unwrap(); - } else { - debug!( - "Find property: {}, belonging to node: {}", - prop.name, - node.name() - ); - new_fdt.property(prop.name, prop.raw_value()).unwrap(); - } - } - } else { - for prop in node.propertys() { - new_fdt.property(prop.name, prop.raw_value()).unwrap(); - } - } - } - - // End all unclosed nodes, and add memory nodes at appropriate positions - while let Some(node) = node_stack.pop() { - previous_node_level -= 1; - new_fdt.end_node(node).unwrap(); - - // add memory node - if previous_node_level == 1 { - let memory_regions = vm.memory_regions(); - debug!("Adding memory node with regions: {memory_regions:?}"); - let memory_node = new_fdt.begin_node("memory").unwrap(); - add_memory_node(&memory_regions, &mut new_fdt); - new_fdt.end_node(memory_node).unwrap(); - } - } - - assert_eq!(previous_node_level, 0); - - info!("Updating FDT memory successfully"); - - let new_fdt_bytes = new_fdt.finish().unwrap(); - - // crate::vmm::fdt::print::print_guest_fdt(new_fdt_bytes.as_slice()); - let vm_clone = vm.clone(); - let dest_addr = calculate_dtb_load_addr(vm, new_fdt_bytes.len()); - info!( - "New FDT will be loaded at {:x}, size: 0x{:x}", - dest_addr, - new_fdt_bytes.len() - ); - // Load the updated FDT into VM - load_vm_image_from_memory(&new_fdt_bytes, dest_addr, vm_clone) - .expect("Failed to load VM images"); -} - -fn calculate_dtb_load_addr(vm: VMRef, fdt_size: usize) -> GuestPhysAddr { - const MB: usize = 1024 * 1024; - - // Get main memory from VM memory regions outside the closure - let main_memory = vm - .memory_regions() - .first() - .cloned() - .expect("VM must have at least one memory region"); - - vm.with_config(|config| { - let dtb_addr = if let Some(addr) = config.image_config.dtb_load_gpa - && !main_memory.is_identical() - { - // If dtb_load_gpa is already set, use the original value - addr - } else { - // If dtb_load_gpa is None, calculate based on memory size and FDT size - let main_memory_size = main_memory.size().min(512 * MB); - let addr = (main_memory.gpa + main_memory_size - fdt_size).align_down(2 * MB); - if fdt_size > main_memory_size { - error!("DTB size is larger than available memory"); - } - addr - }; - config.image_config.dtb_load_gpa = Some(dtb_addr); - dtb_addr - }) -} - -pub fn update_cpu_node(fdt: &Fdt, host_fdt: &Fdt, crate_config: &AxVMCrateConfig) -> Vec { - let mut new_fdt = FdtWriter::new().unwrap(); - let mut previous_node_level = 0; - let mut node_stack: Vec = Vec::new(); - let phys_cpu_ids = crate_config - .base - .phys_cpu_ids - .clone() - .expect("ERROR: phys_cpu_ids is None"); - - // Collect all nodes from both FDTs - let fdt_all_nodes: Vec = fdt.all_nodes().collect(); - let host_fdt_all_nodes: Vec = host_fdt.all_nodes().collect(); - - for (index, node) in fdt_all_nodes.iter().enumerate() { - let node_path = super::build_node_path(&fdt_all_nodes, index); - - if node.name() == "/" { - node_stack.push(new_fdt.begin_node("").unwrap()); - } else if node_path.starts_with("/cpus") { - // Skip CPU nodes from fdt, we'll process them from host_fdt later - continue; - } else { - // For all other nodes, include them from fdt as-is without filtering - handle_node_level_change( - &mut new_fdt, - &mut node_stack, - node.level, - previous_node_level, - ); - node_stack.push(new_fdt.begin_node(node.name()).unwrap()); - } - - previous_node_level = node.level; - - // Copy all properties of the node (for non-CPU nodes) - for prop in node.propertys() { - new_fdt.property(prop.name, prop.raw_value()).unwrap(); - } - } - - // Process all CPU nodes from host_fdt - for (index, node) in host_fdt_all_nodes.iter().enumerate() { - let node_path = super::build_node_path(&host_fdt_all_nodes, index); - - if node_path.starts_with("/cpus") { - // For CPU nodes, apply filtering based on host_fdt nodes - let need = need_cpu_node(&phys_cpu_ids, node, &node_path); - if need { - handle_node_level_change( - &mut new_fdt, - &mut node_stack, - node.level, - previous_node_level, - ); - node_stack.push(new_fdt.begin_node(node.name()).unwrap()); - - // Copy properties from host CPU node - for prop in node.propertys() { - new_fdt.property(prop.name, prop.raw_value()).unwrap(); - } - - previous_node_level = node.level; - } - } - } - - // End all unclosed nodes - while let Some(node) = node_stack.pop() { - previous_node_level -= 1; - new_fdt.end_node(node).unwrap(); - } - assert_eq!(previous_node_level, 0); - - new_fdt.finish().unwrap() -} diff --git a/os/axvisor/src/vmm/fdt/device.rs b/os/axvisor/src/vmm/fdt/device.rs deleted file mode 100644 index 9553ae2b8..000000000 --- a/os/axvisor/src/vmm/fdt/device.rs +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Device passthrough and dependency analysis for FDT processing. - -use alloc::{ - collections::{BTreeMap, BTreeSet}, - string::{String, ToString}, - vec::Vec, -}; -use axvm::config::AxVMConfig; -use fdt_parser::{Fdt, Node}; - -/// Return the collection of all passthrough devices in the configuration file and newly added devices found -pub fn find_all_passthrough_devices(vm_cfg: &mut AxVMConfig, fdt: &Fdt) -> Vec { - let initial_device_count = vm_cfg.pass_through_devices().len(); - - // Pre-build node cache, store all nodes by path to improve lookup performance - let node_cache: BTreeMap> = build_optimized_node_cache(fdt); - - // Get the list of configured device names - let initial_device_names: Vec = vm_cfg - .pass_through_devices() - .iter() - .map(|dev| dev.name.clone()) - .collect(); - - // Phase 1: Discover descendant nodes of all passthrough devices in the configuration file - // Build a set of configured devices, using BTreeSet to improve lookup efficiency - let mut configured_device_names: BTreeSet = - initial_device_names.iter().cloned().collect(); - - // Used to store newly discovered related device names - let mut additional_device_names = Vec::new(); - - // Phase 1: Process initial devices and their descendant nodes - // Note: Directly use device paths instead of device names - for device_name in &initial_device_names { - // Get all descendant node paths for this device - let descendant_paths = get_descendant_nodes_by_path(&node_cache, device_name); - trace!( - "Found {} descendant paths for {}", - descendant_paths.len(), - device_name - ); - - for descendant_path in descendant_paths { - if !configured_device_names.contains(&descendant_path) { - trace!("Found descendant device: {descendant_path}"); - configured_device_names.insert(descendant_path.clone()); - - additional_device_names.push(descendant_path.clone()); - } else { - trace!("Device already exists: {descendant_path}"); - } - } - } - - info!( - "Phase 1 completed: Found {} new descendant device names", - additional_device_names.len() - ); - - // Phase 2: Discover dependency nodes for all existing devices (including descendant devices) - let mut dependency_device_names = Vec::new(); - // Use a work queue of device names, including initial devices and descendant device names - let mut devices_to_process: Vec = configured_device_names.iter().cloned().collect(); - let mut processed_devices: BTreeSet = BTreeSet::new(); - - // Build phandle mapping table - let phandle_map = build_phandle_map(fdt); - - // Use work queue to recursively find all dependent devices - while let Some(device_node_path) = devices_to_process.pop() { - // Avoid processing the same device repeatedly - if processed_devices.contains(&device_node_path) { - continue; - } - processed_devices.insert(device_node_path.clone()); - - trace!("Analyzing dependencies for device: {device_node_path}"); - - // Find direct dependencies of the current device - let dependencies = find_device_dependencies(&device_node_path, &phandle_map, &node_cache); - trace!( - "Found {} dependencies: {:?}", - dependencies.len(), - dependencies - ); - for dep_node_name in dependencies { - // Check if dependency is already in configuration - if !configured_device_names.contains(&dep_node_name) { - trace!("Found new dependency device: {dep_node_name}"); - dependency_device_names.push(dep_node_name.clone()); - - // Add dependency device name to work queue to further find its dependencies - devices_to_process.push(dep_node_name.clone()); - configured_device_names.insert(dep_node_name.clone()); - } - } - } - - info!( - "Phase 2 completed: Found {} new dependency device names", - dependency_device_names.len() - ); - - // Phase 3: Find all excluded devices and remove them from the list - // Convert Vec> to Vec - let excluded_device_path: Vec = vm_cfg - .excluded_devices() - .iter() - .flatten() - .cloned() - .collect(); - let mut all_excludes_devices = excluded_device_path.clone(); - let mut process_excludeds: BTreeSet = excluded_device_path.iter().cloned().collect(); - - for device_path in &excluded_device_path { - // Get all descendant node paths for this device - let descendant_paths = get_descendant_nodes_by_path(&node_cache, device_path); - info!( - "Found {} descendant paths for {}", - descendant_paths.len(), - device_path - ); - - for descendant_path in descendant_paths { - if !process_excludeds.contains(&descendant_path) { - trace!("Found descendant device: {descendant_path}"); - process_excludeds.insert(descendant_path.clone()); - - all_excludes_devices.push(descendant_path.clone()); - } else { - trace!("Device already exists: {descendant_path}"); - } - } - } - info!("Found excluded devices: {all_excludes_devices:?}"); - - // Merge all device name lists - let mut all_device_names = initial_device_names.clone(); - all_device_names.extend(additional_device_names); - all_device_names.extend(dependency_device_names); - - // Remove excluded devices from the final list - if !all_excludes_devices.is_empty() { - info!( - "Removing {} excluded devices from the list", - all_excludes_devices.len() - ); - let excluded_set: BTreeSet = all_excludes_devices.into_iter().collect(); - - // Filter out excluded devices - all_device_names.retain(|device_name| { - let should_keep = !excluded_set.contains(device_name); - if !should_keep { - info!("Excluding device: {device_name}"); - } - should_keep - }); - } - - // Phase 4: remove root node from the list - all_device_names.retain(|device_name| device_name != "/"); - - let final_device_count = all_device_names.len(); - info!( - "Passthrough devices analysis completed. Total devices: {} (added: {})", - final_device_count, - final_device_count - initial_device_count - ); - - // Print final device list - for (i, device_name) in all_device_names.iter().enumerate() { - trace!("Final passthrough device[{i}]: {device_name}"); - } - - all_device_names -} - -/// Build the full path of a node based on node level relationships -/// Build the path by traversing all nodes and constructing paths based on level relationships to avoid path conflicts for nodes with the same name -pub fn build_node_path(all_nodes: &[Node], target_index: usize) -> String { - let mut path_stack: Vec = Vec::new(); - - for node in all_nodes.iter().take(target_index + 1) { - let level = node.level; - - if level == 1 { - path_stack.clear(); - if node.name() != "/" { - path_stack.push(node.name().to_string()); - } - } else { - while path_stack.len() >= level - 1 { - path_stack.pop(); - } - path_stack.push(node.name().to_string()); - } - } - - // Build the full path of the current node - if path_stack.is_empty() || (path_stack.len() == 1 && path_stack[0] == "/") { - "/".to_string() - } else { - "/".to_string() + &path_stack.join("/") - } -} - -/// Build a simplified node cache table, traverse all nodes once and group by full path -/// Use level relationships to directly build paths, avoiding path conflicts for nodes with the same name -pub fn build_optimized_node_cache<'a>(fdt: &'a Fdt) -> BTreeMap>> { - let mut node_cache: BTreeMap>> = BTreeMap::new(); - - let all_nodes: Vec = fdt.all_nodes().collect(); - - for (index, node) in all_nodes.iter().enumerate() { - let node_path = build_node_path(&all_nodes, index); - if let Some(existing_nodes) = node_cache.get(&node_path) - && !existing_nodes.is_empty() - { - error!( - "Duplicate node path found: {} for node '{}' at level {}, existing node: '{}'", - node_path, - node.name(), - node.level, - existing_nodes[0].name() - ); - } - - trace!( - "Adding node to cache: {} (level: {}, index: {})", - node_path, node.level, index - ); - node_cache.entry(node_path).or_default().push(node.clone()); - } - - debug!( - "Built simplified node cache with {} unique device paths", - node_cache.len() - ); - node_cache -} - -/// Build a mapping table from phandle to node information, optimized version using fdt-parser convenience methods -/// Use full path instead of node name -/// Use level relationships to directly build paths, avoiding path conflicts for nodes with the same name -fn build_phandle_map(fdt: &Fdt) -> BTreeMap)> { - let mut phandle_map = BTreeMap::new(); - - let all_nodes: Vec = fdt.all_nodes().collect(); - - for (index, node) in all_nodes.iter().enumerate() { - let node_path = build_node_path(&all_nodes, index); - - // Collect node properties - let mut phandle = None; - let mut cells_map = BTreeMap::new(); - for prop in node.propertys() { - match prop.name { - "phandle" | "linux,phandle" => { - phandle = Some(prop.u32()); - } - "#address-cells" - | "#size-cells" - | "#clock-cells" - | "#reset-cells" - | "#gpio-cells" - | "#interrupt-cells" - | "#power-domain-cells" - | "#thermal-sensor-cells" - | "#phy-cells" - | "#dma-cells" - | "#sound-dai-cells" - | "#mbox-cells" - | "#pwm-cells" - | "#iommu-cells" => { - cells_map.insert(prop.name.to_string(), prop.u32()); - } - _ => {} - } - } - - // If phandle is found, store it together with the node's full path - if let Some(ph) = phandle { - phandle_map.insert(ph, (node_path, cells_map)); - } - } - phandle_map -} - -/// Parse properties containing phandle references intelligently based on #*-cells properties -/// Supports multiple formats: -/// - Single phandle: \ -/// - phandle+specifier: \ -/// - Multiple phandle references: \ -fn parse_phandle_property_with_cells( - prop_data: &[u8], - prop_name: &str, - phandle_map: &BTreeMap)>, -) -> Vec<(u32, Vec)> { - let mut results = Vec::new(); - - debug!( - "Parsing property '{}' with cells info, data length: {} bytes", - prop_name, - prop_data.len() - ); - - if prop_data.is_empty() || !prop_data.len().is_multiple_of(4) { - warn!( - "Property '{}' data length ({} bytes) is invalid", - prop_name, - prop_data.len() - ); - return results; - } - - let u32_values: Vec = prop_data - .chunks(4) - .map(|chunk| u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]])) - .collect(); - - let mut i = 0; - while i < u32_values.len() { - let potential_phandle = u32_values[i]; - - // Check if it's a valid phandle - if let Some((device_name, cells_info)) = phandle_map.get(&potential_phandle) { - // Determine the number of cells required based on property name - let cells_count = get_cells_count_for_property(prop_name, cells_info); - trace!( - "Property '{prop_name}' requires {cells_count} cells for device '{device_name}'" - ); - - // Check if there's enough data - if i + cells_count < u32_values.len() { - let specifiers: Vec = u32_values[i + 1..=i + cells_count].to_vec(); - debug!( - "Parsed phandle reference: phandle={potential_phandle:#x}, specifiers={specifiers:?}" - ); - results.push((potential_phandle, specifiers)); - i += cells_count + 1; // Skip phandle and all specifiers - } else { - warn!( - "Property:{} not enough data for phandle {:#x}, expected {} cells but only {} values remaining", - prop_name, - potential_phandle, - cells_count, - u32_values.len() - i - 1 - ); - break; - } - } else { - // If not a valid phandle, skip this value - i += 1; - } - } - - results -} - -/// Determine the required number of cells based on property name and target node's cells information -fn get_cells_count_for_property(prop_name: &str, cells_info: &BTreeMap) -> usize { - let cells_property = match prop_name { - "clocks" | "assigned-clocks" => "#clock-cells", - "resets" => "#reset-cells", - "power-domains" => "#power-domain-cells", - "phys" => "#phy-cells", - "interrupts" | "interrupts-extended" => "#interrupt-cells", - "gpios" => "#gpio-cells", - _ if prop_name.ends_with("-gpios") || prop_name.ends_with("-gpio") => "#gpio-cells", - "dmas" => "#dma-cells", - "thermal-sensors" => "#thermal-sensor-cells", - "sound-dai" => "#sound-dai-cells", - "mboxes" => "#mbox-cells", - "pwms" => "#pwm-cells", - _ => { - debug!("Unknown property '{prop_name}', defaulting to 0 cell"); - return 0; - } - }; - - cells_info.get(cells_property).copied().unwrap_or(0) as usize -} - -/// Generic phandle property parsing function -/// Parse phandle references according to cells information with correct block size -/// Support single phandle and multiple phandle+specifier formats -/// Return full path instead of node name -fn parse_phandle_property( - prop_data: &[u8], - prop_name: &str, - phandle_map: &BTreeMap)>, -) -> Vec { - let mut dependencies = Vec::new(); - - let phandle_refs = parse_phandle_property_with_cells(prop_data, prop_name, phandle_map); - - for (phandle, specifiers) in phandle_refs { - if let Some((device_path, _cells_info)) = phandle_map.get(&phandle) { - let spec_info = if !specifiers.is_empty() { - format!(" (specifiers: {specifiers:?})") - } else { - String::new() - }; - debug!( - "Found {prop_name} dependency: phandle={phandle:#x}, device={device_path}{spec_info}" - ); - dependencies.push(device_path.clone()); - } - } - - dependencies -} - -/// Device property classifier - used to identify properties that require special handling -struct DevicePropertyClassifier; - -impl DevicePropertyClassifier { - /// Phandle properties that require special handling - includes all properties that need dependency resolution - const PHANDLE_PROPERTIES: &'static [&'static str] = &[ - "clocks", - "power-domains", - "phys", - "resets", - "dmas", - "thermal-sensors", - "mboxes", - "assigned-clocks", - "interrupt-parent", - "phy-handle", - "msi-parent", - "memory-region", - "syscon", - "regmap", - "iommus", - "interconnects", - "nvmem-cells", - "sound-dai", - "pinctrl-0", - "pinctrl-1", - "pinctrl-2", - "pinctrl-3", - "pinctrl-4", - ]; - - /// Determine if it's a phandle property that requires handling - fn is_phandle_property(prop_name: &str) -> bool { - Self::PHANDLE_PROPERTIES.contains(&prop_name) - || prop_name.ends_with("-supply") - || prop_name == "gpios" - || prop_name.ends_with("-gpios") - || prop_name.ends_with("-gpio") - || (prop_name.contains("cells") && !prop_name.starts_with("#") && prop_name.len() >= 4) - } -} - -/// Find device dependencies -fn find_device_dependencies( - device_node_path: &str, - phandle_map: &BTreeMap)>, - node_cache: &BTreeMap>, // Add node_cache parameter -) -> Vec { - let mut dependencies = Vec::new(); - - // Directly find nodes from node_cache, avoiding traversing all nodes - if let Some(nodes) = node_cache.get(device_node_path) { - // Traverse all properties of nodes to find dependencies - for node in nodes { - for prop in node.propertys() { - // Determine if it's a phandle property that needs to be processed - if DevicePropertyClassifier::is_phandle_property(prop.name) { - let mut prop_deps = - parse_phandle_property(prop.raw_value(), prop.name, phandle_map); - dependencies.append(&mut prop_deps); - } - } - } - } - - dependencies -} - -/// Get all descendant nodes based on parent node path (including child nodes, grandchild nodes, etc.) -/// Find all descendant nodes by looking up nodes with parent node path as prefix in node_cache -fn get_descendant_nodes_by_path<'a>( - node_cache: &'a BTreeMap>>, - parent_path: &str, -) -> Vec { - let mut descendant_paths = Vec::new(); - - // Special handling if parent path is root path - let search_prefix = if parent_path == "/" { - "/".to_string() - } else { - parent_path.to_string() + "/" - }; - - // Traverse node_cache, find all nodes with parent path as prefix - for path in node_cache.keys() { - // Check if path has parent path as prefix (and is not the parent path itself) - if path.starts_with(&search_prefix) && path.len() > search_prefix.len() { - // This is a descendant node path, add to results - descendant_paths.push(path.clone()); - } - } - - descendant_paths -} diff --git a/os/axvisor/src/vmm/fdt/mod.rs b/os/axvisor/src/vmm/fdt/mod.rs deleted file mode 100644 index d648ae03f..000000000 --- a/os/axvisor/src/vmm/fdt/mod.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! FDT (Flattened Device Tree) processing module for AxVisor. -//! -//! This module provides functionality for parsing and processing device tree blobs, -//! including CPU configuration, passthrough device detection, and FDT generation. - -mod create; -mod device; -mod parser; -mod print; -mod vm_fdt; - -use alloc::collections::BTreeMap; -use alloc::vec::Vec; -use axvm::config::{AxVMConfig, AxVMCrateConfig}; -use fdt_parser::Fdt; -use lazyinit::LazyInit; -use spin::Mutex; - -pub use parser::*; -// pub use print::print_fdt; -pub use create::*; -pub use device::build_node_path; - -use crate::vmm::config::{config, get_vm_dtb_arc}; - -// DTB cache for generated device trees -static GENERATED_DTB_CACHE: LazyInit>>> = LazyInit::new(); - -/// Initialize the DTB cache -pub fn init_dtb_cache() { - GENERATED_DTB_CACHE.init_once(Mutex::new(BTreeMap::new())); -} - -/// Get reference to the DTB cache -pub fn dtb_cache() -> &'static Mutex>> { - GENERATED_DTB_CACHE.get().unwrap() -} - -/// Generate guest FDT cache the result -/// # Return Value -/// Returns the generated DTB data and stores it in the global cache -pub fn crate_guest_fdt_with_cache(dtb_data: Vec, crate_config: &AxVMCrateConfig) { - // Store data in global cache - let mut cache_lock = dtb_cache().lock(); - cache_lock.insert(crate_config.base.id, dtb_data); -} - -/// Handle all FDT-related operations for aarch64 architecture -pub fn handle_fdt_operations(vm_config: &mut AxVMConfig, vm_create_config: &AxVMCrateConfig) { - let host_fdt_bytes = get_host_fdt(); - let host_fdt = Fdt::from_bytes(host_fdt_bytes) - .map_err(|e| format!("Failed to parse FDT: {e:#?}")) - .expect("Failed to parse FDT"); - set_phys_cpu_sets(vm_config, &host_fdt, vm_create_config); - - if let Some(provided_dtb) = get_developer_provided_dtb(vm_config, vm_create_config) { - info!("VM[{}] found DTB , parsing...", vm_config.id()); - update_provided_fdt(&provided_dtb, host_fdt_bytes, vm_create_config); - } else { - info!( - "VM[{}] DTB not found, generating based on the configuration file.", - vm_config.id() - ); - setup_guest_fdt_from_vmm(host_fdt_bytes, vm_config, vm_create_config); - } - - // Overlay VM config with the given DTB. - if let Some(dtb_arc) = get_vm_dtb_arc(vm_config) { - let dtb = dtb_arc.as_ref(); - parse_passthrough_devices_address(vm_config, dtb); - parse_vm_interrupt(vm_config, dtb); - } else { - error!( - "VM[{}] DTB not found in memory, skipping...", - vm_config.id() - ); - } -} - -pub fn get_developer_provided_dtb( - vm_cfg: &AxVMConfig, - crate_config: &AxVMCrateConfig, -) -> Option> { - match crate_config.kernel.image_location.as_deref() { - Some("memory") => { - let vm_imags = config::get_memory_images() - .iter() - .find(|&v| v.id == vm_cfg.id())?; - - if let Some(dtb) = vm_imags.dtb { - info!("DTB file in memory, size: 0x{:x}", dtb.len()); - return Some(dtb.to_vec()); - } - } - #[cfg(feature = "fs")] - Some("fs") => { - use axerrno::ax_err_type; - use std::io::{BufReader, Read}; - if let Some(dtb_path) = &crate_config.kernel.dtb_path { - let (dtb_file, dtb_size) = - crate::vmm::images::fs::open_image_file(dtb_path).unwrap(); - info!("DTB file in fs, size: 0x{:x}", dtb_size); - - let mut file = BufReader::new(dtb_file); - let mut dtb_buffer = vec![0; dtb_size]; - - file.read_exact(&mut dtb_buffer) - .map_err(|err| { - ax_err_type!( - Io, - format!("Failed in reading from file {}, err {:?}", dtb_path, err) - ) - }) - .unwrap(); - return Some(dtb_buffer); - } - } - _ => unimplemented!( - "Check your \"image_location\" in config.toml, \"memory\" and \"fs\" are supported,\n." - ), - } - None -} diff --git a/os/axvisor/src/vmm/fdt/parser.rs b/os/axvisor/src/vmm/fdt/parser.rs deleted file mode 100644 index e964ffc64..000000000 --- a/os/axvisor/src/vmm/fdt/parser.rs +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! FDT parsing and processing functionality. - -use alloc::{string::ToString, vec::Vec}; -use axvm::config::{AxVMConfig, AxVMCrateConfig, PassThroughDeviceConfig}; -use fdt_parser::{Fdt, FdtHeader, PciRange, PciSpace}; - -use crate::vmm::fdt::crate_guest_fdt_with_cache; -use crate::vmm::fdt::create::update_cpu_node; - -pub fn get_host_fdt() -> &'static [u8] { - const FDT_VALID_MAGIC: u32 = 0xd00d_feed; - let bootarg: usize = std::os::arceos::modules::axhal::dtb::get_bootarg(); - let header = unsafe { - core::slice::from_raw_parts(bootarg as *const u8, core::mem::size_of::()) - }; - let fdt_header = FdtHeader::from_bytes(header) - .map_err(|e| format!("Failed to parse FDT header: {e:#?}")) - .unwrap(); - - if fdt_header.magic.get() != FDT_VALID_MAGIC { - error!( - "FDT magic is invalid, expected {:#x}, got {:#x}", - FDT_VALID_MAGIC, - fdt_header.magic.get() - ); - } - - unsafe { core::slice::from_raw_parts(bootarg as *const u8, fdt_header.total_size()) } -} - -pub fn setup_guest_fdt_from_vmm( - fdt_bytes: &[u8], - vm_cfg: &mut AxVMConfig, - crate_config: &AxVMCrateConfig, -) { - let fdt = Fdt::from_bytes(fdt_bytes) - .map_err(|e| format!("Failed to parse FDT: {e:#?}")) - .expect("Failed to parse FDT"); - - // Call the modified function and get the returned device name list - let passthrough_device_names = super::device::find_all_passthrough_devices(vm_cfg, &fdt); - - let dtb_data = super::create::crate_guest_fdt(&fdt, &passthrough_device_names, crate_config); - crate_guest_fdt_with_cache(dtb_data, crate_config); -} - -pub fn set_phys_cpu_sets(vm_cfg: &mut AxVMConfig, fdt: &Fdt, crate_config: &AxVMCrateConfig) { - // Find and parse CPU information from host DTB - let host_cpus: Vec<_> = fdt.find_nodes("/cpus/cpu").collect(); - info!("Found {} host CPU nodes", &host_cpus.len()); - - let phys_cpu_ids = crate_config - .base - .phys_cpu_ids - .as_ref() - .expect("ERROR: phys_cpu_ids not found in config.toml"); - - // Collect all CPU node information into Vec to avoid using iterators multiple times - let cpu_nodes_info: Vec<_> = host_cpus - .iter() - .filter_map(|cpu_node| { - if let Some(mut cpu_reg) = cpu_node.reg() { - if let Some(r) = cpu_reg.next() { - info!( - "CPU node: {}, phys_cpu_id: 0x{:x}", - cpu_node.name(), - r.address - ); - Some((cpu_node.name().to_string(), r.address as usize)) - } else { - None - } - } else { - None - } - }) - .collect(); - // Create mapping from phys_cpu_id to physical CPU index - // Collect all unique CPU addresses, maintaining the order of appearance in the device tree - let mut unique_cpu_addresses = Vec::new(); - for (_, cpu_address) in &cpu_nodes_info { - if !unique_cpu_addresses.contains(cpu_address) { - unique_cpu_addresses.push(*cpu_address); - } else { - panic!("Duplicate CPU address found"); - } - } - - // Assign index to each CPU address in the device tree and print detailed information - for (index, &cpu_address) in unique_cpu_addresses.iter().enumerate() { - // Find all CPU nodes using this address - for (cpu_name, node_address) in &cpu_nodes_info { - if *node_address == cpu_address { - debug!( - " CPU node: {cpu_name}, address: 0x{cpu_address:x}, assigned index: {index}" - ); - break; // Print each address only once - } - } - } - - // Calculate phys_cpu_sets based on phys_cpu_ids in vcpu_mappings - let mut new_phys_cpu_sets = Vec::new(); - for phys_cpu_id in phys_cpu_ids { - // Find the index corresponding to phys_cpu_id in unique_cpu_addresses - if let Some(cpu_index) = unique_cpu_addresses - .iter() - .position(|&addr| addr == *phys_cpu_id) - { - let cpu_mask = 1usize << cpu_index; // Convert index to mask bit - new_phys_cpu_sets.push(cpu_mask); - debug!( - "vCPU {} with phys_cpu_id 0x{:x} mapped to CPU index {} (mask: 0x{:x})", - vm_cfg.id(), - phys_cpu_id, - cpu_index, - cpu_mask - ); - } else { - error!( - "vCPU {} with phys_cpu_id 0x{:x} not found in device tree!", - vm_cfg.id(), - phys_cpu_id - ); - } - } - - // Update phys_cpu_sets in VM configuration (if VM configuration supports setting) - info!("Calculated phys_cpu_sets: {new_phys_cpu_sets:?}"); - - vm_cfg - .phys_cpu_ls_mut() - .set_guest_cpu_sets(new_phys_cpu_sets); - - debug!( - "vcpu_mappings: {:?}", - vm_cfg.phys_cpu_ls_mut().get_vcpu_affinities_pcpu_ids() - ); -} - -/// Add address mapping configuration for a device -fn add_device_address_config( - vm_cfg: &mut AxVMConfig, - node_name: &str, - base_address: usize, - size: usize, - index: usize, - prefix: Option<&str>, -) { - // Only process devices with address information - if size == 0 { - return; - } - - // Create a device configuration for each address segment - let device_name = if index == 0 { - match prefix { - Some(p) => format!("{node_name}-{p}"), - None => node_name.to_string(), - } - } else { - match prefix { - Some(p) => format!("{node_name}-{p}-region{index}"), - None => format!("{node_name}-region{index}"), - } - }; - - // Add new device configuration - let pt_dev = axvm::config::PassThroughDeviceConfig { - name: device_name, - base_gpa: base_address, - base_hpa: base_address, - length: size, - irq_id: 0, - }; - vm_cfg.add_pass_through_device(pt_dev); -} - -/// Add ranges property configuration for PCIe devices -fn add_pci_ranges_config(vm_cfg: &mut AxVMConfig, node_name: &str, range: &PciRange, index: usize) { - let base_address = range.cpu_address as usize; - let size = range.size as usize; - - // Only process devices with address information - if size == 0 { - return; - } - - // Create a device configuration for each address segment - let prefix = match range.space { - PciSpace::Configuration => "config", - PciSpace::IO => "io", - PciSpace::Memory32 => "mem32", - PciSpace::Memory64 => "mem64", - }; - - let device_name = if index == 0 { - format!("{node_name}-{prefix}") - } else { - format!("{node_name}-{prefix}-region{index}") - }; - - // Add new device configuration - let pt_dev = axvm::config::PassThroughDeviceConfig { - name: device_name, - base_gpa: base_address, - base_hpa: base_address, - length: size, - irq_id: 0, - }; - vm_cfg.add_pass_through_device(pt_dev); - - trace!( - "Added PCIe passthrough device {}: base=0x{:x}, size=0x{:x}, space={:?}", - node_name, base_address, size, range.space - ); -} - -pub fn parse_passthrough_devices_address(vm_cfg: &mut AxVMConfig, dtb: &[u8]) { - let devices = vm_cfg.pass_through_devices().to_vec(); - if !devices.is_empty() && devices[0].length != 0 { - for (index, device) in devices.iter().enumerate() { - add_device_address_config( - vm_cfg, - &device.name, - device.base_gpa, - device.length, - index, - None, - ); - } - } else { - let fdt = Fdt::from_bytes(dtb) - .expect("Failed to parse DTB image, perhaps the DTB is invalid or corrupted"); - - // Clear existing passthrough device configurations - vm_cfg.clear_pass_through_devices(); - - // Traverse all device tree nodes - for node in fdt.all_nodes() { - // Skip root node - if node.name() == "/" || node.name().starts_with("memory") { - continue; - } - - let node_name = node.name().to_string(); - - // Check if it's a PCIe device node - if node_name.starts_with("pcie@") || node_name.contains("pci") { - // Process PCIe device's ranges property - if let Some(pci) = node.clone().into_pci() - && let Ok(ranges) = pci.ranges() - { - for (index, range) in ranges.enumerate() { - add_pci_ranges_config(vm_cfg, &node_name, &range, index); - } - } - - // Process PCIe device's reg property (ECAM space) - if let Some(reg_iter) = node.reg() { - for (index, reg) in reg_iter.enumerate() { - let base_address = reg.address as usize; - let size = reg.size.unwrap_or(0); - - add_device_address_config( - vm_cfg, - &node_name, - base_address, - size, - index, - Some("ecam"), - ); - } - } - } else { - // Get device's reg property (process regular devices) - if let Some(reg_iter) = node.reg() { - // Process all address segments of the device - for (index, reg) in reg_iter.enumerate() { - // Get device's address and size information - let base_address = reg.address as usize; - let size = reg.size.unwrap_or(0); - - add_device_address_config( - vm_cfg, - &node_name, - base_address, - size, - index, - None, - ); - } - } - } - } - trace!( - "All passthrough devices: {:#x?}", - vm_cfg.pass_through_devices() - ); - debug!( - "Finished parsing passthrough devices, total: {}", - vm_cfg.pass_through_devices().len() - ); - } -} - -pub fn parse_vm_interrupt(vm_cfg: &mut AxVMConfig, dtb: &[u8]) { - const GIC_PHANDLE: usize = 1; - let fdt = Fdt::from_bytes(dtb) - .expect("Failed to parse DTB image, perhaps the DTB is invalid or corrupted"); - - for node in fdt.all_nodes() { - let name = node.name(); - - if name.starts_with("memory") { - continue; - } - // Skip the interrupt controller, as we will use vGIC - // TODO: filter with compatible property and parse its phandle from DT; maybe needs a second pass? - else if name.starts_with("interrupt-controller") - || name.starts_with("intc") - || name.starts_with("its") - { - info!("skipping node {name} to use vGIC"); - continue; - } - - // Collect all GIC_SPI interrupts and add them to vGIC - if let Some(interrupts) = node.interrupts() { - // TODO: skip non-GIC interrupt - if let Some(parent) = node.interrupt_parent() { - trace!("node: {}, intr parent: {}", name, parent.node.name()); - if let Some(phandle) = parent.node.phandle() { - if phandle.as_usize() != GIC_PHANDLE { - debug!( - "node: {}, intr parent: {}, phandle: 0x{:x} is not GIC!", - name, - parent.node.name(), - phandle.as_usize() - ); - } - } else { - warn!( - "node: {}, intr parent: {} no phandle!", - name, - parent.node.name(), - ); - } - } else { - warn!("node: {name} no interrupt parent!"); - } - - for interrupt in interrupts { - // - for (k, v) in interrupt.enumerate() { - match k { - 0 => { - if v == 0 { - trace!("node: {name}, GIC_SPI"); - } else { - debug!("node: {name}, intr type: {v}, not GIC_SPI, not supported!"); - break; - } - } - 1 => { - trace!("node: {name}, interrupt id: 0x{v:x}"); - vm_cfg.add_pass_through_spi(v); - } - 2 => { - trace!("node: {name}, interrupt mode: 0x{v:x}"); - } - _ => { - warn!("unknown interrupt property {k}:0x{v:x}") - } - } - } - } - } - } - - vm_cfg.add_pass_through_device(PassThroughDeviceConfig { - name: "Fake Node".to_string(), - base_gpa: 0x0, - base_hpa: 0x0, - length: 0x20_0000, - irq_id: 0, - }); -} - -pub fn update_provided_fdt(provided_dtb: &[u8], host_dtb: &[u8], crate_config: &AxVMCrateConfig) { - let provided_fdt = Fdt::from_bytes(provided_dtb) - .expect("Failed to parse DTB image, perhaps the DTB is invalid or corrupted"); - let host_fdt = Fdt::from_bytes(host_dtb) - .expect("Failed to parse DTB image, perhaps the DTB is invalid or corrupted"); - let provided_dtb_data = update_cpu_node(&provided_fdt, &host_fdt, crate_config); - crate_guest_fdt_with_cache(provided_dtb_data, crate_config); -} diff --git a/os/axvisor/src/vmm/fdt/print.rs b/os/axvisor/src/vmm/fdt/print.rs deleted file mode 100644 index 96b4785c5..000000000 --- a/os/axvisor/src/vmm/fdt/print.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! FDT parsing and processing functionality. - -use fdt_parser::{Fdt, FdtHeader}; - -#[allow(dead_code)] -pub fn print_fdt(fdt_addr: usize) { - const FDT_VALID_MAGIC: u32 = 0xd00d_feed; - let header = unsafe { - core::slice::from_raw_parts(fdt_addr as *const u8, core::mem::size_of::()) - }; - let fdt_header = FdtHeader::from_bytes(header) - .map_err(|e| format!("Failed to parse FDT header: {e:#?}")) - .unwrap(); - - if fdt_header.magic.get() != FDT_VALID_MAGIC { - error!( - "FDT magic is invalid, expected {:#x}, got {:#x}", - FDT_VALID_MAGIC, - fdt_header.magic.get() - ); - return; - } - - let fdt_bytes = - unsafe { core::slice::from_raw_parts(fdt_addr as *const u8, fdt_header.total_size()) }; - - let fdt = Fdt::from_bytes(fdt_bytes) - .map_err(|e| format!("Failed to parse FDT: {e:#?}")) - .expect("Failed to parse FDT"); - - // Statistics of node count and level distribution - let mut node_count = 0; - let mut level_counts = alloc::collections::BTreeMap::new(); - let mut max_level = 0; - - info!("=== FDT Node Information Statistics ==="); - - // Traverse all nodes once for statistics (following optimization strategy) - for node in fdt.all_nodes() { - node_count += 1; - - // Count nodes by level - *level_counts.entry(node.level).or_insert(0) += 1; - - // Record maximum level - if node.level > max_level { - max_level = node.level; - } - - // Count property numbers - let node_properties_count = node.propertys().count(); - - trace!( - "Node[{}]: {} (Level: {}, Properties: {})", - node_count, - node.name(), - node.level, - node_properties_count - ); - - for prop in node.propertys() { - trace!( - "Properties: {}, Raw_value: {:x?}", - prop.name, - prop.raw_value() - ); - } - } - - info!("=== FDT Statistics Results ==="); - info!("Total node count: {node_count}"); - info!("FDT total size: {} bytes", fdt_header.total_size()); - info!("Maximum level depth: {max_level}"); - - info!("Node distribution by level:"); - for (level, count) in level_counts { - let percentage = (count as f32 / node_count as f32) * 100.0; - info!(" Level {level}: {count} nodes ({percentage:.1}%)"); - } -} - -#[allow(dead_code)] -pub fn print_guest_fdt(fdt_bytes: &[u8]) { - let fdt = Fdt::from_bytes(fdt_bytes) - .map_err(|e| format!("Failed to parse FDT: {e:#?}")) - .expect("Failed to parse FDT"); - // Statistics of node count and level distribution - let mut node_count = 0; - let mut level_counts = alloc::collections::BTreeMap::new(); - let mut max_level = 0; - - info!("=== FDT Node Information Statistics ==="); - - // Traverse all nodes once for statistics (following optimization strategy) - for node in fdt.all_nodes() { - node_count += 1; - - // Count nodes by level - *level_counts.entry(node.level).or_insert(0) += 1; - - // Record maximum level - if node.level > max_level { - max_level = node.level; - } - - // Count property numbers - let node_properties_count = node.propertys().count(); - - info!( - "Node[{}]: {} (Level: {}, Properties: {})", - node_count, - node.name(), - node.level, - node_properties_count - ); - - for prop in node.propertys() { - info!( - "Properties: {}, Raw_value: {:x?}", - prop.name, - prop.raw_value() - ); - } - } - - info!("=== FDT Statistics Results ==="); - info!("Total node count: {node_count}"); - info!("Maximum level depth: {max_level}"); - - info!("Node distribution by level:"); - for (level, count) in level_counts { - let percentage = (count as f32 / node_count as f32) * 100.0; - info!(" Level {level}: {count} nodes ({percentage:.1}%)"); - } -} diff --git a/os/axvisor/src/vmm/fdt/vm_fdt/mod.rs b/os/axvisor/src/vmm/fdt/vm_fdt/mod.rs deleted file mode 100644 index b2ac85663..000000000 --- a/os/axvisor/src/vmm/fdt/vm_fdt/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -mod writer; - -pub use writer::{FdtWriter, FdtWriterNode}; - -/// Magic number used in the FDT header. -pub const FDT_MAGIC: u32 = 0xd00dfeed; - -pub const FDT_BEGIN_NODE: u32 = 0x00000001; -pub const FDT_END_NODE: u32 = 0x00000002; -pub const FDT_PROP: u32 = 0x00000003; -pub const FDT_END: u32 = 0x00000009; - -pub const NODE_NAME_MAX_LEN: usize = 31; -pub const PROPERTY_NAME_MAX_LEN: usize = 63; diff --git a/os/axvisor/src/vmm/fdt/vm_fdt/writer.rs b/os/axvisor/src/vmm/fdt/vm_fdt/writer.rs deleted file mode 100644 index 37f4f5e3b..000000000 --- a/os/axvisor/src/vmm/fdt/vm_fdt/writer.rs +++ /dev/null @@ -1,553 +0,0 @@ -use alloc::collections::BTreeMap; -use alloc::ffi::CString; -use alloc::string::String; -use alloc::vec::Vec; -use core::cmp::{Ord, Ordering}; -use core::convert::TryInto; -use core::fmt; -use core::mem::size_of_val; -use hashbrown::HashSet; - -use super::{ - FDT_BEGIN_NODE, FDT_END, FDT_END_NODE, FDT_MAGIC, FDT_PROP, NODE_NAME_MAX_LEN, - PROPERTY_NAME_MAX_LEN, -}; - -#[derive(Debug, Eq, PartialEq)] -/// Errors associated with creating the Flattened Device Tree. -pub enum Error { - /// Properties may not be added before beginning a node. - PropertyBeforeBeginNode, - /// Properties may not be added after a node has been ended. - PropertyAfterEndNode, - /// Property value size must fit in 32 bits. - PropertyValueTooLarge, - /// Total size must fit in 32 bits. - TotalSizeTooLarge, - /// Strings cannot contain NUL. - InvalidString, - /// Attempted to end a node that was not the most recent. - OutOfOrderEndNode, - /// Attempted to call finish without ending all nodes. - UnclosedNode, - /// Memory reservation is invalid. - InvalidMemoryReservation, - /// Memory reservations are overlapping. - OverlappingMemoryReservations, - /// Invalid node name. - InvalidNodeName, - /// Invalid property name. - InvalidPropertyName, - /// Node depth exceeds FDT_MAX_NODE_DEPTH - NodeDepthTooLarge, - /// Duplicate phandle property - DuplicatePhandle, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::PropertyBeforeBeginNode => { - write!(f, "Properties may not be added before beginning a node") - } - Error::PropertyAfterEndNode => { - write!(f, "Properties may not be added after a node has been ended") - } - Error::PropertyValueTooLarge => write!(f, "Property value size must fit in 32 bits"), - Error::TotalSizeTooLarge => write!(f, "Total size must fit in 32 bits"), - Error::InvalidString => write!(f, "Strings cannot contain NUL"), - Error::OutOfOrderEndNode => { - write!(f, "Attempted to end a node that was not the most recent") - } - Error::UnclosedNode => write!(f, "Attempted to call finish without ending all nodes"), - Error::InvalidMemoryReservation => write!(f, "Memory reservation is invalid"), - Error::OverlappingMemoryReservations => { - write!(f, "Memory reservations are overlapping") - } - Error::InvalidNodeName => write!(f, "Invalid node name"), - Error::InvalidPropertyName => write!(f, "Invalid property name"), - Error::NodeDepthTooLarge => write!(f, "Node depth exceeds FDT_MAX_NODE_DEPTH"), - Error::DuplicatePhandle => write!(f, "Duplicate phandle value"), - } - } -} - -/// Result of a FDT writer operation. -pub type Result = core::result::Result; - -const FDT_HEADER_SIZE: usize = 40; -const FDT_VERSION: u32 = 17; -const FDT_LAST_COMP_VERSION: u32 = 16; -/// The same max depth as in the Linux kernel. -const FDT_MAX_NODE_DEPTH: usize = 64; - -/// Interface for writing a Flattened Devicetree (FDT) and emitting a Devicetree Blob (DTB). -#[derive(Debug)] -pub struct FdtWriter { - data: Vec, - off_mem_rsvmap: u32, - off_dt_struct: u32, - strings: Vec, - string_offsets: BTreeMap, - node_depth: usize, - node_ended: bool, - boot_cpuid_phys: u32, - // The set is used to track the uniqueness of phandle values as required by the spec - // https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#phandle - #[allow(dead_code)] - phandles: HashSet, -} - -/// Reserved physical memory region. -/// -/// This represents an area of physical memory reserved by the firmware and unusable by the OS. -/// For example, this could be used to preserve bootloader code or data used at runtime. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct FdtReserveEntry { - address: u64, - size: u64, -} - -impl FdtReserveEntry { - /// Create a memory reservation for the FDT. - /// - /// # Arguments - /// - /// * address: Physical address of the beginning of the reserved region. - /// * size: Size of the reserved region in bytes. - #[allow(dead_code)] - pub fn new(address: u64, size: u64) -> Result { - if address.checked_add(size).is_none() || size == 0 { - return Err(Error::InvalidMemoryReservation); - } - - Ok(FdtReserveEntry { address, size }) - } -} - -impl Ord for FdtReserveEntry { - fn cmp(&self, other: &Self) -> Ordering { - self.address.cmp(&other.address) - } -} - -impl PartialOrd for FdtReserveEntry { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -// Returns true if there are any overlapping memory reservations. -fn check_overlapping(mem_reservations: &[FdtReserveEntry]) -> Result<()> { - let mut mem_rsvmap_copy = mem_reservations.to_vec(); - mem_rsvmap_copy.sort(); - let overlapping = mem_rsvmap_copy.windows(2).any(|w| { - // The following add cannot overflow because we can only have - // valid FdtReserveEntry (as per the constructor of the type). - w[0].address + w[0].size > w[1].address - }); - - if overlapping { - return Err(Error::OverlappingMemoryReservations); - } - - Ok(()) -} - -// Check if `name` is a valid node name in the form "node-name@unit-address". -// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#node-name-requirements -fn node_name_valid(name: &str) -> bool { - // Special case: allow empty node names. - // This is technically not allowed by the spec, but it seems to be accepted in practice. - if name.is_empty() { - return true; - } - - let mut parts = name.split('@'); - - let node_name = parts.next().unwrap(); // split() always returns at least one part - let unit_address = parts.next(); - - if unit_address.is_some() && parts.next().is_some() { - // Node names should only contain one '@'. - return false; - } - - if node_name.is_empty() || node_name.len() > NODE_NAME_MAX_LEN { - return false; - } - - // if !node_name.starts_with(node_name_valid_first_char) { - // return false; - // } - - if node_name.contains(|c: char| !node_name_valid_char(c)) { - return false; - } - - if let Some(unit_address) = unit_address - && unit_address.contains(|c: char| !node_name_valid_char(c)) - { - return false; - } - - true -} - -fn node_name_valid_char(c: char) -> bool { - c.is_ascii_alphanumeric() || matches!(c, ',' | '.' | '_' | '+' | '-') -} - -#[allow(dead_code)] -fn node_name_valid_first_char(c: char) -> bool { - c.is_ascii_alphabetic() -} - -// Check if `name` is a valid property name. -// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#property-names -fn property_name_valid(name: &str) -> bool { - if name.is_empty() || name.len() > PROPERTY_NAME_MAX_LEN { - return false; - } - - if name.contains(|c: char| !property_name_valid_char(c)) { - return false; - } - - true -} - -fn property_name_valid_char(c: char) -> bool { - matches!(c, '0'..='9' | 'a'..='z' | 'A'..='Z' | ',' | '.' | '_' | '+' | '?' | '#' | '-') -} - -/// Handle to an open node created by `FdtWriter::begin_node`. -/// -/// This must be passed back to `FdtWriter::end_node` to close the nodes. -/// Nodes must be closed in reverse order as they were opened, matching the nesting structure -/// of the devicetree. -#[derive(Debug)] -pub struct FdtWriterNode { - depth: usize, -} - -impl FdtWriter { - /// Create a new Flattened Devicetree writer instance. - pub fn new() -> Result { - FdtWriter::new_with_mem_reserv(&[]) - } - - /// Create a new Flattened Devicetree writer instance. - /// - /// # Arguments - /// - /// `mem_reservations` - reserved physical memory regions to list in the FDT header. - pub fn new_with_mem_reserv(mem_reservations: &[FdtReserveEntry]) -> Result { - let data = vec![0u8; FDT_HEADER_SIZE]; // Reserve space for header. - - let mut fdt = FdtWriter { - data, - off_mem_rsvmap: 0, - off_dt_struct: 0, - strings: Vec::new(), - string_offsets: BTreeMap::new(), - node_depth: 0, - node_ended: false, - boot_cpuid_phys: 0, - phandles: HashSet::new(), - }; - - fdt.align(8); - // This conversion cannot fail since the size of the header is fixed. - fdt.off_mem_rsvmap = fdt.data.len() as u32; - - check_overlapping(mem_reservations)?; - fdt.write_mem_rsvmap(mem_reservations); - - fdt.align(4); - fdt.off_dt_struct = fdt - .data - .len() - .try_into() - .map_err(|_| Error::TotalSizeTooLarge)?; - - Ok(fdt) - } - - fn write_mem_rsvmap(&mut self, mem_reservations: &[FdtReserveEntry]) { - for rsv in mem_reservations { - self.append_u64(rsv.address); - self.append_u64(rsv.size); - } - - self.append_u64(0); - self.append_u64(0); - } - - /// Set the `boot_cpuid_phys` field of the devicetree header. - /// - /// # Example - /// - /// ```rust - /// use vm_fdt::{Error, FdtWriter}; - /// - /// fn create_fdt() -> Result, Error> { - /// let mut fdt = FdtWriter::new()?; - /// fdt.set_boot_cpuid_phys(0x12345678); - /// // ... add other nodes & properties - /// fdt.finish() - /// } - /// - /// # let dtb = create_fdt().unwrap(); - /// ``` - #[allow(dead_code)] - pub fn set_boot_cpuid_phys(&mut self, boot_cpuid_phys: u32) { - self.boot_cpuid_phys = boot_cpuid_phys; - } - - // Append `num_bytes` padding bytes (0x00). - fn pad(&mut self, num_bytes: usize) { - self.data.extend(core::iter::repeat_n(0, num_bytes)); - } - - // Append padding bytes (0x00) until the length of data is a multiple of `alignment`. - fn align(&mut self, alignment: usize) { - let offset = self.data.len() % alignment; - if offset != 0 { - self.pad(alignment - offset); - } - } - - // Rewrite the value of a big-endian u32 within data. - fn update_u32(&mut self, offset: usize, val: u32) { - // Safe to use `+ 4` since we are calling this function with small values, and it's a - // private function. - let data_slice = &mut self.data[offset..offset + 4]; - data_slice.copy_from_slice(&val.to_be_bytes()); - } - - fn append_u32(&mut self, val: u32) { - self.data.extend_from_slice(&val.to_be_bytes()); - } - - fn append_u64(&mut self, val: u64) { - self.data.extend_from_slice(&val.to_be_bytes()); - } - - /// Open a new FDT node. - /// - /// The node must be closed using `end_node`. - /// - /// # Arguments - /// - /// `name` - name of the node; must not contain any NUL bytes. - pub fn begin_node(&mut self, name: &str) -> Result { - if self.node_depth >= FDT_MAX_NODE_DEPTH { - return Err(Error::NodeDepthTooLarge); - } - - let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?; - // The unit adddress part of the node name, if present, is not fully validated - // since the exact requirements depend on the bus mapping. - // https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#node-name-requirements - if !node_name_valid(name) { - return Err(Error::InvalidNodeName); - } - self.append_u32(FDT_BEGIN_NODE); - self.data.extend(name_cstr.to_bytes_with_nul()); - self.align(4); - // This can not overflow due to the `if` at the beginning of the function - // where the current depth is checked against FDT_MAX_NODE_DEPTH. - self.node_depth += 1; - self.node_ended = false; - Ok(FdtWriterNode { - depth: self.node_depth, - }) - } - - /// Close a node previously opened with `begin_node`. - pub fn end_node(&mut self, node: FdtWriterNode) -> Result<()> { - if node.depth != self.node_depth { - return Err(Error::OutOfOrderEndNode); - } - - self.append_u32(FDT_END_NODE); - // This can not underflow. The above `if` makes sure there is at least one open node - // (node_depth >= 1). - self.node_depth -= 1; - self.node_ended = true; - Ok(()) - } - - // Find an existing instance of a string `s`, or add it to the strings block. - // Returns the offset into the strings block. - fn intern_string(&mut self, s: CString) -> Result { - if let Some(off) = self.string_offsets.get(&s) { - Ok(*off) - } else { - let off = self - .strings - .len() - .try_into() - .map_err(|_| Error::TotalSizeTooLarge)?; - self.strings.extend_from_slice(s.to_bytes_with_nul()); - self.string_offsets.insert(s, off); - Ok(off) - } - } - - /// Write a property. - /// - /// # Arguments - /// - /// `name` - name of the property; must not contain any NUL bytes. - /// `val` - value of the property (raw byte array). - pub fn property(&mut self, name: &str, val: &[u8]) -> Result<()> { - if self.node_ended { - return Err(Error::PropertyAfterEndNode); - } - - if self.node_depth == 0 { - return Err(Error::PropertyBeforeBeginNode); - } - - let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?; - - if !property_name_valid(name) { - return Err(Error::InvalidPropertyName); - } - - let len = val - .len() - .try_into() - .map_err(|_| Error::PropertyValueTooLarge)?; - - let nameoff = self.intern_string(name_cstr)?; - self.append_u32(FDT_PROP); - self.append_u32(len); - self.append_u32(nameoff); - self.data.extend_from_slice(val); - self.align(4); - Ok(()) - } - - /// Write an empty property. - #[allow(dead_code)] - pub fn property_null(&mut self, name: &str) -> Result<()> { - self.property(name, &[]) - } - - /// Write a string property. - pub fn property_string(&mut self, name: &str, val: &str) -> Result<()> { - let cstr_value = CString::new(val).map_err(|_| Error::InvalidString)?; - self.property(name, cstr_value.to_bytes_with_nul()) - } - - /// Write a stringlist property. - #[allow(dead_code)] - pub fn property_string_list(&mut self, name: &str, values: Vec) -> Result<()> { - let mut bytes = Vec::new(); - for s in values { - let cstr = CString::new(s).map_err(|_| Error::InvalidString)?; - bytes.extend_from_slice(cstr.to_bytes_with_nul()); - } - self.property(name, &bytes) - } - - /// Write a 32-bit unsigned integer property. - #[allow(dead_code)] - pub fn property_u32(&mut self, name: &str, val: u32) -> Result<()> { - self.property(name, &val.to_be_bytes()) - } - - /// Write a 64-bit unsigned integer property. - #[allow(dead_code)] - pub fn property_u64(&mut self, name: &str, val: u64) -> Result<()> { - self.property(name, &val.to_be_bytes()) - } - - /// Write a property containing an array of 32-bit unsigned integers. - pub fn property_array_u32(&mut self, name: &str, cells: &[u32]) -> Result<()> { - let mut arr = Vec::with_capacity(size_of_val(cells)); - for &c in cells { - arr.extend(c.to_be_bytes()); - } - self.property(name, &arr) - } - - /// Write a property containing an array of 64-bit unsigned integers. - #[allow(dead_code)] - pub fn property_array_u64(&mut self, name: &str, cells: &[u64]) -> Result<()> { - let mut arr = Vec::with_capacity(size_of_val(cells)); - for &c in cells { - arr.extend(c.to_be_bytes()); - } - self.property(name, &arr) - } - - /// Write a [`phandle`](https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html?#phandle) - /// property. The value is checked for uniqueness within the FDT. In the case of a duplicate - /// [`Error::DuplicatePhandle`] is returned. - #[allow(dead_code)] - pub fn property_phandle(&mut self, val: u32) -> Result<()> { - if !self.phandles.insert(val) { - return Err(Error::DuplicatePhandle); - } - self.property("phandle", &val.to_be_bytes()) - } - - /// Finish writing the Devicetree Blob (DTB). - /// - /// Returns the DTB as a vector of bytes, consuming the `FdtWriter`. - pub fn finish(mut self) -> Result> { - if self.node_depth > 0 { - return Err(Error::UnclosedNode); - } - - self.append_u32(FDT_END); - let size_dt_plus_header: u32 = self - .data - .len() - .try_into() - .map_err(|_| Error::TotalSizeTooLarge)?; - // The following operation cannot fail because the total size of data - // also includes the offset, and we checked that `size_dt_plus_header` - // does not wrap around when converted to an u32. - let size_dt_struct = size_dt_plus_header - self.off_dt_struct; - - let off_dt_strings = self - .data - .len() - .try_into() - .map_err(|_| Error::TotalSizeTooLarge)?; - let size_dt_strings = self - .strings - .len() - .try_into() - .map_err(|_| Error::TotalSizeTooLarge)?; - - let totalsize = self - .data - .len() - .checked_add(self.strings.len()) - .ok_or(Error::TotalSizeTooLarge)?; - let totalsize = totalsize.try_into().map_err(|_| Error::TotalSizeTooLarge)?; - - // Finalize the header. - self.update_u32(0, FDT_MAGIC); - self.update_u32(4, totalsize); - self.update_u32(2 * 4, self.off_dt_struct); - self.update_u32(3 * 4, off_dt_strings); - self.update_u32(4 * 4, self.off_mem_rsvmap); - self.update_u32(5 * 4, FDT_VERSION); - self.update_u32(6 * 4, FDT_LAST_COMP_VERSION); - self.update_u32(7 * 4, self.boot_cpuid_phys); - self.update_u32(8 * 4, size_dt_strings); - self.update_u32(9 * 4, size_dt_struct); - - // Add the strings block. - self.data.append(&mut self.strings); - - Ok(self.data) - } -} diff --git a/os/axvisor/src/vmm/hvc.rs b/os/axvisor/src/vmm/hvc.rs deleted file mode 100644 index 578f0b5b8..000000000 --- a/os/axvisor/src/vmm/hvc.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axaddrspace::{GuestPhysAddr, MappingFlags}; -use axerrno::{AxResult, ax_err, ax_err_type}; -use axhvc::{HyperCallCode, HyperCallResult}; - -use crate::vmm::ivc::{self, IVCChannel}; -use crate::vmm::{VCpuRef, VMRef}; - -pub struct HyperCall { - _vcpu: VCpuRef, - vm: VMRef, - code: HyperCallCode, - args: [u64; 6], -} - -impl HyperCall { - pub fn new(vcpu: VCpuRef, vm: VMRef, code: u64, args: [u64; 6]) -> AxResult { - let code = HyperCallCode::try_from(code as u32).map_err(|e| { - warn!("Invalid hypercall code: {code} e {e:?}"); - ax_err_type!(InvalidInput) - })?; - - Ok(Self { - _vcpu: vcpu, - vm, - code, - args, - }) - } - - pub fn execute(&self) -> HyperCallResult { - match self.code { - HyperCallCode::HIVCPublishChannel => { - let key = self.args[0] as usize; - let shm_base_gpa_ptr = GuestPhysAddr::from_usize(self.args[1] as usize); - let shm_size_ptr = GuestPhysAddr::from_usize(self.args[2] as usize); - - info!( - "VM[{}] HyperCall {:?} key {:#x}", - self.vm.id(), - self.code, - key - ); - // User will pass the size of the shared memory region, - // we will allocate the shared memory region based on this size. - let shm_region_size = self.vm.read_from_guest_of::(shm_size_ptr)?; - let (shm_base_gpa, shm_region_size) = self.vm.alloc_ivc_channel(shm_region_size)?; - - let ivc_channel = - IVCChannel::alloc(self.vm.id(), key, shm_region_size, shm_base_gpa)?; - - let actual_size = ivc_channel.size(); - - self.vm.map_region( - shm_base_gpa, - ivc_channel.base_hpa(), - actual_size, - MappingFlags::READ | MappingFlags::WRITE, - )?; - - self.vm - .write_to_guest_of(shm_base_gpa_ptr, &shm_base_gpa.as_usize())?; - self.vm.write_to_guest_of(shm_size_ptr, &actual_size)?; - - ivc::insert_channel(self.vm.id(), ivc_channel)?; - - Ok(0) - } - HyperCallCode::HIVCUnPublishChannel => { - let key = self.args[0] as usize; - - info!( - "VM[{}] HyperCall {:?} with key {:#x}", - self.vm.id(), - self.code, - key - ); - let (base_gpa, size) = ivc::unpublish_channel(self.vm.id(), key)?.unwrap(); - self.vm.unmap_region(base_gpa, size)?; - - Ok(0) - } - HyperCallCode::HIVCSubscribChannel => { - let publisher_vm_id = self.args[0] as usize; - let key = self.args[1] as usize; - let shm_base_gpa_ptr = GuestPhysAddr::from_usize(self.args[2] as usize); - let shm_size_ptr = GuestPhysAddr::from_usize(self.args[3] as usize); - - info!( - "VM[{}] HyperCall {:?} to VM[{}]", - self.vm.id(), - self.code, - publisher_vm_id - ); - - let shm_size = ivc::get_channel_size(publisher_vm_id, key)?; - let (shm_base_gpa, _) = self.vm.alloc_ivc_channel(shm_size)?; - - let (base_hpa, actual_size) = ivc::subscribe_to_channel_of_publisher( - publisher_vm_id, - key, - self.vm.id(), - shm_base_gpa, - )?; - - // TODO: seperate the mapping flags of metadata and data. - self.vm.map_region( - shm_base_gpa, - base_hpa, - actual_size, - MappingFlags::READ | MappingFlags::WRITE, - )?; - - self.vm - .write_to_guest_of(shm_base_gpa_ptr, &shm_base_gpa.as_usize())?; - self.vm.write_to_guest_of(shm_size_ptr, &actual_size)?; - - info!( - "VM[{}] HyperCall HIVC_REGISTER_SUBSCRIBER success, base GPA: {:#x}, size: {}", - self.vm.id(), - shm_base_gpa, - actual_size - ); - - Ok(0) - } - HyperCallCode::HIVCUnSubscribChannel => { - let publisher_vm_id = self.args[0] as usize; - let key = self.args[1] as usize; - - info!( - "VM[{}] HyperCall {:?} from VM[{}]", - self.vm.id(), - self.code, - publisher_vm_id - ); - let (base_gpa, size) = - ivc::unsubscribe_from_channel_of_publisher(publisher_vm_id, key, self.vm.id())?; - self.vm.unmap_region(base_gpa, size)?; - - Ok(0) - } - _ => { - warn!("Unsupported hypercall code: {:?}", self.code); - ax_err!(Unsupported)? - } - } - } -} diff --git a/os/axvisor/src/vmm/images/linux.rs b/os/axvisor/src/vmm/images/linux.rs deleted file mode 100644 index 4efe56a2d..000000000 --- a/os/axvisor/src/vmm/images/linux.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// Architecture variants detected from the image header. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ImageArch { - Riscv { - is_be: bool, - }, - Arm64 { - is_be: bool, - page_size: PageSize, - phys_placement_48bit: bool, - }, -} - -#[allow(unused)] -#[derive(Debug, Clone)] -pub struct Header { - pub text_offset: u64, - pub image_size: u64, - pub arch: ImageArch, -} - -#[allow(unused)] -impl Header { - pub fn parse(image: &[u8]) -> Option { - if let Some(hdr) = ARM64Header::parse(image) { - return Some(Self { - text_offset: hdr.text_offset, - image_size: hdr.image_size, - arch: ImageArch::Arm64 { - is_be: hdr.kernel_is_be(), - page_size: hdr.page_size(), - phys_placement_48bit: hdr.phys_placement_48bit(), - }, - }); - } - - if let Some(hdr) = RiscvHeader::parse(image) { - return Some(Self { - text_offset: hdr.text_offset, - image_size: hdr.image_size, - arch: ImageArch::Riscv { - is_be: hdr.kernel_is_be(), - }, - }); - } - - None - } - - pub fn hdr_size() -> usize { - size_of::() - } -} - -#[allow(unused)] -#[repr(C)] -struct ARM64Header { - code0: u32, - code1: u32, - text_offset: u64, - image_size: u64, - flags: u64, - res2: u64, - res3: u64, - res4: u64, - magic: u32, - res5: u32, -} - -impl ARM64Header { - const MAGIC: u32 = 0x644d5241; // 'ARMd' in little-endian - - fn parse(buffer: &[u8]) -> Option { - if buffer.len() < core::mem::size_of::() { - return None; - } - let hdr: Self = unsafe { core::ptr::read_unaligned(buffer.as_ptr() as *const _) }; - if hdr.magic != Self::MAGIC { - return None; - } - Some(hdr) - } - - /// Return whether the kernel image is big-endian according to flags bit 0. - fn kernel_is_be(&self) -> bool { - (self.flags & 0x1) != 0 - } - - /// Return page size encoded in flags bits 1-2. - fn page_size(&self) -> PageSize { - match (self.flags >> 1) & 0x3 { - 0 => PageSize::Unspecified, - 1 => PageSize::Size4K, - 2 => PageSize::Size16K, - 3 => PageSize::Size64K, - _ => PageSize::Unspecified, - } - } - - /// Return physical placement mode (bit 3): false=default, true=48-bit constrained. - fn phys_placement_48bit(&self) -> bool { - ((self.flags >> 3) & 0x1) != 0 - } -} - -/// Page size encoded in the image header flags (bits 1-2). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PageSize { - Unspecified, - Size4K, - Size16K, - Size64K, -} - -#[allow(unused)] -struct RiscvHeader { - code0: u32, - code1: u32, - text_offset: u64, - image_size: u64, - flags: u64, - version: u32, - res1: u32, - res2: u64, - magic: u64, - magic2: u32, - res4: u32, -} - -impl RiscvHeader { - const MAGIC: u64 = 0x5643534952; // 'RISCV\0\0\0' in little-endian - const MAGIC2: u32 = 0x56534905; // secondary magic - - fn parse(buffer: &[u8]) -> Option { - if buffer.len() < core::mem::size_of::() { - return None; - } - let hdr: Self = unsafe { core::ptr::read_unaligned(buffer.as_ptr() as *const _) }; - if hdr.magic != Self::MAGIC || hdr.magic2 != Self::MAGIC2 { - return None; - } - Some(hdr) - } - - fn kernel_is_be(&self) -> bool { - (self.flags & 0x1) != 0 - } -} diff --git a/os/axvisor/src/vmm/images/mod.rs b/os/axvisor/src/vmm/images/mod.rs deleted file mode 100644 index 1ae83994e..000000000 --- a/os/axvisor/src/vmm/images/mod.rs +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axaddrspace::GuestPhysAddr; -use axerrno::AxResult; - -use axvm::VMMemoryRegion; -use axvm::config::AxVMCrateConfig; -use byte_unit::Byte; - -use crate::hal::CacheOp; -use crate::vmm::VMRef; -use crate::vmm::config::{config, get_vm_dtb_arc}; - -mod linux; - -pub fn get_image_header(config: &AxVMCrateConfig) -> Option { - match config.kernel.image_location.as_deref() { - Some("memory") => with_memory_image(config, linux::Header::parse), - #[cfg(feature = "fs")] - Some("fs") => { - let read_size = linux::Header::hdr_size(); - let data = fs::kernal_read(config, read_size).ok()?; - linux::Header::parse(&data) - } - _ => unimplemented!( - "Check your \"image_location\" in config.toml, \"memory\" and \"fs\" are supported,\n NOTE: \"fs\" feature should be enabled if you want to load images from filesystem. (APP_FEATURES=fs)" - ), - } -} - -fn with_memory_image(config: &AxVMCrateConfig, func: F) -> R -where - F: FnOnce(&[u8]) -> R, -{ - let vm_imags = config::get_memory_images() - .iter() - .find(|&v| v.id == config.base.id) - .expect("VM images is missed, Perhaps add `VM_CONFIGS=PATH/CONFIGS/FILE` command."); - - func(vm_imags.kernel) -} - -pub struct ImageLoader { - main_memory: VMMemoryRegion, - vm: VMRef, - config: AxVMCrateConfig, - kernel_load_gpa: GuestPhysAddr, - bios_load_gpa: Option, - dtb_load_gpa: Option, - ramdisk_load_gpa: Option, -} - -impl ImageLoader { - pub fn new(main_memory: VMMemoryRegion, config: AxVMCrateConfig, vm: VMRef) -> Self { - Self { - main_memory, - vm, - config, - kernel_load_gpa: GuestPhysAddr::default(), - bios_load_gpa: None, - dtb_load_gpa: None, - ramdisk_load_gpa: None, - } - } - - pub fn load(&mut self) -> AxResult { - info!( - "Loading VM[{}] images into memory region: gpa={:#x}, hva={:#x}, size={:#}", - self.vm.id(), - self.main_memory.gpa, - self.main_memory.hva, - Byte::from(self.main_memory.size()) - ); - - self.vm.with_config(|config| { - self.kernel_load_gpa = config.image_config.kernel_load_gpa; - self.dtb_load_gpa = config.image_config.dtb_load_gpa; - self.bios_load_gpa = config.image_config.bios_load_gpa; - self.ramdisk_load_gpa = config.image_config.ramdisk_load_gpa; - }); - - match self.config.kernel.image_location.as_deref() { - Some("memory") => self.load_vm_images_from_memory(), - #[cfg(feature = "fs")] - Some("fs") => fs::load_vm_images_from_filesystem(self), - _ => unimplemented!( - "Check your \"image_location\" in config.toml, \"memory\" and \"fs\" are supported,\n NOTE: \"fs\" feature should be enabled if you want to load images from filesystem. (APP_FEATURES=fs)" - ), - } - } - - /// Load VM images from memory - /// into the guest VM's memory space based on the VM configuration. - fn load_vm_images_from_memory(&self) -> AxResult { - info!("Loading VM[{}] images from memory", self.config.base.id); - - let vm_imags = config::get_memory_images() - .iter() - .find(|&v| v.id == self.config.base.id) - .expect("VM images is missed, Perhaps add `VM_CONFIGS=PATH/CONFIGS/FILE` command."); - - load_vm_image_from_memory(vm_imags.kernel, self.kernel_load_gpa, self.vm.clone()) - .expect("Failed to load VM images"); - // Load DTB image - let vm_config = axvm::config::AxVMConfig::from(self.config.clone()); - - if let Some(dtb_arc) = get_vm_dtb_arc(&vm_config) { - let _dtb_slice: &[u8] = &dtb_arc; - #[cfg(target_arch = "aarch64")] - crate::vmm::fdt::update_fdt( - core::ptr::NonNull::new(_dtb_slice.as_ptr() as *mut u8).unwrap(), - _dtb_slice.len(), - self.vm.clone(), - ); - } else { - info!("dtb_load_gpa not provided"); - } - - // Load BIOS image - if let Some(buffer) = vm_imags.bios { - load_vm_image_from_memory(buffer, self.bios_load_gpa.unwrap(), self.vm.clone()) - .expect("Failed to load BIOS images"); - } - - // Load Ramdisk image - if let Some(buffer) = vm_imags.ramdisk { - load_vm_image_from_memory(buffer, self.ramdisk_load_gpa.unwrap(), self.vm.clone()) - .expect("Failed to load Ramdisk images"); - }; - - Ok(()) - } -} - -pub fn load_vm_image_from_memory( - image_buffer: &[u8], - load_addr: GuestPhysAddr, - vm: VMRef, -) -> AxResult { - let mut buffer_pos = 0; - - let image_size = image_buffer.len(); - - debug!( - "loading VM image from memory {:?} {}", - load_addr, - image_buffer.len() - ); - - let image_load_regions = vm.get_image_load_region(load_addr, image_size)?; - - for region in image_load_regions { - let region_len = region.len(); - let bytes_to_write = region_len.min(image_size - buffer_pos); - - // copy data from memory - unsafe { - core::ptr::copy_nonoverlapping( - image_buffer[buffer_pos..].as_ptr(), - region.as_mut_ptr().cast(), - bytes_to_write, - ); - } - - crate::hal::arch::cache::dcache_range( - CacheOp::Clean, - (region.as_ptr() as usize).into(), - region_len, - ); - - // Update the position of the buffer. - buffer_pos += bytes_to_write; - - // If the buffer is fully written, exit the loop. - if buffer_pos >= image_size { - debug!("copy size: {bytes_to_write}"); - break; - } - } - - Ok(()) -} - -#[cfg(feature = "fs")] -pub mod fs { - use super::*; - use crate::hal::CacheOp; - use axerrno::{AxResult, ax_err, ax_err_type}; - use std::{fs::File, vec::Vec}; - - pub fn kernal_read(config: &AxVMCrateConfig, read_size: usize) -> AxResult> { - use std::fs::File; - use std::io::Read; - let file_name = &config.kernel.kernel_path; - - let mut file = File::open(file_name).map_err(|err| { - ax_err_type!( - NotFound, - format!( - "Failed to open {}, err {:?}, please check your disk.img", - file_name, err - ) - ) - })?; - - let mut buffer = vec![0u8; read_size]; - - file.read_exact(&mut buffer).map_err(|err| { - ax_err_type!( - NotFound, - format!( - "Failed to read {}, err {:?}, please check your disk.img", - file_name, err - ) - ) - })?; - - Ok(buffer) - } - - /// Loads the VM image files from the filesystem - /// into the guest VM's memory space based on the VM configuration. - pub(crate) fn load_vm_images_from_filesystem(loader: &ImageLoader) -> AxResult { - info!("Loading VM images from filesystem"); - // Load kernel image. - load_vm_image( - &loader.config.kernel.kernel_path, - loader.kernel_load_gpa, - loader.vm.clone(), - )?; - // Load BIOS image if needed. - if let Some(bios_path) = &loader.config.kernel.bios_path { - if let Some(bios_load_addr) = loader.bios_load_gpa { - load_vm_image(bios_path, bios_load_addr, loader.vm.clone())?; - } else { - return ax_err!(NotFound, "BIOS load addr is missed"); - } - }; - // Load Ramdisk image if needed. - if let Some(ramdisk_path) = &loader.config.kernel.ramdisk_path { - if let Some(ramdisk_load_addr) = loader.ramdisk_load_gpa { - load_vm_image(ramdisk_path, ramdisk_load_addr, loader.vm.clone())?; - } else { - return ax_err!(NotFound, "Ramdisk load addr is missed"); - } - }; - // Load DTB image if needed. - let vm_config = axvm::config::AxVMConfig::from(loader.config.clone()); - if let Some(dtb_arc) = get_vm_dtb_arc(&vm_config) { - let _dtb_slice: &[u8] = &dtb_arc; - #[cfg(target_arch = "aarch64")] - crate::vmm::fdt::update_fdt( - core::ptr::NonNull::new(_dtb_slice.as_ptr() as *mut u8).unwrap(), - _dtb_slice.len(), - loader.vm.clone(), - ); - } - - Ok(()) - } - - fn load_vm_image(image_path: &str, image_load_gpa: GuestPhysAddr, vm: VMRef) -> AxResult { - use std::io::{BufReader, Read}; - let (image_file, image_size) = open_image_file(image_path)?; - - let image_load_regions = vm.get_image_load_region(image_load_gpa, image_size)?; - let mut file = BufReader::new(image_file); - - for buffer in image_load_regions { - file.read_exact(buffer).map_err(|err| { - ax_err_type!( - Io, - format!("Failed in reading from file {}, err {:?}", image_path, err) - ) - })?; - - crate::hal::arch::cache::dcache_range( - CacheOp::Clean, - (buffer.as_ptr() as usize).into(), - buffer.len(), - ); - } - - Ok(()) - } - - pub fn open_image_file(file_name: &str) -> AxResult<(File, usize)> { - let file = File::open(file_name).map_err(|err| { - ax_err_type!( - NotFound, - format!( - "Failed to open {}, err {:?}, please check your disk.img", - file_name, err - ) - ) - })?; - let file_size = file - .metadata() - .map_err(|err| { - ax_err_type!( - Io, - format!( - "Failed to get metadate of file {}, err {:?}", - file_name, err - ) - ) - })? - .size() as usize; - Ok((file, file_size)) - } -} diff --git a/os/axvisor/src/vmm/ivc.rs b/os/axvisor/src/vmm/ivc.rs deleted file mode 100644 index 77aeefac3..000000000 --- a/os/axvisor/src/vmm/ivc.rs +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Inter-VM communication (IVC) module. -use alloc::collections::BTreeMap; -use alloc::vec::Vec; - -use std::os::arceos::modules::axhal::paging::PagingHandlerImpl; -use std::sync::Mutex; - -use axaddrspace::{GuestPhysAddr, HostPhysAddr}; -use axerrno::AxResult; -use page_table_multiarch::PagingHandler; - -/// A global btree map to store IVC channels, -/// indexed by (publisher_vm_id, channel_key). -static IVC_CHANNELS: Mutex>> = - Mutex::new(BTreeMap::new()); - -pub fn insert_channel( - publisher_vm_id: usize, - channel: IVCChannel, -) -> AxResult<()> { - let mut channels = IVC_CHANNELS.lock(); - if channels - .insert((publisher_vm_id, channel.key), channel) - .is_some() - { - Err(axerrno::ax_err_type!( - AlreadyExists, - "IVC channel already exists" - )) - } else { - Ok(()) - } -} - -/// Try to remove a channel according to the publisher VM ID and key. -/// If the channel still has subscribers, it will just mark it as unpublished -/// (by setting its base GPA to None). -/// If the channel is successfully unpublished, it will return the base GPA and size of the channel. -/// If the channel does not exist, it will return an error. -pub fn unpublish_channel( - publisher_vm_id: usize, - key: usize, -) -> AxResult> { - let mut channels = IVC_CHANNELS.lock(); - if let Some(mut channel) = channels.remove(&(publisher_vm_id, key)) { - let base_gpa = channel.base_gpa_in_publisher().ok_or_else(|| { - axerrno::ax_err_type!( - NotFound, - format!( - "IVC channel for publisher VM {} with key {} has no base GPA, it may have been marked as unpublished", - publisher_vm_id, key - ) - ) - })?; - let size = channel.size(); - if !channel.subscribers().is_empty() { - channel.base_gpa = None; // Mark the channel as removed. - // If there are still subscribers, just return None. - channels.insert((publisher_vm_id, key), channel); - } - Ok(Some((base_gpa, size))) - } else { - Err(axerrno::ax_err_type!( - NotFound, - format!( - "IVC channel for publisher VM {} with key {} not found", - publisher_vm_id, key - ) - )) - } -} - -pub fn get_channel_size(publisher_vm_id: usize, key: usize) -> AxResult { - let channels = IVC_CHANNELS.lock(); - if let Some(channel) = channels.get(&(publisher_vm_id, key)) { - Ok(channel.size()) - } else { - Err(axerrno::ax_err_type!( - NotFound, - format!( - "IVC channel for publisher VM {} with key {} not found", - publisher_vm_id, key - ) - )) - } -} - -/// Subcribe to a channel of a publisher VM with the given key, -/// return the shared region base address and size. -pub fn subscribe_to_channel_of_publisher( - publisher_vm_id: usize, - key: usize, - subscriber_vm_id: usize, - subscriber_gpa: GuestPhysAddr, -) -> AxResult<(HostPhysAddr, usize)> { - let mut channels = IVC_CHANNELS.lock(); - if let Some(channel) = channels.get_mut(&(publisher_vm_id, key)) { - // Add the subscriber VM ID to the channel. - channel.add_subscriber(subscriber_vm_id, subscriber_gpa); - Ok((channel.base_hpa(), channel.size())) - } else { - Err(axerrno::ax_err_type!( - NotFound, - format!( - "IVC channel for publisher VM [{}] key {:#x} not found", - publisher_vm_id, key - ) - )) - } -} - -/// Unsubscribe from a channel of a publisher VM with the given key, -/// if the channel has been unpublished (i.e., the base GPA is None) and has no subscribers, -/// it will remove the channel from the global map. -pub fn unsubscribe_from_channel_of_publisher( - publisher_vm_id: usize, - key: usize, - subscriber_vm_id: usize, -) -> AxResult<(GuestPhysAddr, usize)> { - let mut channels = IVC_CHANNELS.lock(); - let (base_gpa, size) = if let Some(channel) = channels.get_mut(&(publisher_vm_id, key)) { - // Remove the subscriber VM ID from the channel. - if let Some(subscriber_gpa) = channel.remove_subscriber(subscriber_vm_id) { - Ok((subscriber_gpa, channel.size())) - } else { - Err(axerrno::ax_err_type!( - NotFound, - format!( - "VM[{}] tries to unsubscribe non-existed channel publisher VM[{}] Key {:#x}", - subscriber_vm_id, publisher_vm_id, key - ) - )) - } - } else { - Err(axerrno::ax_err_type!( - NotFound, - format!("IVC channel for publisher VM {} not found", publisher_vm_id) - )) - }?; - - // If the channel has no subscribers and has been unpublished (base GPA is None), - // remove it from the global map. - if channels - .get(&(publisher_vm_id, key)) - .is_some_and(|c| c.subscribers().is_empty() && c.base_gpa.is_none()) - { - channels.remove(&(publisher_vm_id, key)); - } - - Ok((base_gpa, size)) -} - -pub struct IVCChannel { - publisher_vm_id: usize, - key: usize, - /// A list of subscriber VM IDs that are subscribed to this channel. - /// The key is the subscriber VM ID, and the value is the base address of the shared region in - /// guest physical address of the subscriber VM. - subscriber_vms: BTreeMap, - shared_region_base: HostPhysAddr, - shared_region_size: usize, - /// The base address of the shared memory region in guest physical address of the publisher VM. - /// `None` if the channel has been unpublished (but still has subscribers). - base_gpa: Option, - _phatom: core::marker::PhantomData, -} - -#[repr(C)] -pub struct IVCChannelHeader { - pub publisher_id: u64, - pub key: u64, -} - -impl IVCChannel { - #[allow(unused)] - pub fn header(&self) -> &IVCChannelHeader { - unsafe { - // Map the shared region base to the header structure. - &*H::phys_to_virt(self.shared_region_base).as_mut_ptr_of::() - } - } - - pub fn header_mut(&mut self) -> &mut IVCChannelHeader { - unsafe { - // Map the shared region base to the mutable header structure. - &mut *H::phys_to_virt(self.shared_region_base).as_mut_ptr_of::() - } - } - - #[allow(unused)] - pub fn data_region(&self) -> *const u8 { - unsafe { - // Return a pointer to the data region, which starts after the header. - H::phys_to_virt(self.shared_region_base) - .as_mut_ptr() - .add(core::mem::size_of::()) - } - } -} - -impl core::fmt::Debug for IVCChannel { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "IVCChannel(publisher[{}], subscribers {:?}, base: {:?}, size: {:#x}, gpa: {:?})", - self.publisher_vm_id, - self.subscriber_vms, - self.shared_region_base, - self.shared_region_size, - self.base_gpa - ) - } -} - -impl Drop for IVCChannel { - fn drop(&mut self) { - // Free the shared region frame when the channel is dropped. - debug!( - "Dropping IVCChannel for VM[{}], shared region base: {:?}", - self.publisher_vm_id, self.shared_region_base - ); - H::dealloc_frame(self.shared_region_base); - } -} - -impl IVCChannel { - pub fn alloc( - publisher_vm_id: usize, - key: usize, - shared_region_size: usize, - base_gpa: GuestPhysAddr, - ) -> AxResult { - // TODO: support larger shared region sizes with alloc_frames API. - let shared_region_size = shared_region_size.min(4096); - let shared_region_base = H::alloc_frame().ok_or_else(|| { - axerrno::ax_err_type!(NoMemory, "Failed to allocate shared region frame") - })?; - - let mut channel = IVCChannel { - publisher_vm_id, - key, - subscriber_vms: BTreeMap::new(), - shared_region_base, - shared_region_size, - base_gpa: Some(base_gpa), - _phatom: core::marker::PhantomData, - }; - - channel.header_mut().publisher_id = publisher_vm_id as u64; - channel.header_mut().key = key as u64; - - debug!("Allocated IVCChannel: {channel:?}"); - - Ok(channel) - } - - pub fn base_hpa(&self) -> HostPhysAddr { - self.shared_region_base - } - - pub fn base_gpa_in_publisher(&self) -> Option { - self.base_gpa - } - - pub fn size(&self) -> usize { - self.shared_region_size - } - - pub fn add_subscriber(&mut self, subscriber_vm_id: usize, subscriber_gpa: GuestPhysAddr) { - self.subscriber_vms - .entry(subscriber_vm_id) - .or_insert(subscriber_gpa); - } - - pub fn remove_subscriber(&mut self, subscriber_vm_id: usize) -> Option { - self.subscriber_vms.remove(&subscriber_vm_id) - } - - pub fn subscribers(&self) -> Vec<(usize, GuestPhysAddr)> { - self.subscriber_vms - .iter() - .map(|(vm_id, gpa)| (*vm_id, *gpa)) - .collect() - } -} diff --git a/os/axvisor/src/vmm/mod.rs b/os/axvisor/src/vmm/mod.rs deleted file mode 100644 index cbbfb8d41..000000000 --- a/os/axvisor/src/vmm/mod.rs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod hvc; -mod ivc; - -pub mod config; -pub mod images; -pub mod timer; -pub mod vcpus; -pub mod vm_list; - -#[cfg(target_arch = "aarch64")] -pub mod fdt; - -use core::sync::atomic::{AtomicUsize, Ordering}; -use std::os::arceos::{ - api::task::{self, AxWaitQueueHandle}, - modules::axtask, -}; - -use axerrno::{AxResult, ax_err_type}; - -use crate::{ - hal::{AxVCpuHalImpl, AxVMHalImpl}, - task::AsVCpuTask, -}; -pub use timer::init_percpu as init_timer_percpu; - -/// The instantiated VM type. -pub type VM = axvm::AxVM; -/// The instantiated VM ref type (by `Arc`). -pub type VMRef = axvm::AxVMRef; -/// The instantiated VCpu ref type (by `Arc`). -pub type VCpuRef = axvm::AxVCpuRef; - -static VMM: AxWaitQueueHandle = AxWaitQueueHandle::new(); - -/// The number of running VMs. This is used to determine when to exit the VMM. -static RUNNING_VM_COUNT: AtomicUsize = AtomicUsize::new(0); - -/// Initialize the VMM. -/// -/// This function creates the VM structures and sets up the primary VCpu for each VM. -pub fn init() { - info!("Initializing VMM..."); - // Initialize guest VM according to config file. - config::init_guest_vms(); - - // Setup vcpus, spawn axtask for primary VCpu. - info!("Setting up vcpus..."); - for vm in vm_list::get_vm_list() { - vcpus::setup_vm_primary_vcpu(vm); - } -} - -/// Start the VMM. -pub fn start() { - info!("VMM starting, booting VMs..."); - for vm in vm_list::get_vm_list() { - match vm.boot() { - Ok(_) => { - vcpus::notify_primary_vcpu(vm.id()); - RUNNING_VM_COUNT.fetch_add(1, Ordering::Release); - info!("VM[{}] boot success", vm.id()) - } - Err(err) => warn!("VM[{}] boot failed, error {:?}", vm.id(), err), - } - } - - // Do not exit until all VMs are stopped. - task::ax_wait_queue_wait_until( - &VMM, - || { - let vm_count = RUNNING_VM_COUNT.load(Ordering::Acquire); - info!("a VM exited, current running VM count: {vm_count}"); - vm_count == 0 - }, - None, - ); -} - -#[allow(unused_imports)] -pub use vcpus::with_vcpu_task; - -/// Run a closure with the specified VM. -pub fn with_vm(vm_id: usize, f: impl FnOnce(VMRef) -> T) -> Option { - let vm = vm_list::get_vm_by_id(vm_id)?; - Some(f(vm)) -} - -/// Run a closure with the specified VM and vCPU. -pub fn with_vm_and_vcpu( - vm_id: usize, - vcpu_id: usize, - f: impl FnOnce(VMRef, VCpuRef) -> T, -) -> Option { - let vm = vm_list::get_vm_by_id(vm_id)?; - let vcpu = vm.vcpu(vcpu_id)?; - - Some(f(vm, vcpu)) -} - -/// Run a closure with the specified VM and vCPU, with the guarantee that the closure will be -/// executed on the physical CPU where the vCPU is running, waiting, or queueing. -/// -/// TODO: It seems necessary to disable scheduling when running the closure. -pub fn with_vm_and_vcpu_on_pcpu( - vm_id: usize, - vcpu_id: usize, - f: impl FnOnce(VMRef, VCpuRef) + 'static, -) -> AxResult { - // Disables preemption and IRQs to prevent the current task from being preempted or re-scheduled. - let guard = kernel_guard::NoPreemptIrqSave::new(); - - let current_vm = axtask::current().as_vcpu_task().vm().id(); - let current_vcpu = axtask::current().as_vcpu_task().vcpu.id(); - - // The target vCPU is the current task, execute the closure directly. - if current_vm == vm_id && current_vcpu == vcpu_id { - with_vm_and_vcpu(vm_id, vcpu_id, f).unwrap(); // unwrap is safe here - return Ok(()); - } - - // The target vCPU is not the current task, send an IPI to the target physical CPU. - drop(guard); - - let _pcpu_id = vcpus::with_vcpu_task(vm_id, vcpu_id, |task| task.cpu_id()) - .ok_or_else(|| ax_err_type!(NotFound))?; - - unimplemented!(); - // use std::os::arceos::modules::axipi; - // Ok(axipi::send_ipi_event_to_one(pcpu_id as usize, move || { - // with_vm_and_vcpu_on_pcpu(vm_id, vcpu_id, f); - // })) -} - -pub fn add_running_vm_count(count: usize) { - RUNNING_VM_COUNT.fetch_add(count, Ordering::Release); -} - -pub fn sub_running_vm_count(count: usize) { - RUNNING_VM_COUNT.fetch_sub(count, Ordering::Release); -} diff --git a/os/axvisor/src/vmm/timer.rs b/os/axvisor/src/vmm/timer.rs deleted file mode 100644 index e2e7242b1..000000000 --- a/os/axvisor/src/vmm/timer.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use core::sync::atomic::AtomicUsize; -use core::sync::atomic::Ordering; - -use std::os::arceos::modules::axhal; - -use alloc::boxed::Box; -use kspin::SpinNoIrq; -use lazyinit::LazyInit; -use timer_list::{TimeValue, TimerEvent, TimerList}; - -static TOKEN: AtomicUsize = AtomicUsize::new(0); -// const PERIODIC_INTERVAL_NANOS: u64 = axhal::time::NANOS_PER_SEC / axconfig::TICKS_PER_SEC as u64; - -/// Represents a timer event in the virtual machine monitor (VMM). -/// -/// This struct holds a unique token for the timer and a callback function -/// that will be executed when the timer expires. -pub struct VmmTimerEvent { - // Unique identifier for the timer event - token: usize, - // Callback function to be executed when the timer expires - timer_callback: Box, -} - -impl VmmTimerEvent { - fn new(token: usize, f: F) -> Self - where - F: FnOnce(TimeValue) + Send + 'static, - { - Self { - token, - timer_callback: Box::new(f), - } - } -} - -impl TimerEvent for VmmTimerEvent { - fn callback(self, now: TimeValue) { - (self.timer_callback)(now) - } -} - -#[percpu::def_percpu] -static TIMER_LIST: LazyInit>> = LazyInit::new(); - -/// Registers a new timer that will execute at the specified deadline -/// -/// # Arguments -/// - `deadline`: The absolute time in nanoseconds when the timer should trigger -/// - `handler`: The callback function to execute when the timer expires -/// -/// # Returns -/// A unique token that can be used to cancel this timer later -pub fn register_timer(deadline: u64, handler: F) -> usize -where - F: FnOnce(TimeValue) + Send + 'static, -{ - trace!("Registering timer..."); - trace!( - "deadline is {:#?} = {:#?}", - deadline, - TimeValue::from_nanos(deadline) - ); - let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; - let mut timers = timer_list.lock(); - let token = TOKEN.fetch_add(1, Ordering::Release); - let event = VmmTimerEvent::new(token, handler); - timers.set(TimeValue::from_nanos(deadline), event); - token -} - -/// Cancels a timer with the specified token. -/// -/// # Parameters -/// - `token`: The unique token of the timer to cancel. -pub fn cancel_timer(token: usize) { - let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; - let mut timers = timer_list.lock(); - timers.cancel(|event| event.token == token); -} - -/// Check and process any pending timer events -pub fn check_events() { - // info!("Checking timer events..."); - // info!("now is {:#?}", axhal::time::wall_time()); - let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; - loop { - let now = axhal::time::wall_time(); - let event = timer_list.lock().expire_one(now); - if let Some((_deadline, event)) = event { - trace!("pick one {_deadline:#?} to handle!!!"); - event.callback(now); - } else { - break; - } - } -} - -// /// Schedule the next timer event based on the periodic interval -// pub fn scheduler_next_event() { -// trace!("Scheduling next event..."); -// let now_ns = axhal::time::monotonic_time_nanos(); -// let deadline = now_ns + PERIODIC_INTERVAL_NANOS; -// debug!("PHY deadline {} !!!", deadline); -// axhal::time::set_oneshot_timer(deadline); -// } - -/// Initialize the hypervisor timer system -pub fn init_percpu() { - info!("Initing HV Timer..."); - let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; - timer_list.init_once(SpinNoIrq::new(TimerList::new())); -} diff --git a/os/axvisor/src/vmm/vcpus.rs b/os/axvisor/src/vmm/vcpus.rs deleted file mode 100644 index b80bcc798..000000000 --- a/os/axvisor/src/vmm/vcpus.rs +++ /dev/null @@ -1,599 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::{collections::BTreeMap, vec::Vec}; -use cpumask::CpuMask; - -use core::{ - cell::UnsafeCell, - sync::atomic::{AtomicUsize, Ordering}, - time::Duration, -}; -use std::os::arceos::{ - api::task::{AxCpuMask, ax_wait_queue_wake}, - modules::{ - axhal::{self, time::busy_wait}, - axtask::{self, AxTaskExt}, - }, -}; - -use axaddrspace::GuestPhysAddr; -use axtask::{AxTaskRef, TaskInner, WaitQueue}; -use axvcpu::{AxVCpuExitReason, VCpuState}; - -use crate::{hal::arch::inject_interrupt, task::VCpuTask}; -use crate::{ - task::AsVCpuTask, - vmm::{VCpuRef, VMRef, sub_running_vm_count}, -}; - -const KERNEL_STACK_SIZE: usize = 0x40000; // 256 KiB - -/// A global static BTreeMap that holds the wait queues for VCpus -/// associated with their respective VMs, identified by their VM IDs. -/// -/// TODO: find a better data structure to replace the `static mut`, something like a conditional -/// variable. -static VM_VCPU_TASK_WAIT_QUEUE: Queue = Queue::new(); - -/// A thread-safe queue that manages wait queues for VCpus across multiple VMs. -/// -/// This structure wraps a BTreeMap that maps VM IDs to their corresponding VMVCpus structures. -/// It provides thread-safe access to the mapping through interior mutability using UnsafeCell. -/// Each VM is identified by its unique ID, and the queue manages the VCpu tasks and wait -/// operations for all VMs in the system. -struct Queue(UnsafeCell>); - -unsafe impl Sync for Queue {} -unsafe impl Send for Queue {} - -impl Queue { - /// Creates a new empty Queue. - /// - /// # Returns - /// - /// A new Queue instance with an empty BTreeMap. - const fn new() -> Self { - Self(UnsafeCell::new(BTreeMap::new())) - } - - /// Retrieves a reference to the VMVCpus for the specified VM ID. - fn get(&self, vm_id: &usize) -> Option<&VMVCpus> { - unsafe { (*self.0.get()).get(vm_id) } - } - - /// Retrieves a mutable reference to the VMVCpus for the specified VM ID. - #[allow(clippy::mut_from_ref)] - fn get_mut(&self, vm_id: &usize) -> Option<&mut VMVCpus> { - unsafe { (*self.0.get()).get_mut(vm_id) } - } - - /// Inserts a new VMVCpus entry for the specified VM ID. - fn insert(&self, vm_id: usize, vcpus: VMVCpus) { - unsafe { - (*self.0.get()).insert(vm_id, vcpus); - } - } - - /// Removes the VMVCpus entry for the specified VM ID. - fn remove(&self, vm_id: &usize) -> Option { - unsafe { (*self.0.get()).remove(vm_id) } - } -} - -/// A structure representing the VCpus of a specific VM, including a wait queue -/// and a list of tasks associated with the VCpus. -pub struct VMVCpus { - // The ID of the VM to which these VCpus belong. - _vm_id: usize, - // A wait queue to manage task scheduling for the VCpus. - wait_queue: WaitQueue, - // A list of tasks associated with the VCpus of this VM. - vcpu_task_list: Vec, - /// The number of currently running or halting VCpus. Used to track when the VM is fully - /// shutdown. - /// - /// This number is incremented when a VCpu starts running and decremented when it exits because - /// of the VM being shutdown. - running_halting_vcpu_count: AtomicUsize, -} - -impl VMVCpus { - /// Creates a new `VMVCpus` instance for the given VM. - /// - /// # Arguments - /// - /// * `vm` - A reference to the VM for which the VCpus are being created. - /// - /// # Returns - /// - /// A new `VMVCpus` instance with an empty task list and a fresh wait queue. - fn new(vm: VMRef) -> Self { - Self { - _vm_id: vm.id(), - wait_queue: WaitQueue::new(), - vcpu_task_list: Vec::with_capacity(vm.vcpu_num()), - running_halting_vcpu_count: AtomicUsize::new(0), - } - } - - /// Adds a VCpu task to the list of VCpu tasks for this VM. - /// - /// # Arguments - /// - /// * `vcpu_task` - A reference to the task associated with a VCpu that is to be added. - fn add_vcpu_task(&mut self, vcpu_task: AxTaskRef) { - // It may be dangerous to go lock-free here, as two VCpus may invoke `CpuUp` at the same - // time. However, in most scenarios, only the bsp will `CpuUp` other VCpus, making this - // operation single-threaded. Therefore, we just tolerate this as for now. - self.vcpu_task_list.push(vcpu_task); - } - - /// Blocks the current thread on the wait queue associated with the VCpus of this VM. - fn wait(&self) { - self.wait_queue.wait() - } - - /// Blocks the current thread on the wait queue associated with the VCpus of this VM - /// until the provided condition is met. - fn wait_until(&self, condition: F) - where - F: Fn() -> bool, - { - self.wait_queue.wait_until(condition) - } - - #[allow(dead_code)] - fn notify_one(&mut self) { - // FIXME: `WaitQueue::len` is removed - // info!("Current wait queue length: {}", self.wait_queue.len()); - self.wait_queue.notify_one(false); - } - - /// Notify all waiting vCPU threads to wake up. - /// This is useful when shutting down a VM to ensure all vCPUs can check the shutdown flag. - fn notify_all(&mut self) { - self.wait_queue.notify_all(false); - } - - /// Increments the count of running or halting VCpus by one. - fn mark_vcpu_running(&self) { - self.running_halting_vcpu_count - .fetch_add(1, Ordering::Relaxed); - // Relaxed is enough here, as we only need to ensure that the count is incremented and - // decremented correctly, and there is no other data synchronization needed. - } - - /// Decrements the count of running or halting VCpus by one. Returns true if this was the last - /// VCpu to exit. - fn mark_vcpu_exiting(&self) -> bool { - self.running_halting_vcpu_count - .fetch_sub(1, Ordering::Relaxed) - == 1 - // Relaxed is enough here, as we only need to ensure that the count is incremented and - // decremented correctly, and there is no other data synchronization needed. - } -} - -/// Blocks the current thread until it is explicitly woken up, using the wait queue -/// associated with the VCpus of the specified VM. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM whose VCpu wait queue is used to block the current thread. -/// -fn wait(vm_id: usize) { - VM_VCPU_TASK_WAIT_QUEUE.get(&vm_id).unwrap().wait() -} - -/// Blocks the current thread until the provided condition is met, using the wait queue -/// associated with the VCpus of the specified VM. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM whose VCpu wait queue is used to block the current thread. -/// * `condition` - A closure that returns a boolean value indicating whether the condition is met. -/// -fn wait_for(vm_id: usize, condition: F) -where - F: Fn() -> bool, -{ - VM_VCPU_TASK_WAIT_QUEUE - .get(&vm_id) - .unwrap() - .wait_until(condition) -} - -/// Notifies the primary VCpu task associated with the specified VM to wake up and resume execution. -/// This function is used to notify the primary VCpu of a VM to start running after the VM has been booted. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM whose VCpus are to be notified. -/// -pub(crate) fn notify_primary_vcpu(vm_id: usize) { - // Generally, the primary VCpu is the first and **only** VCpu in the list. - VM_VCPU_TASK_WAIT_QUEUE - .get_mut(&vm_id) - .unwrap() - .notify_one() -} - -/// Notifies all VCpu tasks associated with the specified VM to wake up. -/// This is useful when shutting down a VM to ensure all waiting vCPUs can check the shutdown flag. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM whose VCpus should be notified. -/// -pub(crate) fn notify_all_vcpus(vm_id: usize) { - if let Some(vm_vcpus) = VM_VCPU_TASK_WAIT_QUEUE.get_mut(&vm_id) { - vm_vcpus.notify_all(); - } -} - -/// Cleans up VCpu resources for a VM that is being deleted. -/// This removes the VM's entry from the global VCpu wait queue. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM whose VCpu resources should be cleaned up. -/// -/// # Note -/// -/// This should be called after all VCpu threads have exited to avoid resource leaks. -/// It will join all VCpu tasks to ensure they are fully cleaned up. -pub(crate) fn cleanup_vm_vcpus(vm_id: usize) { - if let Some(vm_vcpus) = VM_VCPU_TASK_WAIT_QUEUE.remove(&vm_id) { - let task_count = vm_vcpus.vcpu_task_list.len(); - - info!("VM[{}] Joining {} VCpu tasks...", vm_id, task_count); - - // Join all VCpu tasks to ensure they have fully exited and cleaned up - for (idx, task) in vm_vcpus.vcpu_task_list.iter().enumerate() { - debug!( - "VM[{}] Joining VCpu task[{}]: {}", - vm_id, - idx, - task.id_name() - ); - let exit_code = task.join(); - debug!( - "VM[{}] VCpu task[{}] exited with code: {}", - vm_id, idx, exit_code - ); - } - - info!( - "VM[{}] VCpu resources cleaned up, {} VCpu tasks joined successfully", - vm_id, task_count - ); - } else { - warn!("VM[{}] VCpu resources not found in queue", vm_id); - } -} - -/// Marks the VCpu of the specified VM as running. -fn mark_vcpu_running(vm_id: usize) { - VM_VCPU_TASK_WAIT_QUEUE - .get(&vm_id) - .unwrap() - .mark_vcpu_running(); -} - -/// Marks the VCpu of the specified VM as exiting for VM shutdown. Returns true if this was the last -/// VCpu to exit. -fn mark_vcpu_exiting(vm_id: usize) -> bool { - VM_VCPU_TASK_WAIT_QUEUE - .get(&vm_id) - .unwrap() - .mark_vcpu_exiting() -} - -/// Boot target VCpu on the specified VM. -/// This function is used to boot a secondary VCpu on a VM, setting the entry point and argument for the VCpu. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM on which the VCpu is to be booted. -/// * `vcpu_id` - The ID of the VCpu to be booted. -/// * `entry_point` - The entry point of the VCpu. -/// * `arg` - The argument to be passed to the VCpu. -/// -fn vcpu_on(vm: VMRef, vcpu_id: usize, entry_point: GuestPhysAddr, arg: usize) { - let vcpu = vm.vcpu_list()[vcpu_id].clone(); - assert_eq!( - vcpu.state(), - VCpuState::Free, - "vcpu_on: {} invalid vcpu state {:?}", - vcpu.id(), - vcpu.state() - ); - - vcpu.set_entry(entry_point) - .expect("vcpu_on: set_entry failed"); - vcpu.set_gpr(0, arg); - - #[cfg(target_arch = "riscv64")] - { - debug!( - "vcpu_on: vcpu[{}] entry={:x} opaque={:x}", - vcpu_id, entry_point, arg - ); - vcpu.set_gpr(0, vcpu_id); - vcpu.set_gpr(1, arg); - } - - let vcpu_task = alloc_vcpu_task(&vm, vcpu); - - VM_VCPU_TASK_WAIT_QUEUE - .get_mut(&vm.id()) - .unwrap() - .add_vcpu_task(vcpu_task); -} - -/// Sets up the primary VCpu for the given VM, -/// generally the first VCpu in the VCpu list, -/// and initializing their respective wait queues and task lists. -/// VM's secondary VCpus are not started at this point. -/// -/// # Arguments -/// -/// * `vm` - A reference to the VM for which the VCpus are being set up. -pub fn setup_vm_primary_vcpu(vm: VMRef) { - info!("Initializing VM[{}]'s {} vcpus", vm.id(), vm.vcpu_num()); - let vm_id = vm.id(); - let mut vm_vcpus = VMVCpus::new(vm.clone()); - - let primary_vcpu_id = 0; - - let primary_vcpu = vm.vcpu_list()[primary_vcpu_id].clone(); - let primary_vcpu_task = alloc_vcpu_task(&vm, primary_vcpu); - vm_vcpus.add_vcpu_task(primary_vcpu_task); - - VM_VCPU_TASK_WAIT_QUEUE.insert(vm_id, vm_vcpus); -} - -/// Finds the [`AxTaskRef`] associated with the specified vCPU of the specified VM. -// pub fn find_vcpu_task(vm_id: usize, vcpu_id: usize) -> Option { -// with_vcpu_task(vm_id, vcpu_id, |task| task.clone()) -// } -/// Executes the provided closure with the [`AxTaskRef`] associated with the specified vCPU of the specified VM. -pub fn with_vcpu_task T>( - vm_id: usize, - vcpu_id: usize, - f: F, -) -> Option { - VM_VCPU_TASK_WAIT_QUEUE - .get(&vm_id) - .unwrap() - .vcpu_task_list - .get(vcpu_id) - .map(f) -} - -/// Allocates arceos task for vcpu, set the task's entry function to [`vcpu_run()`], -/// also initializes the CPU mask if the VCpu has a dedicated physical CPU set. -/// -/// # Arguments -/// -/// * `vm` - A reference to the VM for which the VCpu task is being allocated. -/// * `vcpu` - A reference to the VCpu for which the task is being allocated. -/// -/// # Returns -/// -/// A reference to the task that has been allocated for the VCpu. -/// -/// # Note -/// -/// * The task associated with the VCpu is created with a kernel stack size of 256 KiB. -/// * The task is created in blocked state and added to the wait queue directly, -/// instead of being added to the ready queue. It will be woken up by notify_primary_vcpu(). -fn alloc_vcpu_task(vm: &VMRef, vcpu: VCpuRef) -> AxTaskRef { - info!("Spawning task for VM[{}] VCpu[{}]", vm.id(), vcpu.id()); - let mut vcpu_task = TaskInner::new( - vcpu_run, - format!("VM[{}]-VCpu[{}]", vm.id(), vcpu.id()), - KERNEL_STACK_SIZE, - ); - - if let Some(phys_cpu_set) = vcpu.phys_cpu_set() { - vcpu_task.set_cpumask(AxCpuMask::from_raw_bits(phys_cpu_set)); - } - - // Use Weak reference in TaskExt to avoid keeping VM alive - let inner = VCpuTask::new(vm, vcpu); - *vcpu_task.task_ext_mut() = Some(AxTaskExt::from_impl(inner)); - - info!( - "VCpu task {} created {:?}", - vcpu_task.id_name(), - vcpu_task.cpumask() - ); - axtask::spawn_task(vcpu_task) -} - -/// The main routine for VCpu task. -/// This function is the entry point for the VCpu tasks, which are spawned for each VCpu of a VM. -/// -/// When the VCpu first starts running, it waits for the VM to be in the running state. -/// It then enters a loop where it runs the VCpu and handles the various exit reasons. -fn vcpu_run() { - let curr = axtask::current(); - - let vm = curr.as_vcpu_task().vm(); - let vcpu = curr.as_vcpu_task().vcpu.clone(); - let vm_id = vm.id(); - let vcpu_id = vcpu.id(); - - // boot delay - let boot_delay_sec = (vm_id - 1) * 5; - info!("VM[{vm_id}] boot delay: {boot_delay_sec}s"); - busy_wait(Duration::from_secs(boot_delay_sec as _)); - - info!("VM[{}] VCpu[{}] waiting for running", vm.id(), vcpu.id()); - wait_for(vm_id, || vm.running()); - - info!("VM[{}] VCpu[{}] running...", vm.id(), vcpu.id()); - mark_vcpu_running(vm_id); - - loop { - match vm.run_vcpu(vcpu_id) { - Ok(exit_reason) => match exit_reason { - AxVCpuExitReason::Hypercall { nr, args } => { - debug!("Hypercall [{nr}] args {args:x?}"); - use crate::vmm::hvc::HyperCall; - - match HyperCall::new(vcpu.clone(), vm.clone(), nr, args) { - Ok(hypercall) => { - let ret_val = match hypercall.execute() { - Ok(ret_val) => ret_val as isize, - Err(err) => { - warn!("Hypercall [{nr:#x}] failed: {err:?}"); - -1 - } - }; - vcpu.set_return_value(ret_val as usize); - } - Err(err) => { - warn!("Hypercall [{nr:#x}] failed: {err:?}"); - } - } - } - AxVCpuExitReason::FailEntry { - hardware_entry_failure_reason, - } => { - warn!( - "VM[{vm_id}] VCpu[{vcpu_id}] run failed with exit code {hardware_entry_failure_reason}" - ); - } - AxVCpuExitReason::ExternalInterrupt { vector } => { - debug!("VM[{vm_id}] run VCpu[{vcpu_id}] get irq {vector}"); - - // TODO: maybe move this irq dispatcher to lower layer to accelerate the interrupt handling - axhal::irq::irq_handler(vector as usize); - super::timer::check_events(); - } - AxVCpuExitReason::Halt => { - debug!("VM[{vm_id}] run VCpu[{vcpu_id}] Halt"); - wait(vm_id) - } - AxVCpuExitReason::Nothing => {} - AxVCpuExitReason::CpuDown { _state } => { - warn!("VM[{vm_id}] run VCpu[{vcpu_id}] CpuDown state {_state:#x}"); - wait(vm_id) - } - AxVCpuExitReason::CpuUp { - target_cpu, - entry_point, - arg, - } => { - info!( - "VM[{vm_id}]'s VCpu[{vcpu_id}] try to boot target_cpu [{target_cpu}] entry_point={entry_point:x} arg={arg:#x}" - ); - - // Get the mapping relationship between all vCPUs and physical CPUs from the configuration - let vcpu_mappings = vm.get_vcpu_affinities_pcpu_ids(); - - // Find the vCPU ID corresponding to the physical ID - let target_vcpu_id = vcpu_mappings - .iter() - .find_map(|(vcpu_id, _, phys_id)| { - if *phys_id == target_cpu as usize { - Some(*vcpu_id) - } else { - None - } - }) - .unwrap_or_else(|| { - panic!("Physical CPU ID {target_cpu} not found in VM configuration",) - }); - - vcpu_on(vm.clone(), target_vcpu_id, entry_point, arg as _); - vcpu.set_gpr(0, 0); - } - AxVCpuExitReason::SystemDown => { - warn!("VM[{vm_id}] run VCpu[{vcpu_id}] SystemDown"); - vm.shutdown().expect("VM shutdown failed"); - } - AxVCpuExitReason::SendIPI { - target_cpu, - target_cpu_aux, - send_to_all, - send_to_self, - vector, - } => { - debug!( - "VM[{vm_id}] run VCpu[{vcpu_id}] SendIPI, target_cpu={target_cpu:#x}, target_cpu_aux={target_cpu_aux:#x}, vector={vector}", - ); - if send_to_all { - unimplemented!("Send IPI to all CPUs is not implemented yet"); - } - - if target_cpu == vcpu_id as u64 || send_to_self { - inject_interrupt(vector as _); - } else { - vm.inject_interrupt_to_vcpu( - CpuMask::one_shot(target_cpu as _), - vector as _, - ) - .unwrap(); - } - } - e => { - warn!("VM[{vm_id}] run VCpu[{vcpu_id}] unhandled vmexit: {e:?}"); - } - }, - Err(err) => { - error!("VM[{vm_id}] run VCpu[{vcpu_id}] get error {err:?}"); - // wait(vm_id) - vm.shutdown().expect("VM shutdown failed"); - } - } - - // Check if the VM is suspended - if vm.suspending() { - debug!( - "VM[{}] VCpu[{}] is suspended, waiting for resume...", - vm_id, vcpu_id - ); - wait_for(vm_id, || !vm.suspending()); - info!("VM[{}] VCpu[{}] resumed from suspend", vm_id, vcpu_id); - continue; - } - - // Check if the VM is stopping. - if vm.stopping() { - warn!( - "VM[{}] VCpu[{}] stopping because of VM stopping", - vm_id, vcpu_id - ); - - if mark_vcpu_exiting(vm_id) { - info!("VM[{vm_id}] VCpu[{vcpu_id}] last VCpu exiting, decreasing running VM count"); - - // Transition from Stopping to Stopped - vm.set_vm_status(axvm::VMStatus::Stopped); - info!("VM[{}] state changed to Stopped", vm_id); - - sub_running_vm_count(1); - ax_wait_queue_wake(&super::VMM, 1); - } - - break; - } - } - - info!("VM[{}] VCpu[{}] exiting...", vm_id, vcpu_id); -} diff --git a/os/axvisor/src/vmm/vm_list.rs b/os/axvisor/src/vmm/vm_list.rs deleted file mode 100644 index 136a49949..000000000 --- a/os/axvisor/src/vmm/vm_list.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::collections::BTreeMap; -use alloc::vec::Vec; - -use spin::Mutex; - -use crate::vmm::VMRef; - -/// Represents a list of VMs, -/// stored in a BTreeMap where the key is the VM ID and the value is a reference to the VM. -struct VMList { - vm_list: BTreeMap, -} - -impl VMList { - /// Creates a new, empty `VMList`. - const fn new() -> VMList { - VMList { - vm_list: BTreeMap::new(), - } - } - - /// Adds a new VM to the list. - /// - /// If a VM with the given ID already exists, a warning is logged, and the VM is not added. - /// - /// # Arguments - /// - /// * `vm_id` - The unique identifier for the VM. - /// * `vm` - A reference to the VM that will be added. - fn push_vm(&mut self, vm_id: usize, vm: VMRef) { - if self.vm_list.contains_key(&vm_id) { - warn!("VM[{vm_id}] already exists, push VM failed, just return ..."); - return; - } - self.vm_list.insert(vm_id, vm); - } - - /// Removes a VM from the list by its ID. - /// - /// # Arguments - /// - /// * `vm_id` - The unique identifier of the VM to be removed. - /// - /// # Returns - /// - /// Returns `Some(VMRef)` if the VM was successfully removed, or `None` if the VM with the given ID did not exist. - #[allow(unused)] - fn remove_vm(&mut self, vm_id: usize) -> Option { - self.vm_list.remove(&vm_id) - } - - /// Retrieves a VM from the list by its ID. - /// - /// # Arguments - /// - /// * `vm_id` - The unique identifier of the VM to be retrieved. - /// - /// # Returns - /// - /// Returns `Some(VMRef)` if the VM exists, or `None` if the VM with the given ID does not exist. - fn get_vm_by_id(&self, vm_id: usize) -> Option { - self.vm_list.get(&vm_id).cloned() - } -} - -// A global list of VMs, protected by a mutex for thread-safe access. -static GLOBAL_VM_LIST: Mutex = Mutex::new(VMList::new()); - -/// Adds a VM to the global VM list. -/// -/// # Arguments -/// -/// * `vm` - A reference to the VM instance. -pub fn push_vm(vm: VMRef) { - GLOBAL_VM_LIST.lock().push_vm(vm.id(), vm) -} - -/// Removes a VM from the global VM list by its ID. -/// -/// # Arguments -/// -/// * `vm_id` - The unique identifier of the VM to be removed. -/// -/// # Returns -/// -/// * `Option` - The removed VM reference if it exists, or `None` if not. -#[allow(unused)] -pub fn remove_vm(vm_id: usize) -> Option { - GLOBAL_VM_LIST.lock().remove_vm(vm_id) -} - -/// Retrieves a VM from the global VM list by its ID. -/// -/// # Arguments -/// -/// * `vm_id` - The unique identifier of the VM to retrieve. -/// -/// # Returns -/// -/// * `Option` - The VM reference if it exists, or `None` if not. -#[allow(unused)] -pub fn get_vm_by_id(vm_id: usize) -> Option { - GLOBAL_VM_LIST.lock().get_vm_by_id(vm_id) -} - -pub fn get_vm_list() -> Vec { - let global_vm_list = GLOBAL_VM_LIST.lock().vm_list.clone(); - let mut vm_list = Vec::with_capacity(global_vm_list.len()); - for (_id, vm) in global_vm_list { - vm_list.push(vm.clone()); - } - vm_list -} diff --git a/os/axvisor/xtask/src/cargo.rs b/os/axvisor/xtask/src/cargo.rs deleted file mode 100644 index 432f8837d..000000000 --- a/os/axvisor/xtask/src/cargo.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ostool::build::CargoRunnerKind; -use std::{fs, path::PathBuf}; - -use crate::ctx::Context; - -impl Context { - pub async fn run_qemu(&mut self, config_path: Option) -> anyhow::Result<()> { - let build_config = self.load_config()?; - - let arch = if build_config.target.contains("aarch64") { - Arch::Aarch64 - } else if build_config.target.contains("x86_64") { - Arch::X86_64 - } else { - return Err(anyhow::anyhow!( - "Unsupported target architecture: {}", - build_config.target - )); - }; - - let config_path = if let Some(path) = config_path { - path - } else { - PathBuf::from(format!(".qemu-{arch:?}.toml").to_lowercase()) - }; - let default_config_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("scripts") - .join("ostool") - .join(format!("qemu-{arch:?}.toml").to_lowercase()); - - // If the configuration file does not exist, copy from the default location - if !config_path.exists() { - fs::copy(&default_config_path, &config_path)?; - } - - let kind = CargoRunnerKind::Qemu { - qemu_config: Some(config_path), - debug: false, - dtb_dump: false, - }; - - self.ctx.cargo_run(&build_config, &kind).await?; - - Ok(()) - } - - pub async fn run_uboot(&mut self, config_path: Option) -> anyhow::Result<()> { - let build_config = self.load_config()?; - - let config_path = config_path.unwrap_or_else(|| PathBuf::from(".uboot.toml")); - - let kind = CargoRunnerKind::Uboot { - uboot_config: Some(config_path), - }; - - self.ctx.cargo_run(&build_config, &kind).await?; - - Ok(()) - } -} - -#[derive(Debug, Clone, Copy)] -enum Arch { - Aarch64, - X86_64, -} diff --git a/os/axvisor/xtask/src/clippy.rs b/os/axvisor/xtask/src/clippy.rs deleted file mode 100644 index 5ba071ac1..000000000 --- a/os/axvisor/xtask/src/clippy.rs +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use anyhow::{Context, Result, anyhow}; -use cargo_metadata::{MetadataCommand, Package}; -use colored::*; -use std::collections::HashSet; -use std::process::Command; -use std::time::Instant; - -// Import ClippyArgs from main.rs -use super::ClippyArgs; - -/// Static target array configuration -const TARGETS: &[&str] = &["x86_64-unknown-none", "aarch64-unknown-none-softfloat"]; -const PACKAGE: &str = "axvisor"; - -/// Clippy check result statistics -#[derive(Debug, Default)] -struct ClippyStats { - total_checks: usize, - passed_checks: usize, - failed_checks: usize, - skipped_checks: usize, -} - -impl ClippyStats { - fn record_passed(&mut self) { - self.total_checks += 1; - self.passed_checks += 1; - } - - fn record_failed(&mut self) { - self.total_checks += 1; - self.failed_checks += 1; - } - - fn record_skipped(&mut self) { - self.total_checks += 1; - self.skipped_checks += 1; - } - - fn print_summary(&self, duration: std::time::Duration) { - println!("\n{}", "=== Clippy Check Summary ===".bold().cyan()); - println!("Total checks: {}", self.total_checks); - println!("Passed checks: {}", self.passed_checks.to_string().green()); - if self.failed_checks > 0 { - println!( - "Failed checks: {}", - self.failed_checks.to_string().red().bold() - ); - } else { - println!("Failed checks: {}", self.failed_checks); - } - println!( - "Skipped checks: {}", - self.skipped_checks.to_string().yellow() - ); - println!("Execution time: {:.2}s", duration.as_secs_f64()); - - if self.failed_checks > 0 { - println!( - "\n{}", - "❌ Clippy errors found, please check the output above" - .bold() - .red() - ); - } else { - println!("\n{}", "✅ All clippy checks passed!".bold().green()); - } - } -} - -/// Main clippy runner function -pub fn run_clippy(args: ClippyArgs) -> Result<()> { - let start_time = Instant::now(); - println!( - "{}", - "🔍 Starting comprehensive Clippy checks...".bold().cyan() - ); - - // Parse package filter, default to axvisor package only - let package_filter = args - .packages - .as_ref() - .map(|s| { - s.split(',') - .map(|s| s.trim().to_string()) - .collect::>() - }) - .or_else(|| Some(HashSet::from([PACKAGE.to_string()]))); - - // Parse target filter - let target_filter = args.targets.as_ref().map(|s| { - s.split(',') - .map(|s| s.trim().to_string()) - .collect::>() - }); - - // Get workspace metadata - let metadata = MetadataCommand::new() - .no_deps() - .exec() - .context("Failed to get workspace metadata")?; - - let workspace_members: Vec<&Package> = metadata - .workspace_members - .iter() - .filter_map(|id| metadata.packages.iter().find(|p| p.id == *id)) - .filter(|pkg| { - // Filter out xtask itself - pkg.name != "xtask" - }) - .filter(|pkg| { - // Apply package filter - if let Some(ref filter) = package_filter { - filter.contains(&pkg.name.to_string()) - } else { - true - } - }) - .collect(); - - if workspace_members.is_empty() { - return Err(anyhow!("No matching workspace packages found")); - } - - println!("Found {} workspace packages:", workspace_members.len()); - for pkg in &workspace_members { - println!(" - {} ({})", pkg.name.bold(), pkg.version); - } - - let mut stats = ClippyStats::default(); - let mut has_errors = false; - - // Iterate through all targets - for target in TARGETS { - // Apply target filter - if let Some(ref filter) = target_filter - && !filter.contains(&target.to_string()) - { - continue; - } - - println!( - "\n{}", - format!("🎯 Checking target: {target}").bold().yellow() - ); - - // Iterate through all workspace packages - for package in &workspace_members { - println!("\n📦 Checking package: {}", package.name.bold()); - - // Get all features of the package - let features = get_package_features(package); - - if features.is_empty() { - // If no features, perform basic check - println!(" 🔧 No additional features, performing basic check..."); - - if args.dry_run { - let fix_str = if args.fix { " --fix" } else { "" }; - let allow_dirty_str = if args.fix && args.allow_dirty { - " --allow-dirty" - } else { - "" - }; - println!( - " [DRY RUN] cargo clippy --target {} -p {}{}{}", - target, package.name, fix_str, allow_dirty_str - ); - stats.record_skipped(); - } else { - match run_single_clippy( - target, - package.name.as_ref(), - &[], - args.continue_on_error, - args.fix, - args.allow_dirty, - ) { - Ok(_) => { - stats.record_passed(); - } - Err(e) => { - stats.record_failed(); - eprintln!(" {}", format!("❌ Error: {e}").red()); - if !args.continue_on_error { - return Err(e); - } - has_errors = true; - } - } - } - } else { - // Iterate through each feature for individual checks - for feature in &features { - println!(" 🔧 Checking feature: {}", feature.cyan()); - - if args.dry_run { - let fix_str = if args.fix { " --fix" } else { "" }; - let allow_dirty_str = if args.fix && args.allow_dirty { - " --allow-dirty" - } else { - "" - }; - println!( - " [DRY RUN] cargo clippy --target {} -p {} --features {}{}{}", - target, package.name, feature, fix_str, allow_dirty_str - ); - stats.record_skipped(); - } else { - match run_single_clippy( - target, - package.name.as_ref(), - std::slice::from_ref(feature), - args.continue_on_error, - args.fix, - args.allow_dirty, - ) { - Ok(_) => { - stats.record_passed(); - } - Err(e) => { - stats.record_failed(); - eprintln!(" {}", format!("❌ Error: {e}").red()); - if !args.continue_on_error { - return Err(e); - } - has_errors = true; - } - } - } - } - - // Also check all features enabled together - println!(" 🔧 Checking all features together..."); - if args.dry_run { - let fix_str = if args.fix { " --fix" } else { "" }; - let allow_dirty_str = if args.fix && args.allow_dirty { - " --allow-dirty" - } else { - "" - }; - println!( - " [DRY RUN] cargo clippy --target {} -p {} --features {}{}{}", - target, - package.name, - features.join(","), - fix_str, - allow_dirty_str - ); - stats.record_skipped(); - } else { - match run_single_clippy( - target, - package.name.as_ref(), - &features, - args.continue_on_error, - args.fix, - args.allow_dirty, - ) { - Ok(_) => { - stats.record_passed(); - } - Err(e) => { - stats.record_failed(); - eprintln!(" {}", format!("❌ Error: {e}").red()); - if !args.continue_on_error { - return Err(e); - } - has_errors = true; - } - } - } - } - } - } - - let duration = start_time.elapsed(); - stats.print_summary(duration); - - if has_errors { - Err(anyhow!("Clippy checks found errors")) - } else { - Ok(()) - } -} - -/// Get all features of a package -fn get_package_features(package: &Package) -> Vec { - let mut features = Vec::new(); - - // Collect all non-empty feature names - for feature_name in package.features.keys() { - if !feature_name.is_empty() && !feature_name.starts_with("dep:") { - features.push(feature_name.clone()); - } - } - - // Sort for consistent output - features.sort(); - features -} - -/// Run a single clippy check -fn run_single_clippy( - target: &str, - package: &str, - features: &[String], - continue_on_error: bool, - fix: bool, - allow_dirty: bool, -) -> Result<()> { - let mut args = vec![ - "clippy".to_string(), - "--target".to_string(), - target.to_string(), - "-p".to_string(), - package.to_string(), - "--all-targets".to_string(), - ]; - - // Add --fix parameter - if fix { - args.push("--fix".to_string()); - } - - // Add --allow-dirty parameter - if fix && allow_dirty { - args.push("--allow-dirty".to_string()); - } - - // Add features parameter - if !features.is_empty() { - args.push("--features".to_string()); - args.push(features.join(",")); - } - - // Add clippy options - args.push("--".to_string()); - args.push("-D".to_string()); // Treat all warnings as errors - args.push("warnings".to_string()); - - let mut cmd = Command::new("cargo"); - cmd.args(&args); - - // Set environment variable for stricter checking - cmd.env("RUSTFLAGS", "-D warnings"); - - let fix_str = if fix { " --fix" } else { "" }; - let allow_dirty_str = if fix && allow_dirty { - " --allow-dirty" - } else { - "" - }; - println!( - " Executing: {}", - format!( - "cargo clippy --target {} -p {}{}{}{}", - target, - package, - if features.is_empty() { - String::new() - } else { - format!(" --features {}", features.join(",")) - }, - fix_str, - allow_dirty_str - ) - .dimmed() - ); - - let output = cmd.output().context(format!( - "Failed to execute cargo clippy: target={target}, package={package}, features={features:?}" - ))?; - - if output.status.success() { - // Even if successful, check if there's output (sometimes clippy has warnings but still returns success) - if !output.stderr.is_empty() { - let stderr = String::from_utf8_lossy(&output.stderr); - if stderr.contains("warning:") || stderr.contains("error:") { - return Err(anyhow!("Clippy output warnings or errors:\n{stderr}")); - } - } - println!(" {}", "✅ Passed".green()); - Ok(()) - } else { - let stderr = String::from_utf8_lossy(&output.stderr); - let stdout = String::from_utf8_lossy(&output.stdout); - - let error_msg = if !stderr.is_empty() { - stderr.to_string() - } else if !stdout.is_empty() { - stdout.to_string() - } else { - format!( - "clippy check failed, exit code: {}", - output.status.code().unwrap_or(-1) - ) - }; - - if continue_on_error { - eprintln!( - " {}", - format!("⚠️ Error (continuing):\n{error_msg}").yellow() - ); - Ok(()) - } else { - Err(anyhow!("Clippy check failed:\n{error_msg}")) - } - } -} diff --git a/os/axvisor/xtask/src/ctx.rs b/os/axvisor/xtask/src/ctx.rs deleted file mode 100644 index 1a7727725..000000000 --- a/os/axvisor/xtask/src/ctx.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ostool::ctx::{AppContext, PathConfig}; - -use crate::BuildArgs; - -pub struct Context { - pub ctx: AppContext, - pub build_config_path: Option, - pub vmconfigs: Vec, -} - -impl Context { - pub fn new() -> Self { - let workdir = std::env::current_dir().expect("Failed to get current working directory"); - - let ctx = AppContext { - paths: PathConfig { - workspace: workdir.clone(), - manifest: workdir, - ..Default::default() - }, - ..Default::default() - }; - - Context { - ctx, - build_config_path: None, - vmconfigs: vec![], - } - } - - pub fn apply_build_args(&mut self, args: &BuildArgs) { - self.ctx.paths.config.build_dir = args.build_dir.clone(); - self.ctx.paths.config.bin_dir = args.bin_dir.clone(); - } -} diff --git a/os/axvisor/xtask/src/devspace.rs b/os/axvisor/xtask/src/devspace.rs deleted file mode 100644 index 9427da1dc..000000000 --- a/os/axvisor/xtask/src/devspace.rs +++ /dev/null @@ -1,501 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::collections::{BTreeMap, HashMap, hash_map::Entry}; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; - -use anyhow::{Context, Result, anyhow}; -use cargo_metadata::{Metadata, MetadataCommand, Package}; -use serde::{Deserialize, Serialize}; - -const STATE_DIR: &str = ".devspace"; -const STATE_FILE: &str = ".devspace/state.json"; -const PATCH_BEGIN_MARKER: &str = "# >>> devspace patches >>>"; -const PATCH_END_MARKER: &str = "# <<< devspace patches <<<"; -const CRATES_IO_SOURCE_KEY: &str = "crates-io"; - -const DEVSPACE_REPOS: &[&str] = &[ - "arm_vcpu", - "arm_vgic", - "axaddrspace", - "axdevice_base", - "x86_vcpu", - "x86_vlapic", -]; - -const DEVSPACE_REPO_OVERRIDES: &[(&str, &str)] = &[( - "axdevice_base", - "https://github.com/arceos-hypervisor/axdevice_base.git", -)]; - -pub fn start() -> Result<()> { - let metadata = MetadataCommand::new() - .exec() - .context("Failed to run cargo metadata")?; - let repos = resolve_dev_repos(&metadata)?; - - let mut state = load_state()?; - ensure_submodules(&mut state, &repos)?; - save_state(&state)?; - - let specs = compute_patch_specs(&metadata, &repos)?; - apply_patches(&specs)?; - - state.patches = specs - .into_iter() - .map(|spec| PatchRecord { - source: spec.source, - crate_name: spec.crate_name, - }) - .collect(); - save_state(&state)?; - - println!("devspace start completed"); - Ok(()) -} - -pub fn stop() -> Result<()> { - let mut state = load_state()?; - - if !state.patches.is_empty() { - remove_patches(&state.patches)?; - state.patches.clear(); - } - - if !state.modules.is_empty() { - remove_submodules(&state.modules)?; - state.modules.clear(); - } - - save_state(&state)?; - println!("devspace stop completed"); - Ok(()) -} - -fn ensure_submodules(state: &mut DevspaceState, repos: &[DevRepo]) -> Result<()> { - for repo in repos { - let dest_path = Path::new(&repo.dest); - if dest_path.exists() { - continue; - } - - println!("Adding submodule {} -> {}", repo.git_url, repo.dest); - run_git(&[ - "submodule", - "add", - "--force", - repo.git_url.as_str(), - repo.dest.as_str(), - ])?; - run_git(&[ - "submodule", - "update", - "--init", - "--recursive", - repo.dest.as_str(), - ])?; - match state.modules.entry(repo.name.clone()) { - Entry::Occupied(mut entry) => { - entry.get_mut().path = repo.dest.clone(); - } - Entry::Vacant(entry) => { - entry.insert(ManagedModule { - name: repo.name.clone(), - path: repo.dest.clone(), - }); - } - } - } - - Ok(()) -} - -fn remove_submodules(modules: &HashMap) -> Result<()> { - for module in modules.values() { - println!("Removing submodule {}", module.path); - let path = module.path.as_str(); - let _ = run_git(&["submodule", "deinit", "-f", "--", path]); - let git_modules_dir = Path::new(".git/modules").join(path); - if git_modules_dir.exists() { - fs::remove_dir_all(&git_modules_dir) - .with_context(|| format!("Failed to remove {git_modules_dir:?}"))?; - } - if Path::new(path).exists() { - let _ = run_git(&["rm", "-f", "--", path]); - if Path::new(path).exists() { - fs::remove_dir_all(path).with_context(|| format!("Failed to remove {path}"))?; - } - } - } - Ok(()) -} - -fn compute_patch_specs(metadata: &Metadata, repos: &[DevRepo]) -> Result> { - let repo_map = build_repo_lookup(repos); - let mut specs = BTreeMap::new(); - - for pkg in &metadata.packages { - if let Some(spec) = package_patch_spec(pkg, &repo_map) { - specs - .entry((spec.source.clone(), spec.crate_name.clone())) - .or_insert(spec); - } - } - - for repo in repos { - if !specs.contains_key(&(repo.source.clone(), repo.name.clone())) { - return Err(anyhow!( - "Failed to prepare patch for crate {} (source: {})", - repo.name, - repo.source - )); - } - } - - Ok(specs.into_values().collect()) -} - -fn package_patch_spec(pkg: &Package, repo_map: &HashMap) -> Option { - let source_raw = pkg.source.as_ref()?.to_string(); - let normalized = normalize_source(&source_raw)?; - let key = repo_lookup_key(&normalized, pkg.name.as_str()); - let repo = repo_map.get(&key)?; - let manifest = Path::new(pkg.manifest_path.as_str()); - let local_path = manifest_relative_dir(manifest) - .map(|subdir| Path::new(&repo.dest).join(subdir)) - .unwrap_or_else(|| PathBuf::from(&repo.dest)); - Some(PatchSpec { - source: repo.source.to_string(), - crate_name: pkg.name.to_string(), - path: to_unix_path(&local_path), - }) -} - -fn apply_patches(specs: &[PatchSpec]) -> Result<()> { - if specs.is_empty() { - println!("No git dependencies matched managed repos; skipping patch stage"); - return Ok(()); - } - - let config_path = Path::new(".cargo/config.toml"); - let mut contents = if config_path.exists() { - fs::read_to_string(config_path) - .with_context(|| format!("Failed to read {config_path:?}"))? - } else { - String::new() - }; - - let (cleaned, _) = strip_devspace_section(&contents); - contents = cleaned; - - if !contents.is_empty() && !contents.ends_with('\n') { - contents.push('\n'); - } - if !contents.is_empty() && !contents.ends_with("\n\n") { - contents.push('\n'); - } - - contents.push_str(&render_devspace_section(specs)); - if !contents.ends_with('\n') { - contents.push('\n'); - } - - fs::write(config_path, contents).with_context(|| format!("Failed to write {config_path:?}"))?; - Ok(()) -} - -fn remove_patches(_: &[PatchRecord]) -> Result<()> { - let config_path = Path::new(".cargo/config.toml"); - if !config_path.exists() { - return Ok(()); - } - - let original = fs::read_to_string(config_path) - .with_context(|| format!("Failed to read {config_path:?}"))?; - let (cleaned, removed) = strip_devspace_section(&original); - - if removed { - fs::write(config_path, cleaned) - .with_context(|| format!("Failed to write {config_path:?}"))?; - } - Ok(()) -} - -fn load_state() -> Result { - let path = Path::new(STATE_FILE); - if !path.exists() { - return Ok(DevspaceState::default()); - } - - let contents = fs::read_to_string(path).with_context(|| format!("Failed to read {path:?}"))?; - let state = - serde_json::from_str(&contents).with_context(|| format!("Failed to parse {path:?}"))?; - Ok(state) -} - -fn save_state(state: &DevspaceState) -> Result<()> { - fs::create_dir_all(STATE_DIR).context("Failed to create devspace state dir")?; - let data = serde_json::to_string_pretty(state)?; - fs::write(STATE_FILE, data).context("Failed to write devspace state")?; - Ok(()) -} - -fn resolve_dev_repos(metadata: &Metadata) -> Result> { - let override_map: HashMap<&str, &str> = DEVSPACE_REPO_OVERRIDES.iter().copied().collect(); - - DEVSPACE_REPOS - .iter() - .map(|crate_name| { - let override_url = override_map.get(*crate_name).copied(); - - let matches: Vec<&Package> = metadata - .packages - .iter() - .filter(|pkg| pkg.name == *crate_name) - .collect(); - - if matches.is_empty() { - return Err(anyhow!( - "crate {crate_name} not found in workspace metadata" - )); - } - - let pkg = matches - .iter() - .copied() - .find(|pkg| { - pkg.source - .as_ref() - .map(|src| src.to_string().starts_with("git+")) - .unwrap_or(false) - }) - .unwrap_or(*matches.first().unwrap()); - - let source_raw = pkg - .source - .as_ref() - .map(|s| s.to_string()) - .ok_or_else(|| anyhow!("crate {crate_name} has no source information"))?; - - let (patch_source, git_url) = if source_raw.starts_with("git+") { - let normalized = normalize_source(&source_raw).ok_or_else(|| { - anyhow!( - "crate {} has unsupported source {}", - crate_name, - source_raw.clone() - ) - })?; - let git_url = extract_git_url(&source_raw).ok_or_else(|| { - anyhow!( - "crate {} has unsupported source {}", - crate_name, - source_raw.clone() - ) - })?; - (normalized, git_url) - } else if source_raw == "registry+https://github.com/rust-lang/crates.io-index" { - let repo_url = if let Some(url) = pkg.repository.clone() { - url - } else if let Some(url) = override_url { - println!( - "crate {crate_name} is missing repository metadata; using override {url}" - ); - url.to_string() - } else { - return Err(anyhow!( - "crate {crate_name} is from crates.io but missing repository metadata" - )); - }; - (CRATES_IO_SOURCE_KEY.to_string(), repo_url) - } else { - return Err(anyhow!( - "crate {crate_name} uses unsupported source {source_raw}" - )); - }; - - Ok(DevRepo { - name: crate_name.to_string(), - git_url, - source: patch_source, - dest: format!("modules/{crate_name}"), - }) - }) - .collect() -} - -fn build_repo_lookup(repos: &[DevRepo]) -> HashMap { - repos - .iter() - .map(|repo| (repo_lookup_key(&repo.source, &repo.name), repo)) - .collect() -} - -fn manifest_relative_path(path: &Path) -> Option { - let components: Vec<_> = path.components().collect(); - let idx = components - .iter() - .position(|comp| comp.as_os_str() == "checkouts")?; - if idx + 3 >= components.len() { - return None; - } - let mut rel = PathBuf::new(); - for comp in &components[idx + 3..] { - rel.push(comp.as_os_str()); - } - Some(rel) -} - -fn manifest_relative_dir(path: &Path) -> Option { - let mut rel = manifest_relative_path(path)?; - if rel.pop() { - Some(rel) - } else { - Some(PathBuf::new()) - } -} - -fn normalize_source(raw: &str) -> Option { - if let Some(trimmed) = raw.strip_prefix("git+") { - let no_fragment = trimmed.split('#').next().unwrap_or(trimmed); - let no_query = no_fragment.split('?').next().unwrap_or(no_fragment); - let without_git = no_query.trim_end_matches(".git"); - let normalized = without_git.trim_end_matches('/'); - Some(normalized.to_string()) - } else if raw == "registry+https://github.com/rust-lang/crates.io-index" { - Some(CRATES_IO_SOURCE_KEY.to_string()) - } else { - None - } -} - -fn extract_git_url(raw: &str) -> Option { - if !raw.starts_with("git+") { - return None; - } - let trimmed = &raw[4..]; - let no_query = trimmed.split('?').next().unwrap_or(trimmed); - let no_fragment = no_query.split('#').next().unwrap_or(no_query); - Some(no_fragment.to_string()) -} - -fn render_devspace_section(specs: &[PatchSpec]) -> String { - let mut grouped: BTreeMap> = BTreeMap::new(); - for spec in specs { - grouped - .entry(spec.source.clone()) - .or_default() - .insert(spec.crate_name.clone(), spec.path.clone()); - } - - let mut section = String::new(); - section.push_str(PATCH_BEGIN_MARKER); - section.push('\n'); - section.push_str("# Managed by `cargo xtask devspace`"); - section.push('\n'); - - let mut iter = grouped.iter().peekable(); - while let Some((source, crates)) = iter.next() { - section.push_str(&format!("[patch.\"{source}\"]\n")); - for (crate_name, path) in crates { - section.push_str(&format!("{crate_name} = {{ path = \"{path}\" }}\n")); - } - if iter.peek().is_some() { - section.push('\n'); - } - } - - section.push('\n'); - section.push_str(PATCH_END_MARKER); - section.push('\n'); - section -} - -fn strip_devspace_section(contents: &str) -> (String, bool) { - if let Some(start_idx) = contents.find(PATCH_BEGIN_MARKER) - && let Some(end_rel) = contents[start_idx..].find(PATCH_END_MARKER) - { - let end_idx = start_idx + end_rel + PATCH_END_MARKER.len(); - let mut removal_end = end_idx; - let tail = &contents[removal_end..]; - if tail.starts_with("\r\n") { - removal_end += 2; - } else if tail.starts_with('\n') { - removal_end += 1; - } - let mut result = String::with_capacity(contents.len()); - result.push_str(&contents[..start_idx]); - result.push_str(&contents[removal_end..]); - return (result, true); - } - (contents.to_string(), false) -} - -fn to_unix_path(path: &Path) -> String { - path.to_string_lossy().replace('\\', "/") -} - -fn run_git(args: &[&str]) -> Result<()> { - let status = Command::new("git") - .current_dir(workspace_root()?) - .args(args) - .status() - .with_context(|| format!("Failed to run git {}", args.join(" ")))?; - if !status.success() { - return Err(anyhow!("git command failed: git {}", args.join(" "))); - } - Ok(()) -} - -fn workspace_root() -> Result { - std::env::current_dir().context("Failed to resolve workspace root") -} - -#[derive(Default, Serialize, Deserialize)] -struct DevspaceState { - modules: HashMap, - patches: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -struct ManagedModule { - name: String, - path: String, -} - -#[derive(Clone, Serialize, Deserialize)] -struct PatchRecord { - source: String, - crate_name: String, -} - -#[derive(Clone)] -struct PatchSpec { - source: String, - crate_name: String, - path: String, -} - -#[derive(Clone)] -struct DevRepo { - name: String, - git_url: String, - source: String, - dest: String, -} - -fn repo_lookup_key(source: &str, crate_name: &str) -> String { - format!("{source}::{crate_name}") -} diff --git a/os/axvisor/xtask/src/image.rs b/os/axvisor/xtask/src/image.rs deleted file mode 100644 index 3c1b3e16d..000000000 --- a/os/axvisor/xtask/src/image.rs +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Guest Image management commands for the Axvisor build configuration tool -//! -//! This module provides functionality to list, download, and remove -//! pre-built guest images for various supported boards and architectures. The images -//! are downloaded from a specified URL base and verified using SHA-256 checksums. The downloaded -//! images are automatically extracted to a specified output directory. Images can also be removed -//! from the temporary directory. -//! -//! # Usage examples -//! -//! ``` -//! // List available images -//! xtask image ls -//! // Download a specific image and automatically extract it (default behavior) -//! xtask image download evm3588_arceos --output-dir ./images -//! // Download a specific image without extracting -//! xtask image download evm3588_arceos --output-dir ./images --no-extract -//! // Remove a specific image from temp directory -//! xtask image rm evm3588_arceos -//! ``` - -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use anyhow::{Result, anyhow}; -use clap::{Parser, Subcommand}; -use flate2::read::GzDecoder; -use tar::Archive; - -mod config; -mod download; -mod registry; -mod spec; -mod storage; - -use config::ImageConfig; -use spec::ImageSpecRef; -use storage::Storage; - -/// Image management command line arguments. -#[derive(Parser)] -pub struct ImageArgs { - #[command(flatten)] - pub overrides: ImageConfigOverrides, - - /// Image subcommand to run: `ls`, `download`, `rm`, or `sync`. - #[command(subcommand)] - pub command: ImageCommands, -} - -#[derive(Parser, Default)] -pub struct ImageConfigOverrides { - /// The path to the local storage of images. Override the config file. - #[arg(short('S'), long, global = true)] - pub local_storage: Option, - - /// The URL of the remote registry of images. Override the config file. - #[arg(short('R'), long, global = true)] - pub registry: Option, - - /// Do not sync from remote registry even if the local image storage is - /// broken, missing, or out of date. Override the config file. - #[arg(short('N'), long, global = true)] - pub no_auto_sync: bool, - - /// The threshold in seconds to automatically synchronize image list from - /// remote registry. 0 means never. Override the config file. - #[arg(long, global = true)] - pub auto_sync_threshold: Option, -} - -impl ImageConfigOverrides { - /// Applies CLI overrides onto the given config (in-place). - /// - /// # Arguments - /// - /// * `config` - Config to mutate; non-`None` override fields overwrite corresponding values - pub fn apply_on(&self, config: &mut ImageConfig) { - if let Some(local_storage) = self.local_storage.as_ref() { - config.local_storage = local_storage.clone(); - } - if let Some(registry) = self.registry.as_ref() { - config.registry = registry.clone(); - } - if self.no_auto_sync { - config.auto_sync = false; - } - if let Some(auto_sync_threshold) = self.auto_sync_threshold { - config.auto_sync_threshold = auto_sync_threshold; - } - } -} - -/// Image management commands -#[derive(Subcommand)] -pub enum ImageCommands { - /// List all available images. - Ls { - /// Show different versions of the same image in separate lines. - #[arg(short, long)] - verbose: bool, - - /// Filter images by name pattern. - pattern: Option, - }, - - /// Download the specified image and automatically extract it. Use ASCII - /// colon to specify version, e.g. `evm3588_arceos:0.0.22`; omit for latest. - Download { - /// Image to download: `name` or `name:version`. - image_name: String, - - /// Output directory for the downloaded image, defaults to - /// "/tmp/.axvisor-images/". - #[arg(short, long)] - output_dir: Option, - - /// Do not extract after download. - #[arg(long)] - no_extract: bool, - }, - - /// Remove the specified image from temp directory. Use ASCII colon to - /// specify version, e.g. `evm3588_arceos:0.0.22`; omit for default path. - Rm { - /// Image to remove: `name` or `name:version`. - image_name: String, - }, - - /// Synchronize image list from a remote registry. - Sync, - - /// Reset the image config file to default. - Defconfig, -} - -/// Converts a path to absolute; joins with current dir if relative. -fn to_absolute_path(path: &Path) -> Result { - Ok(if path.is_absolute() { - path.to_path_buf() - } else { - std::env::current_dir()?.join(path) - }) -} - -/// Returns the path to the AxVisor repository root (parent of the xtask crate). -fn get_axvisor_repo_dir() -> Result { - // CARGO_MANIFEST_DIR contains the path of the xtask crate, and we need to - // get the parent directory to get the AxVisor repository directory. - Ok(Path::new(&std::env::var("CARGO_MANIFEST_DIR")?) - .parent() - .ok_or_else(|| anyhow!("Failed to determine AxVisor repository directory"))? - .to_path_buf()) -} - -impl ImageArgs { - /// Loads image configuration, merging CLI overrides with values from the config file. - pub async fn get_config(&self) -> Result { - let mut config = ImageConfig::read_config(&get_axvisor_repo_dir()?)?; - self.overrides.apply_on(&mut config); - Ok(config) - } - - /// Executes the selected image subcommand (`ls`, `download`, `rm`, `sync`, or `defconfig`). - pub async fn execute(&self) -> Result<()> { - match &self.command { - ImageCommands::Ls { verbose, pattern } => { - self.list_images(*verbose, pattern.as_deref()).await?; - } - ImageCommands::Download { - image_name, - output_dir, - no_extract, - } => { - self.download_image(image_name, output_dir.as_deref(), !no_extract) - .await?; - } - ImageCommands::Rm { image_name } => { - self.remove_image(image_name).await?; - } - ImageCommands::Sync => { - self.sync_registry().await?; - } - ImageCommands::Defconfig => { - ImageConfig::reset_config(&get_axvisor_repo_dir()?)?; - } - } - - Ok(()) - } - - /// Lists all available images from the local registry to stdout. - /// - /// # Arguments - /// - /// * `verbose` - If `true`, show each version separately; if `false`, merge same-name images and show version count - /// * `pattern` - If `Some`, filter by name: try regex match first, fallback to substring - pub async fn list_images(&self, verbose: bool, pattern: Option<&str>) -> Result<()> { - let config = self.get_config().await?; - let storage = Storage::new_from_config(&config).await?; - - storage.image_registry.print(verbose, pattern); - - Ok(()) - } - - /// Downloads the specified image and optionally extracts it. - /// - /// # Arguments - /// - /// * `spec` - Image spec (name and optional version) - /// * `output_dir` - If `Some`, write the `.tar.gz` to this directory; if `None`, use config's local storage path - /// * `extract` - If `true`, extract the archive after download - pub async fn download_image( - &self, - spec: impl Into>, - output_dir: Option<&str>, - extract: bool, - ) -> Result<()> { - let spec = spec.into(); - let config = self.get_config().await?; - let storage = Storage::new_from_config(&config).await?; - - let output_path = match output_dir { - Some(dir) => { - storage - .download_image_to(spec, &to_absolute_path(Path::new(dir))?) - .await? - } - None => storage.download_image(spec).await?, - }; - - if extract { - println!("Extracting image..."); - - let extract_dir = output_path - .parent() - .ok_or_else(|| anyhow!("Unable to determine parent directory of downloaded file"))? - .join(storage::image_extract_dir_name(spec)); - - fs::create_dir_all(&extract_dir)?; - - let tar_gz = fs::File::open(&output_path)?; - let decoder = GzDecoder::new(tar_gz); - let mut archive = Archive::new(decoder); - - archive.unpack(&extract_dir)?; - - println!("Image extracted to: {}", extract_dir.display()); - } - Ok(()) - } - - /// Removes the specified image from local storage (both `.tar.gz` and extracted directory). - /// - /// # Arguments - /// - /// * `spec` - Image spec (name and optional version) - pub async fn remove_image(&self, spec: impl Into>) -> Result<()> { - let spec = spec.into(); - let config = self.get_config().await?; - let storage = Storage::new_from_config(&config).await?; - - if storage.remove_image(spec).await? { - println!("Image removed successfully"); - } else { - println!("No files found for image: {}", spec.name); - } - Ok(()) - } - - /// Synchronizes the image list from the remote registry to local storage. - /// - /// Overwrites the local `images.toml` with the registry contents. - pub async fn sync_registry(&self) -> Result<()> { - let config: ImageConfig = self.get_config().await?; - let _ = Storage::new_from_registry(config.registry, config.local_storage).await?; - Ok(()) - } -} - -/// Dispatches and runs the image subcommand (ls, download, rm, sync) from parsed CLI arguments. -/// -/// # Arguments -/// -/// * `args` - Parsed image CLI arguments (subcommand and its options) -/// -/// # Returns -/// -/// * `Ok(())` - Subcommand completed successfully -/// * `Err` - Subcommand failed (e.g. list load, download, checksum, sync, or remove error) -/// -/// # Examples -/// -/// ```ignore -/// xtask image ls -/// xtask image download evm3588_arceos --output-dir ./images -/// xtask image rm evm3588_arceos -/// ``` -pub async fn run_image(args: ImageArgs) -> Result<()> { - args.execute().await -} diff --git a/os/axvisor/xtask/src/image/config.rs b/os/axvisor/xtask/src/image/config.rs deleted file mode 100644 index 90544a737..000000000 --- a/os/axvisor/xtask/src/image/config.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Image configuration management. -//! -//! Handles reading and writing of the `.image.toml` config file. - -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use anyhow::{Result, anyhow}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -/// Default registry URL for the image list. -pub const DEFAULT_REGISTRY_URL: &str = "https://raw.githubusercontent.com/arceos-hypervisor/axvisor-guest/refs/heads/main/registry/default.toml"; - -/// Relative path to the image config file under the repository root. -const IMAGE_CONFIG_PATH: &str = ".image.toml"; - -/// Default auto-sync threshold in seconds (7 days). -const DEFAULT_AUTO_SYNC_THRESHOLD: u64 = 60 * 60 * 24 * 7; - -/// Configuration for the image management. -/// -/// This struct is used to parse image config file (in [`IMAGE_CONFIG_PATH`]). -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] -pub struct ImageConfig { - /// The path to the local storage of images. - pub local_storage: PathBuf, - /// The URL of the remote registry of images. - pub registry: String, - /// Automatically synchronize image list from remote registry if the local - /// storage is broken, missing, or out of date. - pub auto_sync: bool, - /// The threshold in seconds to automatically synchronize image list from - /// remote registry due to too long since last synchronization. 0 means - /// never. - pub auto_sync_threshold: u64, -} - -impl ImageConfig { - /// Creates a config with default values (temp dir storage, default registry, auto-sync on). - pub fn new_default() -> Self { - Self { - local_storage: std::env::temp_dir().join(".axvisor-images"), - registry: DEFAULT_REGISTRY_URL.to_string(), - auto_sync: true, - auto_sync_threshold: DEFAULT_AUTO_SYNC_THRESHOLD, - } - } - - /// Returns the full path to the image config file. - /// - /// # Arguments - /// - /// * `base_dir` - Repository root directory (e.g. AxVisor repo root) - /// - /// # Returns - /// - /// Path `base_dir/.image.toml` - pub fn get_config_file_path(base_dir: &Path) -> Result { - Ok(base_dir.join(IMAGE_CONFIG_PATH)) - } - - /// Reads the image config from disk, creating a default file if it does not exist. - /// - /// # Arguments - /// - /// * `base_dir` - Repository root directory - /// - /// # Returns - /// - /// * `Ok(ImageConfig)` - Parsed config or default if newly created - /// * `Err` - File read or TOML parse error - pub fn read_config(base_dir: &Path) -> Result { - let path = Self::get_config_file_path(base_dir)?; - - if !path.exists() { - println!( - "Creating default image config file at {}...", - path.display() - ); - Self::write_config(base_dir, &Self::new_default()) - .map_err(|e| anyhow!("Failed to create default image config file: {e}"))?; - return Ok(Self::new_default()); - } - - let s = fs::read_to_string(&path)?; - toml::from_str(&s).map_err(|e| anyhow!("Invalid image config file: {e}")) - } - - /// Writes the given config to the image config file. - /// - /// # Arguments - /// - /// * `base_dir` - Repository root directory - /// * `config` - Config to serialize and write - /// - /// # Returns - /// - /// * `Ok(())` - Config written successfully - /// * `Err` - File write or serialization error - pub fn write_config(base_dir: &Path, config: &Self) -> Result<()> { - let path = Self::get_config_file_path(base_dir)?; - fs::write(path, toml::to_string(config)?) - .map_err(|e| anyhow!("Failed to write image config file: {e}")) - } - - /// Resets the image config file to default values. - /// - /// # Arguments - /// - /// * `base_dir` - Repository root directory - /// - /// # Returns - /// - /// * `Ok(())` - Config reset and written successfully - /// * `Err` - File write error - pub fn reset_config(base_dir: &Path) -> Result<()> { - let default_config = Self::new_default(); - Self::write_config(base_dir, &default_config) - .map_err(|e| anyhow!("Failed to reset image config file: {e}")) - } -} - -impl std::fmt::Display for ImageConfig { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "Local storage: {}", self.local_storage.display())?; - writeln!(f, "Registry: {}", self.registry)?; - writeln!(f, "Auto sync: {}", self.auto_sync)?; - - if self.auto_sync_threshold == 0 { - writeln!(f, "Auto sync threshold: 0 (never)")?; - } else { - let threshold_days = self.auto_sync_threshold / (60 * 60 * 24); - writeln!( - f, - "Auto sync threshold: {} ({} day(s))", - self.auto_sync_threshold, threshold_days - )?; - } - - Ok(()) - } -} diff --git a/os/axvisor/xtask/src/image/download.rs b/os/axvisor/xtask/src/image/download.rs deleted file mode 100644 index 157743806..000000000 --- a/os/axvisor/xtask/src/image/download.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Download utilities and checksum verification. -//! -//! Provides async HTTP download with optional progress reporting and SHA256 verification. - -use std::{fs, io::Read, path::Path, time::Duration}; - -use anyhow::{Result, anyhow}; -use reqwest::Client; -use sha2::{Digest, Sha256}; -use tokio::io::{AsyncWriteExt, BufWriter}; - -/// HTTP client with 30s connect timeout and 30min total timeout. -fn http_client() -> Result { - Client::builder() - .connect_timeout(Duration::from_secs(30)) - .timeout(Duration::from_secs(1800)) - .build() - .map_err(|e| anyhow!("Failed to create HTTP client: {e}")) -} - -/// Downloads a URL and returns its body as a string. -/// -/// # Arguments -/// -/// * `url` - URL to download -pub async fn download_to_string(url: &str) -> Result { - let client = http_client()?; - let response = client.get(url).send().await?; - if !response.status().is_success() { - return Err(anyhow!("failed to download: HTTP {}", response.status())); - } - let body = response.text().await?; - Ok(body) -} - -/// Downloads a URL to a local file, creating parent directories as needed. -/// -/// # Arguments -/// -/// * `url` - URL to download -/// * `path` - Local path to write the file -/// * `progress_label` - If `Some`, prints download progress (percent/bytes) with this label -pub async fn download_to_path(url: &str, path: &Path, progress_label: Option<&str>) -> Result<()> { - let client = http_client()?; - let mut response = client.get(url).send().await?; - if !response.status().is_success() { - return Err(anyhow!("failed to download: HTTP {}", response.status())); - } - - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - - let file = tokio::fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(path) - .await?; - let mut writer = BufWriter::new(file); - - let content_length = response.content_length(); - let mut downloaded = 0u64; - - while let Some(chunk) = response.chunk().await? { - writer - .write_all(&chunk) - .await - .map_err(|e| anyhow!("Error writing to file: {e}"))?; - downloaded += chunk.len() as u64; - if let Some(label) = progress_label { - if let Some(total) = content_length { - let percent = (downloaded * 100) / total; - print!("\r{label}: {percent}% ({downloaded}/{total} bytes)"); - } else { - print!("\r{label}: {downloaded} bytes"); - } - std::io::Write::flush(&mut std::io::stdout()).unwrap(); - } - } - - println!(); - - writer - .flush() - .await - .map_err(|e| anyhow!("Error flushing file: {e}"))?; - Ok(()) -} - -/// Verifies the SHA256 checksum of a file. -/// -/// # Arguments -/// -/// * `file_path` - Path to the file to verify -/// * `expected_sha256` - Expected SHA256 checksum as lowercase hex -/// -/// # Returns -/// -/// * `Ok(true)` - Checksum matches -/// * `Ok(false)` - Checksum does not match -/// * `Err` - I/O or read error during verification -pub fn image_verify_sha256(file_path: &Path, expected_sha256: &str) -> Result { - let mut file = fs::File::open(file_path)?; - let mut hasher = Sha256::new(); - let mut buffer = [0; 8192]; - - loop { - let bytes_read = file.read(&mut buffer)?; - if bytes_read == 0 { - break; - } - hasher.update(&buffer[..bytes_read]); - } - - let result = hasher.finalize(); - let actual_sha256 = format!("{result:x}"); - - Ok(actual_sha256 == expected_sha256) -} diff --git a/os/axvisor/xtask/src/image/registry.rs b/os/axvisor/xtask/src/image/registry.rs deleted file mode 100644 index 8c3cf34ad..000000000 --- a/os/axvisor/xtask/src/image/registry.rs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Image registry data structures and parsing. -//! -//! Defines `ImageEntry` and `ImageRegistry` for the TOML-based image list format. - -use std::{collections::BTreeMap, fs, path::Path}; - -use anyhow::{Result, anyhow}; -use chrono::{DateTime, Utc}; -use regex::Regex; -use serde::{Deserialize, Serialize}; - -use super::download::download_to_string; -use super::spec::ImageSpecRef; - -/// An image entry in the image list file (one row in the registry TOML). -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct ImageEntry { - /// Unique image identifier (e.g. `qemu_x86_64_nimbos`, `evm3588_arceos`). - pub name: String, - /// Version of the image (required in registry config). - pub version: String, - /// Release timestamp (UTC). Optional. Entries without it sort earliest when resolving by name only. - /// Serialized as RFC 3339 / ISO 8601 in TOML (e.g. `2026-01-06T03:10:51Z`). - pub released_at: Option>, - /// Short human-readable description of the image. - pub description: String, - /// SHA-256 checksum of the image archive (hex string). - pub sha256: String, - /// Target architecture (e.g. `x86_64`, `aarch64`). - pub arch: String, - /// URL to download the image archive (e.g. `.tar.gz`). - pub url: String, -} - -/// An image list contains a list of [`ImageEntry`]s (top-level structure of the registry TOML). -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ImageRegistry { - /// All image entries from the registry. - pub images: Vec, -} - -/// A single entry in the `[[includes]]` array of a registry TOML. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IncludeEntry { - /// URL of another registry TOML to include and merge. - pub url: String, -} - -/// Raw registry as parsed from TOML: may contain `includes` and/or `images`. -/// Used when fetching from network; local saved registry has only `images`. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct RawRegistry { - /// Optional list of registry URLs to include and merge. - #[serde(default)] - pub includes: Vec, - /// Image entries (may be empty if registry only includes other URLs). - #[serde(default)] - pub images: Vec, -} - -/// Merges multiple image entry lists into one, deduplicating by (name, version). -/// -/// On conflict (same name+version, different other fields), prints a warning and keeps one entry. -/// -/// # Arguments -/// -/// * `sources` - Iterator of image entry vectors to merge -pub fn merge_entries(sources: impl IntoIterator>) -> Vec { - use std::collections::HashMap; - let mut by_key: HashMap<(String, String), ImageEntry> = HashMap::new(); - for entries in sources { - for entry in entries { - let key = (entry.name.clone(), entry.version.clone()); - if let Some(existing) = by_key.get(&key) { - if existing != &entry { - println!( - "Warning: conflict for image {} version {} (different description/sha256/arch/url); keeping existing entry.", - entry.name, entry.version - ); - } - continue; - } - by_key.insert(key, entry); - } - } - let mut out: Vec = by_key.into_values().collect(); - out.sort_by(|a, b| { - (a.name.as_str(), a.version.as_str()).cmp(&(b.name.as_str(), b.version.as_str())) - }); - out -} - -impl ImageRegistry { - /// Fetches a registry from `url`, resolving `[[includes]]`, and returns a merged registry. - /// - /// # Arguments - /// - /// * `url` - URL of the registry TOML to fetch - pub async fn fetch_with_includes(url: &str) -> Result { - use std::collections::{HashSet, VecDeque}; - - let mut all_sources: Vec> = Vec::new(); - let mut queue: VecDeque = VecDeque::new(); - let mut seen: HashSet = HashSet::new(); - queue.push_back(url.to_string()); - - while let Some(current_url) = queue.pop_front() { - if !seen.insert(current_url.clone()) { - continue; // already fetched, skip - } - let body = download_to_string(¤t_url).await?; - let raw: RawRegistry = toml::from_str(&body) - .map_err(|e| anyhow!("Invalid registry format at {}: {e}", current_url))?; - - all_sources.push(raw.images); - - for include in raw.includes { - queue.push_back(include.url); - } - } - - let images = merge_entries(all_sources); - Ok(ImageRegistry { images }) - } - - /// Loads the image registry from a TOML file. - /// - /// # Arguments - /// - /// * `path` - Path to the registry TOML file - pub fn load_from_file(path: &Path) -> Result { - let s = fs::read_to_string(path) - .map_err(|e| anyhow!("Failed to read image registry from {}: {e}", path.display()))?; - toml::from_str(&s).map_err(|e| anyhow!("Invalid image list format: {e}")) - } - - /// Prints the image list in a formatted table to stdout. - /// - /// # Arguments - /// - /// * `verbose` - If `true`, show each version separately; if `false`, merge same-name images and show version count - /// * `pattern` - If `Some`, filter by name: try regex match first (partial), fallback to substring - pub fn print(&self, verbose: bool, pattern: Option<&str>) { - let entries = self.filtered_entries(pattern); - if verbose { - Self::print_verbose(&entries); - } else { - Self::print_merged(&entries); - } - } - - /// Filters entries by pattern: regex match (partial) or substring. - fn filtered_entries<'a>(&'a self, pattern: Option<&str>) -> Vec<&'a ImageEntry> { - let Some(pat) = pattern else { - return self.images.iter().collect(); - }; - let re = Regex::new(pat).ok(); - self.images - .iter() - .filter(|e| match &re { - Some(r) => r.is_match(&e.name), - None => e.name.contains(pat), - }) - .collect() - } - - fn print_verbose(entries: &[&ImageEntry]) { - println!( - "{:<25} {:<12} {:<15} {:<50}", - "Name", "Version", "Architecture", "Description" - ); - println!("{}", "-".repeat(102)); - for image in entries { - println!( - "{:<25} {:<12} {:<15} {:<50}", - image.name, image.version, image.arch, image.description - ); - } - } - - fn print_merged(entries: &[&ImageEntry]) { - let by_name: BTreeMap<&str, Vec<&ImageEntry>> = - entries.iter().fold(BTreeMap::new(), |mut m, e| { - m.entry(e.name.as_str()).or_default().push(*e); - m - }); - println!( - "{:<25} {:<12} {:<15} {:<50}", - "Name", "Version", "Architecture", "Description" - ); - println!("{}", "-".repeat(102)); - for (name, vers) in by_name { - let n = vers.len(); - let version_str = if n == 1 { - "1 version".to_string() - } else { - format!("{} versions", n) - }; - let first = vers.first().unwrap(); - println!( - "{:<25} {:<12} {:<15} {:<50}", - name, version_str, first.arch, first.description - ); - } - } - - /// Returns an iterator over all image entries in the registry. - pub fn iter(&self) -> impl Iterator { - self.images.iter() - } - - /// Looks up an image by spec (name and optional version). - /// - /// When version is `Some`, returns the exact match. When `None`, returns the entry with the - /// latest `released_at`; entries without `released_at` sort earliest. - /// - /// # Arguments - /// - /// * `spec` - Image spec (name and optional version) - pub fn find(&self, spec: ImageSpecRef<'_>) -> Option<&ImageEntry> { - match spec.version { - Some(v) => self.iter().find(|e| e.name == spec.name && e.version == v), - None => self - .iter() - .filter(|e| e.name == spec.name) - .max_by(|a, b| a.released_at.cmp(&b.released_at)), - } - } -} diff --git a/os/axvisor/xtask/src/image/spec.rs b/os/axvisor/xtask/src/image/spec.rs deleted file mode 100644 index 39b6eda26..000000000 --- a/os/axvisor/xtask/src/image/spec.rs +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Image spec: owned and reference types for (name, version). -//! -//! Parses specs as `name` or `name:version` (ASCII colon) and provides -//! [`ImageSpec`] (owned) and [`ImageSpecRef`] (reference) with common trait impls. - -use std::fmt; - -/// Owned image spec: name and optional version. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[allow(dead_code)] -pub struct ImageSpec { - /// Image name (e.g. `evm3588_arceos`). - pub name: String, - /// Image version; `None` means "latest" or default path. - pub version: Option, -} - -/// Reference image spec: borrowed name and optional version. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct ImageSpecRef<'a> { - /// Image name. - pub name: &'a str, - /// Image version; `None` means "latest" or default path. - pub version: Option<&'a str>, -} - -#[allow(dead_code)] -impl ImageSpec { - /// Parses a spec string: `name` or `name:version` (ASCII colon). - /// - /// # Arguments - /// - /// * `s` - Spec string (e.g. `evm3588_arceos` or `evm3588_arceos:0.0.22`) - pub fn parse(s: &str) -> ImageSpec { - let r = ImageSpecRef::parse(s); - r.to_owned() - } - - /// Creates an owned spec from a reference spec. - /// - /// # Arguments - /// - /// * `r` - Reference spec to clone - pub fn from_ref(r: ImageSpecRef<'_>) -> ImageSpec { - ImageSpec { - name: r.name.to_string(), - version: r.version.map(String::from), - } - } - - /// Borrows this spec as an [`ImageSpecRef`]. - pub fn as_ref(&self) -> ImageSpecRef<'_> { - ImageSpecRef { - name: &self.name, - version: self.version.as_deref(), - } - } -} - -impl<'a> ImageSpecRef<'a> { - /// Parses a spec string: `name` or `name:version` (ASCII colon). - /// - /// The returned spec borrows from `s`. - /// - /// # Arguments - /// - /// * `s` - Spec string (e.g. `evm3588_arceos` or `evm3588_arceos:0.0.22`) - pub fn parse(s: &'a str) -> ImageSpecRef<'a> { - match s.split_once(':') { - Some((name, version)) => ImageSpecRef { - name, - version: Some(version), - }, - None => ImageSpecRef { - name: s, - version: None, - }, - } - } - - /// Converts to an owned [`ImageSpec`]. - pub fn to_owned(self) -> ImageSpec { - ImageSpec::from_ref(self) - } -} - -impl fmt::Display for ImageSpec { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.version { - Some(v) => write!(f, "{}:{}", self.name, v), - None => write!(f, "{}", self.name), - } - } -} - -impl fmt::Display for ImageSpecRef<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.version { - Some(v) => write!(f, "{}:{}", self.name, v), - None => write!(f, "{}", self.name), - } - } -} - -impl From> for ImageSpec { - fn from(r: ImageSpecRef<'_>) -> Self { - ImageSpec::from_ref(r) - } -} - -impl<'a> From<&'a str> for ImageSpecRef<'a> { - fn from(s: &'a str) -> Self { - ImageSpecRef::parse(s) - } -} - -impl<'a> From<&'a String> for ImageSpecRef<'a> { - fn from(s: &'a String) -> Self { - ImageSpecRef::parse(s.as_str()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_name_only() { - let r = ImageSpecRef::parse("evm3588_arceos"); - assert_eq!(r.name, "evm3588_arceos"); - assert_eq!(r.version, None); - } - - #[test] - fn parse_name_and_version() { - let r = ImageSpecRef::parse("evm3588_arceos:0.0.22"); - assert_eq!(r.name, "evm3588_arceos"); - assert_eq!(r.version, Some("0.0.22")); - } - - #[test] - fn display_ref() { - assert_eq!(ImageSpecRef::parse("a").to_string(), "a"); - assert_eq!(ImageSpecRef::parse("a:b").to_string(), "a:b"); - } - - #[test] - fn as_ref() { - let spec = ImageSpec::parse("x:1"); - let r = spec.as_ref(); - assert_eq!(r.name, "x"); - assert_eq!(r.version, Some("1")); - } -} diff --git a/os/axvisor/xtask/src/image/storage.rs b/os/axvisor/xtask/src/image/storage.rs deleted file mode 100644 index 0b42c49dd..000000000 --- a/os/axvisor/xtask/src/image/storage.rs +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Local image storage management. -//! -//! Provides `Storage` for managing a local image directory and its registry index. - -use std::path::Path; -use std::time::{SystemTime, UNIX_EPOCH}; -use std::{fs, path::PathBuf}; - -use anyhow::{Result, anyhow}; - -use super::config::ImageConfig; -use super::download::{download_to_path, image_verify_sha256}; -use super::registry::{ImageEntry, ImageRegistry}; -use super::spec::ImageSpecRef; - -/// Filename of the image registry index inside the local storage directory. -pub const REGISTRY_FILENAME: &str = "images.toml"; - -/// Filename storing the last sync timestamp (Unix seconds) inside the local storage directory. -const LAST_SYNC_FILENAME: &str = ".last_sync"; - -// ----------------------------------------------------------------------------- -// Path and naming helpers (free functions, no Storage instance needed) -// ----------------------------------------------------------------------------- - -/// Returns the path to the registry index file within a storage directory. -/// -/// # Arguments -/// -/// * `storage_path` - Root path of the local image storage -pub fn registry_filepath(storage_path: &Path) -> PathBuf { - storage_path.join(REGISTRY_FILENAME) -} - -/// Path to `.last_sync` file in the storage directory. -fn last_sync_filepath(storage_path: &Path) -> PathBuf { - storage_path.join(LAST_SYNC_FILENAME) -} - -/// Current time as Unix timestamp (seconds). -fn current_unix_timestamp() -> Result { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|e| anyhow!("System time error: {e}")) - .map(|d| d.as_secs()) -} - -/// Reads the last sync timestamp from `.last_sync`; returns `None` if missing or invalid. -fn read_last_sync_time(storage_path: &Path) -> Option { - let path = last_sync_filepath(storage_path); - if !path.exists() { - return None; - } - let s = match fs::read_to_string(&path) { - Ok(s) => s, - Err(e) => { - println!( - "Note: could not read last sync file {}: {e}; treating as no previous sync.", - path.display() - ); - return None; - } - }; - let s = s.trim(); - if s.is_empty() { - return None; - } - match s.parse::() { - Ok(ts) => Some(ts), - Err(_) => { - println!( - "Note: last sync file {} has invalid content; treating as no previous sync.", - path.display() - ); - None - } - } -} - -/// Writes the current timestamp to `.last_sync`. -fn write_last_sync_time(storage_path: &Path) -> Result<()> { - let now = current_unix_timestamp()?; - let path = last_sync_filepath(storage_path); - fs::write(&path, now.to_string()).map_err(|e| anyhow!("Failed to write last sync file: {e}")) -} - -/// Canonical archive filename for an image: `{name}.tar.gz` or `{name}-{version}.tar.gz`. -/// -/// # Arguments -/// -/// * `spec` - Image spec (name and optional version) -pub fn image_archive_filename(spec: ImageSpecRef<'_>) -> String { - match spec.version { - Some(v) => format!("{}-{}.tar.gz", spec.name, v), - None => format!("{}.tar.gz", spec.name), - } -} - -/// Canonical extract directory name for an image: `{name}` or `{name}-{version}`. -/// -/// # Arguments -/// -/// * `spec` - Image spec (name and optional version) -pub fn image_extract_dir_name(spec: ImageSpecRef<'_>) -> String { - match spec.version { - Some(v) => format!("{}-{}", spec.name, v), - None => spec.name.to_string(), - } -} - -/// Returns the path where an image archive (`.tar.gz`) would be stored. -pub fn image_path(storage_path: &Path, spec: ImageSpecRef<'_>) -> PathBuf { - storage_path.join(image_archive_filename(spec)) -} - -/// Local image storage backed by a directory and an image registry index. -pub struct Storage { - /// Root path of the local image storage directory. - pub path: PathBuf, - /// Parsed image registry (list of available images). - pub image_registry: ImageRegistry, -} - -// ----------------------------------------------------------------------------- -// Construction -// ----------------------------------------------------------------------------- - -impl Storage { - /// Creates a storage instance from an existing local directory. - /// - /// Loads the image registry from `images.toml` in the storage path. - /// - /// # Arguments - /// - /// * `path` - Path to the local storage directory (must contain `images.toml`) - /// - /// # Returns - /// - /// * `Ok(Self)` - Storage loaded successfully - /// * `Err` - Directory or registry file read/parse error - pub fn new(path: PathBuf) -> Result { - let registry_filepath = registry_filepath(&path); - let image_registry = ImageRegistry::load_from_file(®istry_filepath)?; - Ok(Self { - path, - image_registry, - }) - } - - /// Creates storage by downloading the registry index from the remote URL. This method does not - /// affect existing images in the local storage. - /// - /// If the registry TOML contains `[[includes]]`, those URLs are fetched recursively - /// and merged (deduplicated by name+version). The local saved registry has no `includes`. - /// - /// # Arguments - /// - /// * `registry` - URL of the registry TOML file to download - /// * `path` - Path to the local storage directory - /// - /// # Returns - /// - /// * `Ok(Self)` - Registry downloaded and storage created - /// * `Err` - Download, directory creation, or parse error - pub async fn new_from_registry(registry: String, path: PathBuf) -> Result { - fs::create_dir_all(&path).map_err(|e| anyhow!("Failed to create directory: {e}"))?; - - let registry_filepath = registry_filepath(&path); - - let image_registry = ImageRegistry::fetch_with_includes(®istry).await?; - let toml_content = toml::to_string_pretty(&image_registry) - .map_err(|e| anyhow!("Failed to serialize registry: {e}"))?; - fs::write(®istry_filepath, toml_content) - .map_err(|e| anyhow!("Failed to write registry file: {e}"))?; - write_last_sync_time(&path)?; - - println!("Image list saved to {}", registry_filepath.display()); - - Ok(Self { - path, - image_registry, - }) - } - - /// Creates storage, falling back to syncing from the remote registry if local load fails. - /// When local load succeeds and `auto_sync_threshold` is non-zero, checks the last sync - /// time (stored in `.last_sync` under the storage path) and syncs from the remote registry - /// if the threshold in seconds has been exceeded (or no last sync time exists). - /// - /// # Arguments - /// - /// * `path` - Path to the local storage directory - /// * `registry` - URL of the remote registry to sync from when local storage is invalid or stale - /// * `auto_sync_threshold` - Seconds since last sync before auto-updating; 0 means never update when load succeeds - /// - /// # Returns - /// - /// * `Ok(Self)` - Storage from local dir or from synced registry - /// * `Err` - Both local load and sync failed - pub async fn new_with_auto_sync( - path: PathBuf, - registry: String, - auto_sync_threshold: u64, - ) -> Result { - let storage = match Self::new(path.clone()) { - Ok(storage) => storage, - Err(e) => { - println!("Error while loading local storage: {e}"); - println!("Auto syncing from registry {registry}..."); - let storage = Self::new_from_registry(registry, path).await?; - return Ok(storage); - } - }; - - if auto_sync_threshold == 0 { - return Ok(storage); - } - - let now = current_unix_timestamp()?; - let last_sync = read_last_sync_time(&storage.path); - let need_sync = match last_sync { - None => true, - Some(ts) => now.saturating_sub(ts) >= auto_sync_threshold, - }; - - if !need_sync { - return Ok(storage); - } - - println!( - "Last sync was {} (threshold: {}s). Auto syncing from registry {registry}...", - last_sync - .map(|ts| format!("{}s ago", now - ts)) - .unwrap_or_else(|| "never".to_string()), - auto_sync_threshold - ); - - // backup registry file so we can restore on sync failure. - let registry_path = registry_filepath(&storage.path); - let registry_backup = fs::read_to_string(®istry_path) - .map_err(|e| anyhow!("Failed to read registry file: {e}"))?; - - match Self::new_from_registry(registry, path).await { - Ok(new_storage) => Ok(new_storage), - Err(e) => { - println!("Auto sync failed: {e}"); - println!("Restoring previous registry and using existing storage."); - - fs::write(®istry_path, registry_backup) - .map_err(|e| anyhow!("Failed to write registry file: {e}"))?; - - Ok(storage) - } - } - } - - /// Creates storage from config, optionally auto-syncing when local storage is invalid. - /// - /// # Arguments - /// - /// * `config` - Image config (storage path, registry URL, auto-sync settings) - /// - /// # Returns - /// - /// * `Ok(Self)` - Storage loaded or synced according to config - /// * `Err` - Load or sync failed - pub async fn new_from_config(config: &ImageConfig) -> Result { - if config.auto_sync { - Self::new_with_auto_sync( - config.local_storage.clone(), - config.registry.clone(), - config.auto_sync_threshold, - ) - .await - } else { - Self::new(config.local_storage.clone()) - } - } -} - -// ----------------------------------------------------------------------------- -// Download and remove -// ----------------------------------------------------------------------------- - -impl Storage { - /// Resolves an image by name and optional version (latest by `released_at` when version is `None`). - fn resolve_image(&self, spec: ImageSpecRef<'_>) -> Option<&ImageEntry> { - self.image_registry.find(spec) - } - - /// Downloads an image into the given directory and verifies its SHA256 checksum. - /// The output filename is derived from the image spec (see [`image_archive_filename`]). - /// - /// Skips download if the file already exists and matches the expected checksum. - /// Re-downloads on checksum mismatch. - /// - /// # Arguments - /// - /// * `spec` - Image spec (name and optional version) - /// * `output_dir` - Directory to write the `.tar.gz` file into (created if missing) - /// - /// # Returns - /// - /// * `Ok(PathBuf)` - Full path to the downloaded (or existing) image file - /// * `Err` - Image not found, download failed, or checksum verification failed - pub async fn download_image_to( - &self, - spec: ImageSpecRef<'_>, - output_dir: &Path, - ) -> Result { - let image = self.resolve_image(spec).ok_or_else(|| { - anyhow!( - "Image not found: {}{}. Use 'xtask image ls' to view available images", - spec.name, - spec.version - .map(|v| format!(" version {}", v)) - .unwrap_or_default() - ) - })?; - - fs::create_dir_all(output_dir) - .map_err(|e| anyhow!("Failed to create output directory: {e}"))?; - - let output_path = output_dir.join(image_archive_filename(spec)); - - if output_path.is_dir() { - return Err(anyhow!( - "Output path is a directory: {}", - output_path.display() - )); - } - - if output_path.exists() { - match image_verify_sha256(&output_path, &image.sha256) { - Ok(true) => { - println!("Image already exists and verified"); - return Ok(output_path); - } - Ok(false) => { - println!("Existing image verification failed"); - } - Err(e) => { - println!("Error verifying existing image: {e}"); - } - } - - println!("Removing existing image for re-downloading..."); - let _ = fs::remove_file(&output_path); - } - - println!("Downloading: {}", image.url); - - download_to_path(&image.url, &output_path, Some("Downloading")).await?; - - match image_verify_sha256(&output_path, &image.sha256) { - Ok(true) => { - println!("Download completed and verified successfully"); - Ok(output_path) - } - Ok(false) => { - let err = - anyhow!("Image downloaded but verification failed: SHA256 verification failed"); - println!("{err}"); - let _ = fs::remove_file(&output_path); - Err(err) - } - Err(e) => { - let err = - anyhow!("Image downloaded but verification failed: Error verifying image: {e}"); - println!("{err}"); - let _ = fs::remove_file(&output_path); - Err(err) - } - } - } - - /// Downloads an image to the default location in local storage. - /// - /// Equivalent to `download_image_to(spec, &self.path)`. - /// - /// # Returns - /// - /// * `Ok(PathBuf)` - Full path to the downloaded (or existing) image file - /// * `Err` - Same as [`download_image_to`](Self::download_image_to) - pub async fn download_image(&self, spec: ImageSpecRef<'_>) -> Result { - self.download_image_to(spec, &self.path).await - } - - /// Removes an image from local storage (archive and extracted directory). - /// - /// # Arguments - /// - /// * `spec` - Image spec (name and optional version) - /// - /// # Returns - /// - /// `true` if at least one file or directory was removed, `false` if none found - pub async fn remove_image(&self, spec: ImageSpecRef<'_>) -> Result { - let mut anything_removed = false; - let output_path = image_path(&self.path, spec); - if output_path.exists() { - fs::remove_file(&output_path)?; - anything_removed = true; - } - let extract_dir = self.path.join(image_extract_dir_name(spec)); - if extract_dir.exists() { - fs::remove_dir_all(&extract_dir)?; - anything_removed = true; - } - Ok(anything_removed) - } -} diff --git a/os/axvisor/xtask/src/main.rs b/os/axvisor/xtask/src/main.rs deleted file mode 100644 index 99d799799..000000000 --- a/os/axvisor/xtask/src/main.rs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Build tool for Axvisor hypervisor. -//! -//! This crate provides the `xtask` binary for building, running, and managing -//! the Axvisor hypervisor project. - -#![cfg_attr(not(any(windows, unix)), no_main)] -#![cfg_attr(not(any(windows, unix)), no_std)] -#![cfg(any(windows, unix))] - -use anyhow::{Context, Result, anyhow}; -use chrono::Utc; -use clap::{Args, Parser, Subcommand}; -use std::fs; -use std::path::{Path, PathBuf}; - -mod cargo; -mod clippy; -mod ctx; -mod devspace; -mod image; -mod menuconfig; -mod tbuld; -mod vmconfig; - -#[derive(Parser)] -#[command(name = "xtask")] -#[command(about = "ArceOS build configuration management tool")] -struct Cli { - #[command(subcommand)] - command: Commands, -} - -#[derive(Subcommand)] -enum Commands { - /// Set default build configuration from board configs - Defconfig { - /// Board configuration name (e.g., qemu-aarch64, orangepi-5-plus, phytiumpi) - board_name: String, - }, - /// Build the ArceOS project with current configuration - Build(BuildArgs), - /// Run clippy checks across all targets and feature combinations - Clippy(ClippyArgs), - /// Run ArceOS in QEMU emulation environment - Qemu(QemuArgs), - /// Run ArceOS with U-Boot bootloader - Uboot(UbootArgs), - /// Generate VM configuration schema - Vmconfig, - /// Interactive menu-based configuration editor - Menuconfig, - /// Guest Image management - Image(image::ImageArgs), - /// Manage local devspace dependencies - Devspace(DevspaceArgs), -} - -#[derive(Parser)] -struct QemuArgs { - /// Path to custom build configuration file (TOML format) - #[arg(long)] - build_config: Option, - - /// Path to custom QEMU configuration file - #[arg(long)] - qemu_config: Option, - - /// Comma-separated list of VM configuration files - #[arg(long)] - vmconfigs: Vec, - - #[command(flatten)] - build: BuildArgs, -} - -#[derive(Parser)] -struct ClippyArgs { - /// Only check specific packages (comma separated) - #[arg(long)] - packages: Option, - - /// Only check specific targets (comma separated) - #[arg(long)] - targets: Option, - - /// Continue on error instead of exiting immediately - #[arg(long)] - continue_on_error: bool, - - /// Dry run - show what would be checked without running clippy - #[arg(long)] - dry_run: bool, - - /// Automatically fix clippy warnings where possible - #[arg(long)] - fix: bool, - - /// Allow fixing when the working directory is dirty (has uncommitted changes) - #[arg(long)] - allow_dirty: bool, -} - -#[derive(Parser)] -struct UbootArgs { - /// Path to custom build configuration file (TOML format) - #[arg(long)] - build_config: Option, - - /// Path to custom U-Boot configuration file - #[arg(long)] - uboot_config: Option, - - /// Comma-separated list of VM configuration files - #[arg(long)] - vmconfigs: Vec, - - #[command(flatten)] - build: BuildArgs, -} - -#[derive(Args)] -struct BuildArgs { - #[arg(long)] - build_dir: Option, - #[arg(long)] - bin_dir: Option, -} - -#[derive(Args)] -struct DevspaceArgs { - #[command(subcommand)] - action: DevspaceCommand, -} - -#[derive(Subcommand)] -enum DevspaceCommand { - /// Start the development workspace - Start, - /// Stop the development workspace - Stop, -} - -#[tokio::main] -async fn main() -> Result<()> { - let cli = Cli::parse(); - - let mut ctx = ctx::Context::new(); - - match cli.command { - Commands::Defconfig { board_name } => { - defconfig_command(&board_name)?; - } - Commands::Build(args) => { - println!("Building the project..."); - ctx.apply_build_args(&args); - ctx.run_build().await?; - println!("Build completed successfully."); - } - Commands::Clippy(args) => { - clippy::run_clippy(args)?; - } - Commands::Qemu(args) => { - ctx.apply_build_args(&args.build); - ctx.vmconfigs = args.vmconfigs; - ctx.build_config_path = args.build_config; - ctx.run_qemu(args.qemu_config).await?; - } - Commands::Uboot(args) => { - ctx.apply_build_args(&args.build); - ctx.vmconfigs = args.vmconfigs; - ctx.build_config_path = args.build_config; - ctx.run_uboot(args.uboot_config).await?; - } - Commands::Vmconfig => { - ctx.run_vmconfig().await?; - } - Commands::Menuconfig => { - ctx.run_menuconfig().await?; - } - Commands::Image(args) => { - image::run_image(args).await?; - } - Commands::Devspace(args) => match args.action { - DevspaceCommand::Start => devspace::start()?, - DevspaceCommand::Stop => devspace::stop()?, - }, - } - - Ok(()) -} - -fn defconfig_command(board_name: &str) -> Result<()> { - println!("Setting default configuration for board: {board_name}"); - - // Validate board configuration exists - let board_config_path = format!("configs/board/{board_name}.toml"); - if !Path::new(&board_config_path).exists() { - return Err(anyhow!( - "Board configuration '{board_name}' not found. Available boards: qemu-aarch64, orangepi-5-plus" - )); - } - - // Backup existing .build.toml if it exists - backup_existing_config()?; - - // Copy board configuration to .build.toml - copy_board_config(board_name)?; - - println!("Successfully set default configuration to: {board_name}"); - Ok(()) -} - -fn backup_existing_config() -> Result<()> { - let build_config_path = ".build.toml"; - - if Path::new(build_config_path).exists() { - let timestamp = Utc::now().format("%Y%m%d_%H%M%S"); - let backup_path = format!("{build_config_path}.backup_{timestamp}"); - - fs::copy(build_config_path, &backup_path) - .with_context(|| format!("Failed to backup {build_config_path} to {backup_path}"))?; - - println!("Backed up existing configuration to: {backup_path}"); - } - - Ok(()) -} - -fn copy_board_config(board_name: &str) -> Result<()> { - let source_path = format!("configs/board/{board_name}.toml"); - let target_path = ".build.toml"; - - fs::copy(&source_path, target_path) - .with_context(|| format!("Failed to copy {source_path} to {target_path}"))?; - - println!("Copied board configuration from: {source_path}"); - Ok(()) -} diff --git a/os/axvisor/xtask/src/menuconfig.rs b/os/axvisor/xtask/src/menuconfig.rs deleted file mode 100644 index 0a6604edb..000000000 --- a/os/axvisor/xtask/src/menuconfig.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::ctx::Context; -use crate::tbuld::Config; -use jkconfig::{ElemHock, ui::components::editors::show_feature_select}; -use std::sync::Arc; // HashMap is unused - -impl Context { - /// Main menuconfig runner function - pub async fn run_menuconfig(&mut self) -> anyhow::Result<()> { - println!("Configure runtime parameters"); - - let config_path = self.ctx.paths.workspace.join(".build.toml"); - if config_path.exists() { - println!( - "\nCurrent .build.toml configuration file: {}", - config_path.display() - ); - } else { - println!("\nNo .build.toml configuration file found, will use default configuration"); - } - - let Some(_c): Option = - jkconfig::run(config_path, true, &[self.default_package_feature_select()]).await? - else { - return Err(anyhow::anyhow!("Menuconfig was cancelled")); - }; - Ok(()) - } - - pub fn default_package_feature_select(&self) -> ElemHock { - let path = "features"; - let package_name = "axvisor".to_string(); - - let cargo_toml = self.ctx.paths.workspace.join("Cargo.toml"); - ElemHock { - path: path.to_string(), - callback: Arc::new(move |siv: &mut jkconfig::cursive::Cursive, _path: &str| { - show_feature_select(siv, &package_name, &cargo_toml, None); - }), - } - } -} diff --git a/os/axvisor/xtask/src/tbuld.rs b/os/axvisor/xtask/src/tbuld.rs deleted file mode 100644 index ef6d26033..000000000 --- a/os/axvisor/xtask/src/tbuld.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::path::PathBuf; - -use anyhow::Context as _; -use ostool::build::config::{Cargo, LogLevel}; -use schemars::{JsonSchema, schema_for}; -use serde::{Deserialize, Serialize}; - -use crate::ctx::Context; - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] -pub struct Config { - /// target triple - pub target: String, - /// features to enable - pub features: Vec, - /// log level feature - pub log: Option, - /// other cargo args - pub cargo_args: Vec, - /// whether to output as binary - pub to_bin: bool, - pub smp: Option, - pub vm_configs: Vec, -} - -impl Context { - pub fn load_config(&mut self) -> anyhow::Result { - let json = schema_for!(Config); - - let mut config_path = self.ctx.paths.workspace.join(".build.toml"); - if let Some(c) = &self.build_config_path { - config_path = c.clone(); - } - - let path = config_path.parent().unwrap().join(".build-schema.json"); - - std::fs::write(&path, serde_json::to_string_pretty(&json).unwrap()) - .with_context(|| format!("Failed to write schema file: {}", path.display()))?; - - let config_str = std::fs::read_to_string(&config_path) - .with_context(|| format!("Failed to read config file: {}", config_path.display()))?; - let config: Config = toml::from_str(&config_str) - .with_context(|| format!("Failed to parse config file: {}", config_path.display()))?; - - self.ctx.build_config_path = Some(config_path); - - let mut vm_configs = config.vm_configs.to_vec(); - vm_configs.extend(self.vmconfigs.iter().cloned()); - - let mut vm_config_paths = vec![]; - for vm_config in &vm_configs { - let mut vm_config = PathBuf::from(vm_config); - if !vm_config.is_absolute() { - vm_config = self.ctx.paths.workspace.join(vm_config); - } - if !vm_config.exists() { - return Err(anyhow::anyhow!( - "VM config file '{}' does not exist.", - vm_config.display() - )); - } - vm_config_paths.push(vm_config); - } - - let log_level = config - .log - .as_ref() - .map(|l| format!("{:?}", l).to_lowercase()); - - let mut cargo = Cargo { - target: config.target, - package: "axvisor".to_string(), - features: config.features, - log: config.log, - args: vec!["--bin".to_string(), "axvisor".to_string()], - to_bin: config.to_bin, - ..Default::default() - }; - if cargo.target == "aarch64-unknown-none-softfloat" - && cargo.features.iter().any(|feature| feature == "dyn-plat") - { - // Dynamic-platform AArch64 builds link as PIE, so core/alloc must be - // rebuilt with the same PIC settings instead of using prebuilt std. - ensure_cargo_arg_pair(&mut cargo.args, "-Z", "build-std=core,alloc"); - ensure_cargo_arg_pair( - &mut cargo.args, - "-Z", - "build-std-features=compiler-builtins-mem", - ); - ensure_cargo_arg_pair( - &mut cargo.args, - "--config", - &format!( - "target.{}.rustflags=[\"-Clink-arg=-Taxplat.x\"]", - cargo.target - ), - ); - } - cargo.args.extend(config.cargo_args); - - if let Some(smp) = config.smp { - cargo.env.insert("AXVISOR_SMP".to_string(), smp.to_string()); - } - - if let Some(log_level) = log_level { - cargo.env.insert("AX_LOG".to_string(), log_level); - } - - if !vm_config_paths.is_empty() { - let value = std::env::join_paths(&vm_config_paths) - .map_err(|e| anyhow::anyhow!("Failed to join VM config paths: {e}"))? - .to_string_lossy() - .into_owned(); - cargo.env.insert("AXVISOR_VM_CONFIGS".to_string(), value); - } - - Ok(cargo) - } - - pub async fn run_build(&mut self) -> anyhow::Result<()> { - let config = self.load_config()?; - self.ctx.cargo_build(&config).await?; - - Ok(()) - } -} - -fn ensure_cargo_arg_pair(args: &mut Vec, flag: &str, value: &str) { - if args - .windows(2) - .any(|window| window[0] == flag && window[1] == value) - { - return; - } - args.push(flag.to_string()); - args.push(value.to_string()); -} diff --git a/os/axvisor/xtask/src/vmconfig.rs b/os/axvisor/xtask/src/vmconfig.rs deleted file mode 100644 index 5732d07bc..000000000 --- a/os/axvisor/xtask/src/vmconfig.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use anyhow::Context as _; - -use crate::ctx::Context; - -impl Context { - pub async fn run_vmconfig(&mut self) -> anyhow::Result<()> { - let json = schemars::schema_for!(axvmconfig::AxVMCrateConfig); - std::fs::write( - ".vmconfig-schema.json", - serde_json::to_string_pretty(&json).unwrap(), - ) - .with_context(|| "Failed to write schema file .vmconfig-schema.json")?; - Ok(()) - } -} From 165b14f4797f1c0caece002446b391c7d7f54816 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 20 Mar 2026 22:51:07 +0800 Subject: [PATCH 126/132] fixes for axvisor merge --- Cargo.lock | 461 ++++-------------- Cargo.toml | 2 +- components/axaddrspace/tests/address_space.rs | 3 +- components/axaddrspace/tests/frame.rs | 1 + .../axaddrspace/tests/test_utils/mod.rs | 2 +- components/axdevice_base/tests/test.rs | 4 +- components/axvm/src/vm.rs | 82 +--- components/riscv_vcpu/src/percpu.rs | 1 - components/riscv_vplic/tests/vplic_tests.rs | 2 +- os/axvisor/.cargo/config.toml | 2 +- os/axvisor/Cargo.toml | 14 +- os/axvisor/src/hal/arch/aarch64/api.rs | 4 +- os/axvisor/src/hal/impl_host.rs | 2 +- os/axvisor/src/hal/impl_vmm.rs | 1 - os/axvisor/src/hal/mod.rs | 7 +- platform/x86-qemu-q35/src/apic.rs | 6 +- platform/x86-qemu-q35/src/boot.rs | 6 +- platform/x86-qemu-q35/src/mp.rs | 6 +- platform/x86-qemu-q35/src/time.rs | 3 +- 19 files changed, 143 insertions(+), 466 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc4c47c69..441d592d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,12 +34,6 @@ dependencies = [ name = "aarch64_sysreg" version = "0.1.1" -[[package]] -name = "aarch64_sysreg" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2c929f5025d9b8a0f549b187c4d3a39671f44015ff6ccddd0b134c874b3c1a" - [[package]] name = "acpi" version = "6.1.1" @@ -213,7 +207,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -224,7 +218,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -435,31 +429,13 @@ dependencies = [ [[package]] name = "arm_vcpu" -version = "0.2.2" -dependencies = [ - "aarch64-cpu 10.0.0", - "axaddrspace 0.1.5", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.1.2", - "axvcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axvisor_api", - "log", - "numeric-enum-macro", - "percpu", - "spin 0.10.0", -] - -[[package]] -name = "arm_vcpu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8581cf4d84a33f95aa43d39c0a25cabeeaddd65c97a790a0830e37da6e5d871" +version = "0.3.0" dependencies = [ - "aarch64-cpu 10.0.0", - "axaddrspace 0.1.5", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.1.2", - "axvcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aarch64-cpu 11.2.0", + "axaddrspace", + "axdevice_base", + "axerrno 0.2.2", + "axvcpu", "axvisor_api", "log", "numeric-enum-macro", @@ -469,37 +445,18 @@ dependencies = [ [[package]] name = "arm_vgic" -version = "0.2.1" -dependencies = [ - "aarch64-cpu 10.0.0", - "aarch64_sysreg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axaddrspace 0.1.5", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.1.2", - "axvisor_api", - "bitmaps", - "log", - "memory_addr", - "spin 0.9.8", - "tock-registers 0.10.1", -] - -[[package]] -name = "arm_vgic" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e2c4d90852cad20bbe1e5ee6e6d1b05468b98787a8344ffea58537eb54f375" +version = "0.2.2" dependencies = [ - "aarch64-cpu 10.0.0", - "aarch64_sysreg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axaddrspace 0.1.5", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.1.2", + "aarch64-cpu 11.2.0", + "aarch64_sysreg", + "axaddrspace", + "axdevice_base", + "axerrno 0.2.2", "axvisor_api", "bitmaps", "log", "memory_addr", - "spin 0.9.8", + "spin 0.10.0", "tock-registers 0.10.1", ] @@ -587,26 +544,6 @@ dependencies = [ "buddy_system_allocator 0.12.0", ] -[[package]] -name = "axaddrspace" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91962ea80ef137c2986b6802d664900e73aa7a014272c13dd9172cc948d5c94e" -dependencies = [ - "axerrno 0.1.2", - "bit_field", - "bitflags 2.11.0", - "cfg-if", - "lazyinit 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log", - "memory_addr", - "memory_set", - "numeric-enum-macro", - "page_table_entry", - "page_table_multiarch", - "x86", -] - [[package]] name = "axaddrspace" version = "0.3.0" @@ -697,7 +634,7 @@ name = "axbuild" version = "0.3.0-preview.3" dependencies = [ "anyhow", - "axvmconfig 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axvmconfig", "cargo_metadata", "chrono", "clap", @@ -773,61 +710,28 @@ dependencies = [ [[package]] name = "axdevice" -version = "0.2.1" -dependencies = [ - "arm_vgic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axaddrspace 0.1.5", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.1.2", - "axvmconfig 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if", - "log", - "memory_addr", - "range-alloc-arceos 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "riscv_vplic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.9.8", -] - -[[package]] -name = "axdevice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473e2bedf3a04bede7ab6e05909d56f8aed538c56507ce172aaaf0d14dc3be36" +version = "0.2.2" dependencies = [ - "arm_vgic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axaddrspace 0.1.5", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.1.2", - "axvmconfig 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arm_vgic", + "axaddrspace", + "axdevice_base", + "axerrno 0.2.2", + "axvmconfig", "cfg-if", "log", "memory_addr", - "range-alloc-arceos 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "riscv_vplic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.9.8", -] - -[[package]] -name = "axdevice_base" -version = "0.2.1" -dependencies = [ - "axaddrspace 0.1.5", - "axerrno 0.1.2", - "axvmconfig 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory_addr", - "serde", + "range-alloc-arceos", + "riscv_vplic", + "spin 0.10.0", ] [[package]] name = "axdevice_base" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2707beb1e7bb6557406d77f3968a9ed7ec3d0476691022eafa550c266ec5c9" +version = "0.2.2" dependencies = [ - "axaddrspace 0.1.5", - "axerrno 0.1.2", - "axvmconfig 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memory_addr", + "axaddrspace", + "axerrno 0.2.2", + "axvmconfig", "serde", ] @@ -875,7 +779,7 @@ dependencies = [ "axhal", "axplat-dyn", "cfg-if", - "crate_interface 0.1.4", + "crate_interface 0.3.0", "log", "smallvec", ] @@ -1122,15 +1026,6 @@ dependencies = [ "axerrno 0.2.2", ] -[[package]] -name = "axhvc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6262e1b84fe6c912c07cc7d9dfa45d8f4511870d79a2886ce6217278bfb8c0dc" -dependencies = [ - "axerrno 0.2.2", -] - [[package]] name = "axin" version = "0.1.0" @@ -1184,17 +1079,6 @@ dependencies = [ "trait-ffi", ] -[[package]] -name = "axklib" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03bf328ee0dd583179ce7108584ec69da10e06bd4beb4d6acac7f6cb33754dab" -dependencies = [ - "axerrno 0.2.2", - "memory_addr", - "trait-ffi", -] - [[package]] name = "axlibc" version = "0.3.0-preview.3" @@ -1213,7 +1097,7 @@ dependencies = [ "axlog", "cfg-if", "chrono", - "crate_interface 0.1.4", + "crate_interface 0.3.0", "kspin", "log", ] @@ -1372,7 +1256,7 @@ dependencies = [ "axdriver_block", "axdriver_virtio", "axerrno 0.2.2", - "axklib 0.3.0", + "axklib", "axplat", "dma-api 0.7.1", "fdt-edit", @@ -1518,7 +1402,7 @@ dependencies = [ "axhal", "axinput", "axipi", - "axklib 0.3.0", + "axklib", "axlog", "axmm", "axnet", @@ -1527,7 +1411,7 @@ dependencies = [ "axtask", "cfg-if", "chrono", - "crate_interface 0.1.4", + "crate_interface 0.3.0", "ctor_bare", "indoc", "percpu", @@ -1587,7 +1471,7 @@ dependencies = [ "axtask", "cfg-if", "cpumask 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crate_interface 0.1.4", + "crate_interface 0.3.0", "event-listener", "extern-trait", "futures-util", @@ -1654,23 +1538,10 @@ dependencies = [ [[package]] name = "axvcpu" -version = "0.2.2" -dependencies = [ - "axaddrspace 0.1.5", - "axerrno 0.1.2", - "axvisor_api", - "memory_addr", - "percpu", -] - -[[package]] -name = "axvcpu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70880a87fffe8087719ad2e7aa09da482db88a65d123dc9170297b7e2a162fb5" +version = "0.3.0" dependencies = [ - "axaddrspace 0.1.5", - "axerrno 0.1.2", + "axaddrspace", + "axerrno 0.2.2", "axvisor_api", "memory_addr", "percpu", @@ -1683,20 +1554,20 @@ dependencies = [ "aarch64-cpu-ext", "anyhow", "arm-gic-driver 0.17.0", - "axaddrspace 0.1.5", - "axbuild", + "axaddrspace", "axconfig", - "axdevice 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "axdevice", + "axdevice_base", "axerrno 0.2.2", - "axhvc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "axklib 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "axhvc", + "axklib", + "axplat-riscv64-qemu-virt", "axplat-x86-qemu-q35", "axstd", - "axvcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axvcpu", "axvisor_api", - "axvm 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "axvmconfig 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axvm", + "axvmconfig", "bitflags 2.11.0", "byte-unit", "cargo_metadata", @@ -1705,7 +1576,7 @@ dependencies = [ "clap", "colored", "cpumask 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crate_interface 0.1.4", + "crate_interface 0.3.0", "extern-trait", "fdt-parser", "flate2", @@ -1732,7 +1603,6 @@ dependencies = [ "rdrive", "regex", "reqwest 0.13.2", - "rk3568_clk", "rk3588-clk", "rockchip-pm", "schemars", @@ -1740,7 +1610,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "spin 0.9.8", + "spin 0.10.0", "syn 2.0.117", "tar", "timer_list 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1750,21 +1620,18 @@ dependencies = [ [[package]] name = "axvisor_api" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7233b2a1338dc06a80e2779b572b4df02007ea128ef7b235b66fc3eeac0ca6" +version = "0.3.0" dependencies = [ - "axaddrspace 0.1.5", + "axaddrspace", "axvisor_api_proc", - "crate_interface 0.1.4", + "cpumask 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crate_interface 0.3.0", "memory_addr", ] [[package]] name = "axvisor_api_proc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a64eb4410ae8357ac8c01c2fb201e57d7aeeb5436ed4d0f5774bfa11cc5902" +version = "0.3.0" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1774,42 +1641,17 @@ dependencies = [ [[package]] name = "axvm" -version = "0.2.3" -dependencies = [ - "arm_vcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "arm_vgic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axaddrspace 0.1.5", - "axdevice 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.2.2", - "axvcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axvmconfig 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if", - "cpumask 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log", - "memory_addr", - "page_table_entry", - "page_table_multiarch", - "percpu", - "riscv_vcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.9.8", - "x86_vcpu", -] - -[[package]] -name = "axvm" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c13dc01a73107817fa04f8f2cad019eaee992541713d7064fd2da855502c6d" +version = "0.3.0" dependencies = [ - "arm_vcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "arm_vgic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axaddrspace 0.1.5", - "axdevice 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "arm_vcpu", + "arm_vgic", + "axaddrspace", + "axdevice", + "axdevice_base", "axerrno 0.2.2", - "axvcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axvmconfig 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axvcpu", + "axvisor_api", + "axvmconfig", "cfg-if", "cpumask 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log", @@ -1817,8 +1659,8 @@ dependencies = [ "page_table_entry", "page_table_multiarch", "percpu", - "riscv_vcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.9.8", + "riscv_vcpu", + "spin 0.10.0", "x86_vcpu", ] @@ -1837,23 +1679,6 @@ dependencies = [ "toml", ] -[[package]] -name = "axvmconfig" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35f2cf8bfcabc84e636f5cf3cd7b1c1ddcf1f584d6fa91791a572056204db35a" -dependencies = [ - "axerrno 0.2.2", - "clap", - "enumerable", - "env_logger 0.11.9", - "log", - "schemars", - "serde", - "serde_repr", - "toml", -] - [[package]] name = "bare-metal" version = "1.0.0" @@ -2327,7 +2152,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3278,7 +3103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4187,7 +4012,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5524,12 +5349,6 @@ checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" name = "range-alloc-arceos" version = "0.1.4" -[[package]] -name = "range-alloc-arceos" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187083342c080f6150a1e8e6e6a687b49239df195aaf866ff4ed535961ab860e" - [[package]] name = "ranges-ext" version = "0.6.1" @@ -5948,19 +5767,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b59d645e392e041ad18f5e529ed13242d8405c66bb192f59703ea2137017d0" -[[package]] -name = "riscv-h" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cffa652689d01c5f7033abe105e69f4d57ac85bf7e17da688bab10e4b9d3a2d8" -dependencies = [ - "bare-metal", - "bit_field", - "bitflags 2.11.0", - "log", - "riscv 0.14.0", -] - [[package]] name = "riscv-h" version = "0.2.0" @@ -5972,19 +5778,6 @@ dependencies = [ "riscv 0.14.0", ] -[[package]] -name = "riscv-h" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bf93901d1af87f984c7be7a348cd27571e1f6f29293b8f3b3193f4c40d4d40" -dependencies = [ - "bare-metal", - "bit_field", - "bitflags 2.11.0", - "log", - "riscv 0.14.0", -] - [[package]] name = "riscv-macros" version = "0.2.0" @@ -6043,97 +5836,41 @@ dependencies = [ [[package]] name = "riscv_vcpu" -version = "0.2.2" -dependencies = [ - "axaddrspace 0.1.5", - "axerrno 0.1.2", - "axvcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "axvisor_api", - "bit_field", - "bitflags 2.11.0", - "cfg-if", - "crate_interface 0.1.4", - "log", - "memoffset", - "memory_addr", - "page_table_entry", - "riscv 0.14.0", - "riscv-decode", - "riscv-h 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustsbi", - "sbi-rt", - "sbi-spec", - "tock-registers 0.9.0", -] - -[[package]] -name = "riscv_vcpu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6122b26f3d5920206f22b871fd13d79f5b23e570c7860f345d8736528dacc4c" +version = "0.3.0" dependencies = [ - "axaddrspace 0.1.5", - "axerrno 0.1.2", - "axvcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axaddrspace", + "axerrno 0.2.2", + "axvcpu", "axvisor_api", "bit_field", "bitflags 2.11.0", "cfg-if", - "crate_interface 0.1.4", + "crate_interface 0.3.0", "log", "memoffset", "memory_addr", "page_table_entry", "riscv 0.14.0", "riscv-decode", - "riscv-h 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "riscv-h", "rustsbi", "sbi-rt", "sbi-spec", - "tock-registers 0.9.0", -] - -[[package]] -name = "riscv_vplic" -version = "0.2.1" -dependencies = [ - "axaddrspace 0.1.5", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.1.2", - "axvisor_api", - "bitmaps", - "log", - "riscv-h 0.1.0", - "spin 0.9.8", + "tock-registers 0.10.1", ] [[package]] name = "riscv_vplic" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193b9d354fe0680a7e151b069929ab7e5722519b5623b24f2bf0211236742a5f" +version = "0.2.2" dependencies = [ - "axaddrspace 0.1.5", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.1.2", + "axaddrspace", + "axdevice_base", + "axerrno 0.2.2", "axvisor_api", "bitmaps", "log", - "riscv-h 0.1.0", - "spin 0.9.8", -] - -[[package]] -name = "rk3568_clk" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9786b01d79acb045652d4cb451b049b26a509171d4afe5c19767d06a366585" -dependencies = [ - "aarch64-cpu 10.0.0", - "bare-test-macros", - "fdt-parser", - "kspin", - "log", + "riscv-h", + "spin 0.10.0", ] [[package]] @@ -6309,7 +6046,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6368,7 +6105,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6845,7 +6582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -7341,7 +7078,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -8278,7 +8015,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -8764,26 +8501,24 @@ dependencies = [ [[package]] name = "x86_vcpu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b71adc527803929792ea1af4a2cd7c264c9cd3dd0096b03f9bc959505f73a" +version = "0.3.0" dependencies = [ - "axaddrspace 0.1.5", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.1.2", - "axvcpu 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "axaddrspace", + "axdevice_base", + "axerrno 0.2.2", + "axvcpu", "axvisor_api", "bit_field", "bitflags 2.11.0", "cfg-if", - "crate_interface 0.1.4", + "crate_interface 0.3.0", "log", "memory_addr", "numeric-enum-macro", "page_table_entry", "paste", "raw-cpuid 11.6.0", - "spin 0.9.8", + "spin 0.10.0", "x86", "x86_64", "x86_vlapic", @@ -8791,13 +8526,11 @@ dependencies = [ [[package]] name = "x86_vlapic" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809257bd2252fc337f3b494ae9230e4600bfa4e7d3f2478a0dd794e16849040d" +version = "0.2.2" dependencies = [ - "axaddrspace 0.1.5", - "axdevice_base 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "axerrno 0.1.2", + "axaddrspace", + "axdevice_base", + "axerrno 0.2.2", "axvisor_api", "bit", "log", diff --git a/Cargo.toml b/Cargo.toml index ac5fe926f..a53df4080 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -231,7 +231,7 @@ axbuild = { path = "scripts/axbuild", version = "0.3.0-preview.3" } # === axvisor === axvm = { path = "components/axvm", version = "0.3" } -axvmconfig = { path = "components/axvmconfig", version = "0.3" } +axvmconfig = { path = "components/axvmconfig", version = "0.2.2" } axvcpu = { path = "components/axvcpu", version = "0.3" } x86_vcpu = { path = "components/x86_vcpu", version = "0.3" } x86_vlapic = { path = "components/x86_vlapic", version = "0.2.2" } diff --git a/components/axaddrspace/tests/address_space.rs b/components/axaddrspace/tests/address_space.rs index e78285ca2..80b8e125f 100644 --- a/components/axaddrspace/tests/address_space.rs +++ b/components/axaddrspace/tests/address_space.rs @@ -14,9 +14,10 @@ mod test_utils; +use core::sync::atomic::Ordering; + use axaddrspace::{AddrSpace, GuestPhysAddr, MappingFlags}; use axin::axin; -use core::sync::atomic::Ordering; use memory_addr::PhysAddr; use test_utils::{ ALLOC_COUNT, BASE_PADDR, DEALLOC_COUNT, MEMORY_LEN, MockHal, mock_hal_test, test_dealloc_count, diff --git a/components/axaddrspace/tests/frame.rs b/components/axaddrspace/tests/frame.rs index eab6bd228..5a8b28a44 100644 --- a/components/axaddrspace/tests/frame.rs +++ b/components/axaddrspace/tests/frame.rs @@ -17,6 +17,7 @@ extern crate alloc; mod test_utils; use alloc::vec::Vec; + use assert_matches::assert_matches; use axaddrspace::PhysFrame; use axin::axin; diff --git a/components/axaddrspace/tests/test_utils/mod.rs b/components/axaddrspace/tests/test_utils/mod.rs index bd926e969..d94b7b8a2 100644 --- a/components/axaddrspace/tests/test_utils/mod.rs +++ b/components/axaddrspace/tests/test_utils/mod.rs @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; use lazy_static::lazy_static; use memory_addr::{PAGE_SIZE_4K as PAGE_SIZE, PhysAddr, VirtAddr}; use page_table_multiarch::PagingHandler; diff --git a/components/axdevice_base/tests/test.rs b/components/axdevice_base/tests/test.rs index 54e31f76d..a4357122a 100644 --- a/components/axdevice_base/tests/test.rs +++ b/components/axdevice_base/tests/test.rs @@ -14,8 +14,8 @@ extern crate alloc; -use alloc::vec; -use alloc::{sync::Arc, vec::Vec}; +use alloc::{sync::Arc, vec, vec::Vec}; + use axaddrspace::{GuestPhysAddr, GuestPhysAddrRange, device::AccessWidth}; use axdevice_base::{BaseDeviceOps, EmuDeviceType, map_device_of_type}; use axerrno::AxResult; diff --git a/components/axvm/src/vm.rs b/components/axvm/src/vm.rs index faeb53223..6eda0795f 100644 --- a/components/axvm/src/vm.rs +++ b/components/axvm/src/vm.rs @@ -12,38 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -use alloc::boxed::Box; -use alloc::format; -use alloc::sync::Arc; -use alloc::vec::Vec; -use axaddrspace::HostVirtAddr; -use axerrno::{AxError, AxResult, ax_err, ax_err_type}; -use axvisor_api::vmm::InterruptVector; -use core::alloc::Layout; -use core::fmt; -use memory_addr::{align_down_4k, align_up_4k}; -use spin::{Mutex, Once}; +use alloc::{boxed::Box, format, sync::Arc, vec::Vec}; +use core::{alloc::Layout, fmt}; -use axaddrspace::{AddrSpace, GuestPhysAddr, HostPhysAddr, MappingFlags, device::AccessWidth}; +use axaddrspace::{ + AddrSpace, GuestPhysAddr, HostPhysAddr, HostVirtAddr, MappingFlags, device::AccessWidth, +}; use axdevice::{AxVmDeviceConfig, AxVmDevices}; +use axerrno::{AxError, AxResult, ax_err, ax_err_type}; use axvcpu::{AxVCpu, AxVCpuExitReason}; +use axvisor_api::vmm::InterruptVector; use cpumask::CpuMask; use memory_addr::{align_down_4k, align_up_4k}; use spin::{Mutex, Once}; -use crate::config::{AxVMConfig, PhysCpuList}; -use crate::hal::PagingHandlerImpl; -use crate::has_hardware_support; -use crate::vcpu::AxArchVCpuImpl; - #[cfg(not(target_arch = "x86_64"))] use crate::vcpu::AxVCpuCreateConfig; - #[cfg(target_arch = "aarch64")] use crate::vcpu::{AxVCpuCreateConfig, get_sysreg_device}; use crate::{ - AxVMHal, config::{AxVMConfig, PhysCpuList}, + hal::PagingHandlerImpl, has_hardware_support, vcpu::AxArchVCpuImpl, }; @@ -149,55 +138,6 @@ impl fmt::Display for VMStatus { } } -/// VM status enumeration representing the lifecycle states of a virtual machine -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum VMStatus { - /// VM is being created/loaded - Loading, - /// VM is loaded but not yet started - Loaded, - /// VM is currently running - Running, - /// VM is suspended (paused but can be resumed) - Suspended, - /// VM is in the process of shutting down - Stopping, - /// VM is stopped - Stopped, -} - -impl VMStatus { - /// Get status as a string (lowercase) - pub fn as_str(&self) -> &'static str { - match self { - VMStatus::Loading => "loading", - VMStatus::Loaded => "loaded", - VMStatus::Running => "running", - VMStatus::Suspended => "suspended", - VMStatus::Stopping => "stopping", - VMStatus::Stopped => "stopped", - } - } - - /// Get status with emoji icon - pub fn as_str_with_icon(&self) -> &'static str { - match self { - VMStatus::Loading => "🔄 loading", - VMStatus::Loaded => "📦 loaded", - VMStatus::Running => "🚀 running", - VMStatus::Suspended => "🛑 suspended", - VMStatus::Stopping => "⏹️ stopping", - VMStatus::Stopped => "💤 stopped", - } - } -} - -impl fmt::Display for VMStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - const TEMP_MAX_VCPU_NUM: usize = 64; /// A Virtual Machine. @@ -876,7 +816,8 @@ impl AxVM { let current_status = self.vm_status(); if !matches!(current_status, VMStatus::Stopping | VMStatus::Stopped) { warn!( - "VM[{}] is being dropped without explicit shutdown (status: {:?}), marking as stopping", + "VM[{}] is being dropped without explicit shutdown (status: {:?}), marking as \ + stopping", self.id(), current_status ); @@ -924,7 +865,8 @@ impl AxVM { } } else { debug!( - "VM[{}] skipping dealloc for reserved memory region: GPA={:#x}, HVA={:#x}, size={:#x}", + "VM[{}] skipping dealloc for reserved memory region: GPA={:#x}, HVA={:#x}, \ + size={:#x}", self.id(), region.gpa.as_usize(), region.hva.as_usize(), diff --git a/components/riscv_vcpu/src/percpu.rs b/components/riscv_vcpu/src/percpu.rs index cebed0729..ad56b3f05 100644 --- a/components/riscv_vcpu/src/percpu.rs +++ b/components/riscv_vcpu/src/percpu.rs @@ -14,7 +14,6 @@ use axerrno::{AxError, AxResult}; use axvcpu::AxArchPerCpu; - use riscv::register::sie; use riscv_h::register::{hedeleg, hideleg, hvip}; diff --git a/components/riscv_vplic/tests/vplic_tests.rs b/components/riscv_vplic/tests/vplic_tests.rs index 515f37c65..0aa9b03c0 100644 --- a/components/riscv_vplic/tests/vplic_tests.rs +++ b/components/riscv_vplic/tests/vplic_tests.rs @@ -1,6 +1,6 @@ use axaddrspace::GuestPhysAddr; use riscv_vplic::{ - VPlicGlobal, PLIC_CONTEXT_CLAIM_COMPLETE_OFFSET, PLIC_CONTEXT_CTRL_OFFSET, PLIC_CONTEXT_STRIDE, + PLIC_CONTEXT_CLAIM_COMPLETE_OFFSET, PLIC_CONTEXT_CTRL_OFFSET, PLIC_CONTEXT_STRIDE, VPlicGlobal, }; /// Calculate minimum required size for VPlicGlobal with given contexts diff --git a/os/axvisor/.cargo/config.toml b/os/axvisor/.cargo/config.toml index d20ffe7fa..8ebf9f457 100644 --- a/os/axvisor/.cargo/config.toml +++ b/os/axvisor/.cargo/config.toml @@ -25,7 +25,7 @@ runner = "cargo osrun" rustflags = [ "-Clink-args=-no-pie", "-Clink-args=-znostart-stop-gc", - "-Clink-args=-Tlink.x", + "-Clink-args=-Tlinker.x", ] [alias] diff --git a/os/axvisor/Cargo.toml b/os/axvisor/Cargo.toml index 58944e6bf..4e1a8dfa3 100644 --- a/os/axvisor/Cargo.toml +++ b/os/axvisor/Cargo.toml @@ -118,19 +118,12 @@ rdrive = "0.20" rd-block = "0.1" pcie = "0.5.0" -# platform -axplat-riscv64-qemu-virt = {path = "platform/riscv64-qemu-virt"} - # Optional driver dependencies sdmmc = { version = "0.1", default-features = false, features = ["pio"], optional = true } rk3588-clk = { version = "0.1", optional = true } rockchip-pm = { version = "0.4", optional = true } phytium-mci = { version = "0.1", default-features = false, features = ["pio"], optional = true } -[target.'cfg(target_arch = "aarch64")'.dependencies] -aarch64-cpu-ext = "0.1" -arm-gic-driver = {version = "0.17", features = ["rdif"]} - [target.'cfg(target_arch = "x86_64")'.dependencies] axplat-x86-qemu-q35 = { version = "0.2.0", default-features = false, features = [ "reboot-on-system-off", @@ -139,6 +132,13 @@ axplat-x86-qemu-q35 = { version = "0.2.0", default-features = false, features = ] } axconfig = { version = "=0.3.0-preview.3", features = ["plat-dyn"] } +[target.'cfg(target_arch = "aarch64")'.dependencies] +aarch64-cpu-ext = "0.1" +arm-gic-driver = {version = "0.17", features = ["rdif"]} + +[target.'cfg(target_arch = "riscv64")'.dependencies] +axplat-riscv64-qemu-virt = "0.3.1-pre.6" + # xtask dependencies (only used when xtask feature is enabled) [target.'cfg(any(windows, unix))'.dependencies] anyhow = { version = "1.0", optional = true } diff --git a/os/axvisor/src/hal/arch/aarch64/api.rs b/os/axvisor/src/hal/arch/aarch64/api.rs index 205fd29b0..32ca2441d 100644 --- a/os/axvisor/src/hal/arch/aarch64/api.rs +++ b/os/axvisor/src/hal/arch/aarch64/api.rs @@ -95,10 +95,10 @@ impl ArchIf for ArchImpl { .lock() .unwrap(); if let Some(gic) = gic.typed_mut::() { - return u32::from(gic.cpu_interface().ack()) as _ + return u32::from(gic.cpu_interface().ack()) as _; } if let Some(gic) = gic.typed_mut::() { - return gic.cpu_interface().ack1().to_u32() as _ + return gic.cpu_interface().ack1().to_u32() as _; } panic!("No GIC driver found"); } diff --git a/os/axvisor/src/hal/impl_host.rs b/os/axvisor/src/hal/impl_host.rs index 0643e3ad8..283aae9f5 100644 --- a/os/axvisor/src/hal/impl_host.rs +++ b/os/axvisor/src/hal/impl_host.rs @@ -6,6 +6,6 @@ struct HostImpl; impl HostIf for HostImpl { fn get_host_cpu_num() -> usize { // std::os::arceos::modules::axconfig::plat::CPU_NUM - axruntime::cpu_count() + std::os::arceos::modules::axhal::cpu_num() } } diff --git a/os/axvisor/src/hal/impl_vmm.rs b/os/axvisor/src/hal/impl_vmm.rs index c42462cd9..44717cc47 100644 --- a/os/axvisor/src/hal/impl_vmm.rs +++ b/os/axvisor/src/hal/impl_vmm.rs @@ -3,7 +3,6 @@ use std::os::arceos::modules::{axhal, axtask}; use axaddrspace::{HostPhysAddr, HostVirtAddr}; use axerrno::{AxResult, ax_err_type}; use axvisor_api::vmm::{InterruptVector, VCpuId, VCpuSet, VMId, VmmIf}; -use axvm::AxVMHal; use crate::{task::AsVCpuTask, vmm}; diff --git a/os/axvisor/src/hal/mod.rs b/os/axvisor/src/hal/mod.rs index 00bf71ff0..183c7c794 100644 --- a/os/axvisor/src/hal/mod.rs +++ b/os/axvisor/src/hal/mod.rs @@ -12,15 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::os::arceos::{ - self, - modules::{axhal::percpu::this_cpu_id}, -}; +use std::os::arceos::{self, modules::axhal::percpu::this_cpu_id}; -use page_table_multiarch::PagingHandler; use arceos::modules::axhal; use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; use axvm::AxVMPerCpu; +use page_table_multiarch::PagingHandler; #[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/mod.rs")] #[cfg_attr(target_arch = "x86_64", path = "arch/x86_64/mod.rs")] diff --git a/platform/x86-qemu-q35/src/apic.rs b/platform/x86-qemu-q35/src/apic.rs index ace4681f5..0bb543e4e 100644 --- a/platform/x86-qemu-q35/src/apic.rs +++ b/platform/x86-qemu-q35/src/apic.rs @@ -19,8 +19,10 @@ use core::mem::MaybeUninit; use axplat::mem::{PhysAddr, pa, phys_to_virt}; use kspin::SpinNoIrq; use lazyinit::LazyInit; -use x2apic::ioapic::IoApic; -use x2apic::lapic::{LocalApic, LocalApicBuilder, xapic_base}; +use x2apic::{ + ioapic::IoApic, + lapic::{LocalApic, LocalApicBuilder, xapic_base}, +}; use x86_64::instructions::port::Port; use self::vectors::*; diff --git a/platform/x86-qemu-q35/src/boot.rs b/platform/x86-qemu-q35/src/boot.rs index 9dca312fd..e2bb392b2 100644 --- a/platform/x86-qemu-q35/src/boot.rs +++ b/platform/x86-qemu-q35/src/boot.rs @@ -16,8 +16,10 @@ use core::arch::global_asm; -use x86_64::registers::control::{Cr0Flags, Cr4Flags}; -use x86_64::registers::model_specific::EferFlags; +use x86_64::registers::{ + control::{Cr0Flags, Cr4Flags}, + model_specific::EferFlags, +}; use crate::config::plat::{BOOT_STACK_SIZE, PHYS_VIRT_OFFSET}; diff --git a/platform/x86-qemu-q35/src/mp.rs b/platform/x86-qemu-q35/src/mp.rs index 4198f03dc..3777a84cf 100644 --- a/platform/x86-qemu-q35/src/mp.rs +++ b/platform/x86-qemu-q35/src/mp.rs @@ -14,8 +14,10 @@ //! Multi-processor booting. -use axplat::mem::{PAGE_SIZE_4K, PhysAddr, pa}; -use axplat::time::{Duration, busy_wait}; +use axplat::{ + mem::{PAGE_SIZE_4K, PhysAddr, pa}, + time::{Duration, busy_wait}, +}; const START_PAGE_IDX: u8 = 6; const START_PAGE_PADDR: PhysAddr = pa!(START_PAGE_IDX as usize * PAGE_SIZE_4K); diff --git a/platform/x86-qemu-q35/src/time.rs b/platform/x86-qemu-q35/src/time.rs index 8ac8ffeba..1450e6fac 100644 --- a/platform/x86-qemu-q35/src/time.rs +++ b/platform/x86-qemu-q35/src/time.rs @@ -17,10 +17,9 @@ //! Currently, the TSC is used as the clock source. use axplat::time::TimeIf; -use raw_cpuid::CpuId; - #[cfg(feature = "irq")] use int_ratio::Ratio; +use raw_cpuid::CpuId; #[cfg(feature = "irq")] const LAPIC_TICKS_PER_SEC: u64 = 1_000_000_000; // TODO: need to calibrate From fd419aae124c49b854d32ccdcddd3aba31832cb7 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 20 Mar 2026 23:43:48 +0800 Subject: [PATCH 127/132] fixes --- Cargo.toml | 14 +++++++------- src/hal/arch/aarch64/api.rs | 4 ++-- src/hal/impl_host.rs | 3 +-- src/hal/impl_vmm.rs | 1 - src/hal/mod.rs | 7 ++----- 5 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 58944e6bf..4e1a8dfa3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,19 +118,12 @@ rdrive = "0.20" rd-block = "0.1" pcie = "0.5.0" -# platform -axplat-riscv64-qemu-virt = {path = "platform/riscv64-qemu-virt"} - # Optional driver dependencies sdmmc = { version = "0.1", default-features = false, features = ["pio"], optional = true } rk3588-clk = { version = "0.1", optional = true } rockchip-pm = { version = "0.4", optional = true } phytium-mci = { version = "0.1", default-features = false, features = ["pio"], optional = true } -[target.'cfg(target_arch = "aarch64")'.dependencies] -aarch64-cpu-ext = "0.1" -arm-gic-driver = {version = "0.17", features = ["rdif"]} - [target.'cfg(target_arch = "x86_64")'.dependencies] axplat-x86-qemu-q35 = { version = "0.2.0", default-features = false, features = [ "reboot-on-system-off", @@ -139,6 +132,13 @@ axplat-x86-qemu-q35 = { version = "0.2.0", default-features = false, features = ] } axconfig = { version = "=0.3.0-preview.3", features = ["plat-dyn"] } +[target.'cfg(target_arch = "aarch64")'.dependencies] +aarch64-cpu-ext = "0.1" +arm-gic-driver = {version = "0.17", features = ["rdif"]} + +[target.'cfg(target_arch = "riscv64")'.dependencies] +axplat-riscv64-qemu-virt = "0.3.1-pre.6" + # xtask dependencies (only used when xtask feature is enabled) [target.'cfg(any(windows, unix))'.dependencies] anyhow = { version = "1.0", optional = true } diff --git a/src/hal/arch/aarch64/api.rs b/src/hal/arch/aarch64/api.rs index 205fd29b0..32ca2441d 100644 --- a/src/hal/arch/aarch64/api.rs +++ b/src/hal/arch/aarch64/api.rs @@ -95,10 +95,10 @@ impl ArchIf for ArchImpl { .lock() .unwrap(); if let Some(gic) = gic.typed_mut::() { - return u32::from(gic.cpu_interface().ack()) as _ + return u32::from(gic.cpu_interface().ack()) as _; } if let Some(gic) = gic.typed_mut::() { - return gic.cpu_interface().ack1().to_u32() as _ + return gic.cpu_interface().ack1().to_u32() as _; } panic!("No GIC driver found"); } diff --git a/src/hal/impl_host.rs b/src/hal/impl_host.rs index 0643e3ad8..831160f98 100644 --- a/src/hal/impl_host.rs +++ b/src/hal/impl_host.rs @@ -5,7 +5,6 @@ struct HostImpl; #[axvisor_api::api_impl] impl HostIf for HostImpl { fn get_host_cpu_num() -> usize { - // std::os::arceos::modules::axconfig::plat::CPU_NUM - axruntime::cpu_count() + std::os::arceos::modules::axhal::cpu_num() } } diff --git a/src/hal/impl_vmm.rs b/src/hal/impl_vmm.rs index c42462cd9..44717cc47 100644 --- a/src/hal/impl_vmm.rs +++ b/src/hal/impl_vmm.rs @@ -3,7 +3,6 @@ use std::os::arceos::modules::{axhal, axtask}; use axaddrspace::{HostPhysAddr, HostVirtAddr}; use axerrno::{AxResult, ax_err_type}; use axvisor_api::vmm::{InterruptVector, VCpuId, VCpuSet, VMId, VmmIf}; -use axvm::AxVMHal; use crate::{task::AsVCpuTask, vmm}; diff --git a/src/hal/mod.rs b/src/hal/mod.rs index 00bf71ff0..183c7c794 100644 --- a/src/hal/mod.rs +++ b/src/hal/mod.rs @@ -12,15 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::os::arceos::{ - self, - modules::{axhal::percpu::this_cpu_id}, -}; +use std::os::arceos::{self, modules::axhal::percpu::this_cpu_id}; -use page_table_multiarch::PagingHandler; use arceos::modules::axhal; use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; use axvm::AxVMPerCpu; +use page_table_multiarch::PagingHandler; #[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/mod.rs")] #[cfg_attr(target_arch = "x86_64", path = "arch/x86_64/mod.rs")] From 38e3c31d9947e195595012fd16ab0f046071cdb2 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Fri, 20 Mar 2026 23:46:18 +0800 Subject: [PATCH 128/132] Remove subtree os/axvisor before force re-add --- os/axvisor/.cargo/config.toml | 32 - os/axvisor/.github/config.json | 10 - os/axvisor/.github/workflows/check.yml | 67 - os/axvisor/.github/workflows/deploy.yml | 116 - os/axvisor/.github/workflows/push.yml | 147 - .../.github/workflows/qemu-aarch64.toml | 24 - .../.github/workflows/qemu-riscv64.toml | 24 - .../.github/workflows/qemu-x86_64-kvm.toml | 24 - os/axvisor/.github/workflows/qemu-x86_64.toml | 21 - os/axvisor/.github/workflows/release.yml | 165 - os/axvisor/.github/workflows/test-board.yml | 64 - os/axvisor/.github/workflows/test-qemu.yml | 122 - os/axvisor/.github/workflows/uboot.toml | 20 - os/axvisor/.gitignore | 68 - os/axvisor/.gitmodules | 0 os/axvisor/.rustfmt.toml | 1 - os/axvisor/Cargo.lock | 6916 -------------- os/axvisor/Cargo.toml | 170 - os/axvisor/LICENSE | 201 - os/axvisor/README.md | 116 - os/axvisor/README_CN.md | 116 - os/axvisor/build.rs | 308 - os/axvisor/configs/board/orangepi-5-plus.dtb | Bin 280066 -> 0 bytes os/axvisor/configs/board/orangepi-5-plus.toml | 13 - os/axvisor/configs/board/phytiumpi.toml | 13 - os/axvisor/configs/board/qemu-aarch64.toml | 10 - os/axvisor/configs/board/qemu-riscv64.toml | 12 - os/axvisor/configs/board/qemu-x86_64.toml | 10 - os/axvisor/configs/board/roc-rk3568-pc.toml | 13 - os/axvisor/configs/defconfig.toml | 11 - .../configs/vms/arceos-aarch64-e2000-smp1.dts | 155 - .../vms/arceos-aarch64-e2000-smp1.toml | 61 - .../configs/vms/arceos-aarch64-e2000-smp2.dts | 155 - .../vms/arceos-aarch64-e2000-smp2.toml | 60 - .../configs/vms/arceos-aarch64-qemu-smp1.toml | 75 - .../vms/arceos-aarch64-rk3568-smp1.dts | 87 - .../vms/arceos-aarch64-rk3568-smp1.toml | 63 - .../vms/arceos-aarch64-rk3568-smp2.dts | 101 - .../vms/arceos-aarch64-rk3568-smp2.toml | 63 - .../vms/arceos-aarch64-tac_e400-smp1.dts | 155 - .../vms/arceos-aarch64-tac_e400-smp1.toml | 61 - .../configs/vms/arceos-riscv64-qemu-smp1.toml | 72 - .../vms/linux-aarch64-a1000-smp8-fada.dts | 3385 ------- .../vms/linux-aarch64-a1000-smp8-fadb.dts | 3378 ------- .../configs/vms/linux-aarch64-a1000-smp8.toml | 64 - .../configs/vms/linux-aarch64-e2000-smp1.dts | 1302 --- .../configs/vms/linux-aarch64-e2000-smp1.toml | 62 - .../configs/vms/linux-aarch64-e2000-smp2.dts | 1302 --- .../configs/vms/linux-aarch64-e2000-smp2.toml | 63 - .../configs/vms/linux-aarch64-qemu-smp1.dts | 397 - .../configs/vms/linux-aarch64-qemu-smp1.toml | 70 - .../configs/vms/linux-aarch64-rk3568-smp1.dts | 6108 ------------- .../vms/linux-aarch64-rk3568-smp1.toml | 62 - .../configs/vms/linux-aarch64-rk3568-smp2.dts | 6108 ------------- .../vms/linux-aarch64-rk3568-smp2.toml | 62 - .../configs/vms/linux-aarch64-rk3588-smp8.dts | 8099 ----------------- .../vms/linux-aarch64-rk3588-smp8.toml | 122 - .../vms/linux-aarch64-tac_e400-smp1.dts | 1302 --- .../vms/linux-aarch64-tac_e400-smp1.toml | 62 - .../configs/vms/linux-riscv64-qemu-smp1.dts | 132 - .../configs/vms/linux-riscv64-qemu-smp1.toml | 67 - .../configs/vms/nimbos-aarch64-qemu-smp1.toml | 82 - .../configs/vms/nimbos-riscv64-qemu-smp1.toml | 59 - .../configs/vms/nimbos-x86_64-qemu-smp1.toml | 80 - .../vms/rtthread-aarch64-e2000-smp1.toml | 63 - os/axvisor/doc/FDT_Configuration_Guide.md | 274 - os/axvisor/doc/qemu-quickstart.md | 151 - os/axvisor/doc/qemu-quickstart_cn.md | 151 - os/axvisor/doc/shell.md | 1104 --- os/axvisor/doc/task.py-usage.md | 271 - .../platform/riscv64-qemu-virt/Cargo.toml | 33 - .../platform/riscv64-qemu-virt/axconfig.toml | 105 - .../platform/riscv64-qemu-virt/build.rs | 23 - .../platform/riscv64-qemu-virt/linker.lds.S | 99 - .../platform/riscv64-qemu-virt/src/boot.rs | 98 - .../platform/riscv64-qemu-virt/src/console.rs | 54 - .../platform/riscv64-qemu-virt/src/init.rs | 40 - .../platform/riscv64-qemu-virt/src/irq.rs | 264 - .../platform/riscv64-qemu-virt/src/lib.rs | 39 - .../platform/riscv64-qemu-virt/src/mem.rs | 58 - .../platform/riscv64-qemu-virt/src/power.rs | 32 - .../platform/riscv64-qemu-virt/src/time.rs | 71 - os/axvisor/rust-toolchain.toml | 5 - os/axvisor/scripts/ci_run_qemu_nimbos.py | 94 - os/axvisor/scripts/lds/linker.lds.S | 100 - os/axvisor/scripts/ostool/qemu-aarch64.toml | 21 - os/axvisor/scripts/ostool/qemu-riscv64.toml | 23 - os/axvisor/scripts/ostool/qemu-x86_64.toml | 21 - os/axvisor/scripts/quick-start.sh | 904 -- os/axvisor/scripts/setup_qemu.sh | 205 - os/axvisor/src/driver/blk/mod.rs | 104 - os/axvisor/src/driver/blk/phytium.rs | 314 - os/axvisor/src/driver/blk/rockchip.rs | 269 - os/axvisor/src/driver/mod.rs | 32 - os/axvisor/src/driver/serial/mod.rs | 68 - os/axvisor/src/driver/soc/mod.rs | 15 - .../src/driver/soc/rockchip/clk/rk3568-clk.rs | 137 - .../src/driver/soc/rockchip/clk/rk3588-clk.rs | 97 - os/axvisor/src/driver/soc/rockchip/mod.rs | 21 - os/axvisor/src/driver/soc/rockchip/pm.rs | 52 - os/axvisor/src/hal/arch/aarch64/api.rs | 109 - os/axvisor/src/hal/arch/aarch64/cache.rs | 31 - os/axvisor/src/hal/arch/aarch64/mod.rs | 159 - os/axvisor/src/hal/arch/riscv64/api.rs | 8 - os/axvisor/src/hal/arch/riscv64/cache.rs | 5 - os/axvisor/src/hal/arch/riscv64/mod.rs | 31 - os/axvisor/src/hal/arch/x86_64/cache.rs | 19 - os/axvisor/src/hal/arch/x86_64/mod.rs | 18 - os/axvisor/src/hal/impl_host.rs | 11 - os/axvisor/src/hal/impl_memory.rs | 53 - os/axvisor/src/hal/impl_time.rs | 33 - os/axvisor/src/hal/impl_vmm.rs | 74 - os/axvisor/src/hal/mod.rs | 135 - os/axvisor/src/logo.rs | 51 - os/axvisor/src/main.rs | 55 - os/axvisor/src/shell/command/base.rs | 796 -- os/axvisor/src/shell/command/history.rs | 82 - os/axvisor/src/shell/command/mod.rs | 580 -- os/axvisor/src/shell/command/vm.rs | 1409 --- os/axvisor/src/shell/mod.rs | 239 - os/axvisor/src/task.rs | 57 - os/axvisor/src/vmm/config.rs | 271 - os/axvisor/src/vmm/fdt/create.rs | 483 - os/axvisor/src/vmm/fdt/device.rs | 522 -- os/axvisor/src/vmm/fdt/mod.rs | 137 - os/axvisor/src/vmm/fdt/parser.rs | 411 - os/axvisor/src/vmm/fdt/print.rs | 149 - os/axvisor/src/vmm/fdt/vm_fdt/mod.rs | 14 - os/axvisor/src/vmm/fdt/vm_fdt/writer.rs | 553 -- os/axvisor/src/vmm/hvc.rs | 162 - os/axvisor/src/vmm/images/linux.rs | 162 - os/axvisor/src/vmm/images/mod.rs | 329 - os/axvisor/src/vmm/ivc.rs | 299 - os/axvisor/src/vmm/mod.rs | 152 - os/axvisor/src/vmm/timer.rs | 127 - os/axvisor/src/vmm/vcpus.rs | 603 -- os/axvisor/src/vmm/vm_list.rs | 127 - os/axvisor/xtask/src/cargo.rs | 84 - os/axvisor/xtask/src/clippy.rs | 424 - os/axvisor/xtask/src/ctx.rs | 49 - os/axvisor/xtask/src/devspace.rs | 501 - os/axvisor/xtask/src/image.rs | 318 - os/axvisor/xtask/src/image/config.rs | 159 - os/axvisor/xtask/src/image/download.rs | 134 - os/axvisor/xtask/src/image/registry.rs | 243 - os/axvisor/xtask/src/image/spec.rs | 168 - os/axvisor/xtask/src/image/storage.rs | 426 - os/axvisor/xtask/src/main.rs | 252 - os/axvisor/xtask/src/menuconfig.rs | 55 - os/axvisor/xtask/src/tbuld.rs | 121 - os/axvisor/xtask/src/vmconfig.rs | 29 - 151 files changed, 59121 deletions(-) delete mode 100644 os/axvisor/.cargo/config.toml delete mode 100644 os/axvisor/.github/config.json delete mode 100644 os/axvisor/.github/workflows/check.yml delete mode 100644 os/axvisor/.github/workflows/deploy.yml delete mode 100644 os/axvisor/.github/workflows/push.yml delete mode 100644 os/axvisor/.github/workflows/qemu-aarch64.toml delete mode 100644 os/axvisor/.github/workflows/qemu-riscv64.toml delete mode 100644 os/axvisor/.github/workflows/qemu-x86_64-kvm.toml delete mode 100644 os/axvisor/.github/workflows/qemu-x86_64.toml delete mode 100644 os/axvisor/.github/workflows/release.yml delete mode 100644 os/axvisor/.github/workflows/test-board.yml delete mode 100644 os/axvisor/.github/workflows/test-qemu.yml delete mode 100644 os/axvisor/.github/workflows/uboot.toml delete mode 100644 os/axvisor/.gitignore delete mode 100644 os/axvisor/.gitmodules delete mode 100644 os/axvisor/.rustfmt.toml delete mode 100644 os/axvisor/Cargo.lock delete mode 100644 os/axvisor/Cargo.toml delete mode 100644 os/axvisor/LICENSE delete mode 100644 os/axvisor/README.md delete mode 100644 os/axvisor/README_CN.md delete mode 100644 os/axvisor/build.rs delete mode 100644 os/axvisor/configs/board/orangepi-5-plus.dtb delete mode 100644 os/axvisor/configs/board/orangepi-5-plus.toml delete mode 100644 os/axvisor/configs/board/phytiumpi.toml delete mode 100644 os/axvisor/configs/board/qemu-aarch64.toml delete mode 100644 os/axvisor/configs/board/qemu-riscv64.toml delete mode 100644 os/axvisor/configs/board/qemu-x86_64.toml delete mode 100644 os/axvisor/configs/board/roc-rk3568-pc.toml delete mode 100644 os/axvisor/configs/defconfig.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-e2000-smp1.dts delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-e2000-smp1.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.dts delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.dts delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.dts delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.toml delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.dts delete mode 100644 os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.toml delete mode 100644 os/axvisor/configs/vms/arceos-riscv64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fada.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fadb.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-a1000-smp8.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-e2000-smp1.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-e2000-smp1.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-e2000-smp2.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-e2000-smp2.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-qemu-smp1.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.toml delete mode 100644 os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.dts delete mode 100644 os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.toml delete mode 100644 os/axvisor/configs/vms/linux-riscv64-qemu-smp1.dts delete mode 100644 os/axvisor/configs/vms/linux-riscv64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/nimbos-aarch64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/nimbos-riscv64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/nimbos-x86_64-qemu-smp1.toml delete mode 100644 os/axvisor/configs/vms/rtthread-aarch64-e2000-smp1.toml delete mode 100644 os/axvisor/doc/FDT_Configuration_Guide.md delete mode 100644 os/axvisor/doc/qemu-quickstart.md delete mode 100644 os/axvisor/doc/qemu-quickstart_cn.md delete mode 100644 os/axvisor/doc/shell.md delete mode 100644 os/axvisor/doc/task.py-usage.md delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/Cargo.toml delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/axconfig.toml delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/build.rs delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/linker.lds.S delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/src/boot.rs delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/src/console.rs delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/src/init.rs delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/src/irq.rs delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/src/lib.rs delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/src/mem.rs delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/src/power.rs delete mode 100644 os/axvisor/platform/riscv64-qemu-virt/src/time.rs delete mode 100644 os/axvisor/rust-toolchain.toml delete mode 100755 os/axvisor/scripts/ci_run_qemu_nimbos.py delete mode 100644 os/axvisor/scripts/lds/linker.lds.S delete mode 100644 os/axvisor/scripts/ostool/qemu-aarch64.toml delete mode 100644 os/axvisor/scripts/ostool/qemu-riscv64.toml delete mode 100644 os/axvisor/scripts/ostool/qemu-x86_64.toml delete mode 100755 os/axvisor/scripts/quick-start.sh delete mode 100755 os/axvisor/scripts/setup_qemu.sh delete mode 100644 os/axvisor/src/driver/blk/mod.rs delete mode 100644 os/axvisor/src/driver/blk/phytium.rs delete mode 100644 os/axvisor/src/driver/blk/rockchip.rs delete mode 100644 os/axvisor/src/driver/mod.rs delete mode 100644 os/axvisor/src/driver/serial/mod.rs delete mode 100644 os/axvisor/src/driver/soc/mod.rs delete mode 100644 os/axvisor/src/driver/soc/rockchip/clk/rk3568-clk.rs delete mode 100644 os/axvisor/src/driver/soc/rockchip/clk/rk3588-clk.rs delete mode 100644 os/axvisor/src/driver/soc/rockchip/mod.rs delete mode 100644 os/axvisor/src/driver/soc/rockchip/pm.rs delete mode 100644 os/axvisor/src/hal/arch/aarch64/api.rs delete mode 100644 os/axvisor/src/hal/arch/aarch64/cache.rs delete mode 100644 os/axvisor/src/hal/arch/aarch64/mod.rs delete mode 100644 os/axvisor/src/hal/arch/riscv64/api.rs delete mode 100644 os/axvisor/src/hal/arch/riscv64/cache.rs delete mode 100644 os/axvisor/src/hal/arch/riscv64/mod.rs delete mode 100644 os/axvisor/src/hal/arch/x86_64/cache.rs delete mode 100644 os/axvisor/src/hal/arch/x86_64/mod.rs delete mode 100644 os/axvisor/src/hal/impl_host.rs delete mode 100644 os/axvisor/src/hal/impl_memory.rs delete mode 100644 os/axvisor/src/hal/impl_time.rs delete mode 100644 os/axvisor/src/hal/impl_vmm.rs delete mode 100644 os/axvisor/src/hal/mod.rs delete mode 100644 os/axvisor/src/logo.rs delete mode 100644 os/axvisor/src/main.rs delete mode 100644 os/axvisor/src/shell/command/base.rs delete mode 100644 os/axvisor/src/shell/command/history.rs delete mode 100644 os/axvisor/src/shell/command/mod.rs delete mode 100644 os/axvisor/src/shell/command/vm.rs delete mode 100644 os/axvisor/src/shell/mod.rs delete mode 100644 os/axvisor/src/task.rs delete mode 100644 os/axvisor/src/vmm/config.rs delete mode 100644 os/axvisor/src/vmm/fdt/create.rs delete mode 100644 os/axvisor/src/vmm/fdt/device.rs delete mode 100644 os/axvisor/src/vmm/fdt/mod.rs delete mode 100644 os/axvisor/src/vmm/fdt/parser.rs delete mode 100644 os/axvisor/src/vmm/fdt/print.rs delete mode 100644 os/axvisor/src/vmm/fdt/vm_fdt/mod.rs delete mode 100644 os/axvisor/src/vmm/fdt/vm_fdt/writer.rs delete mode 100644 os/axvisor/src/vmm/hvc.rs delete mode 100644 os/axvisor/src/vmm/images/linux.rs delete mode 100644 os/axvisor/src/vmm/images/mod.rs delete mode 100644 os/axvisor/src/vmm/ivc.rs delete mode 100644 os/axvisor/src/vmm/mod.rs delete mode 100644 os/axvisor/src/vmm/timer.rs delete mode 100644 os/axvisor/src/vmm/vcpus.rs delete mode 100644 os/axvisor/src/vmm/vm_list.rs delete mode 100644 os/axvisor/xtask/src/cargo.rs delete mode 100644 os/axvisor/xtask/src/clippy.rs delete mode 100644 os/axvisor/xtask/src/ctx.rs delete mode 100644 os/axvisor/xtask/src/devspace.rs delete mode 100644 os/axvisor/xtask/src/image.rs delete mode 100644 os/axvisor/xtask/src/image/config.rs delete mode 100644 os/axvisor/xtask/src/image/download.rs delete mode 100644 os/axvisor/xtask/src/image/registry.rs delete mode 100644 os/axvisor/xtask/src/image/spec.rs delete mode 100644 os/axvisor/xtask/src/image/storage.rs delete mode 100644 os/axvisor/xtask/src/main.rs delete mode 100644 os/axvisor/xtask/src/menuconfig.rs delete mode 100644 os/axvisor/xtask/src/tbuld.rs delete mode 100644 os/axvisor/xtask/src/vmconfig.rs diff --git a/os/axvisor/.cargo/config.toml b/os/axvisor/.cargo/config.toml deleted file mode 100644 index 8ebf9f457..000000000 --- a/os/axvisor/.cargo/config.toml +++ /dev/null @@ -1,32 +0,0 @@ -[net] -# 使用系统 git 拉取依赖,可利用已配置的 git 凭证(如 gh auth、credential helper) -git-fetch-with-cli = true - -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-Crelocation-model=pic", - "-Clink-args=-pie", - "-Clink-args=-znostart-stop-gc", - "-Clink-args=-Taxplat.x", -] - -[target.riscv64gc-unknown-none-elf] -rustflags = [ - "-Clink-args=-no-pie", - "-Clink-args=-znostart-stop-gc", - "-Clink-args=-Tlink.x", -] - - -[target.'cfg(target_os = "none")'] -runner = "cargo osrun" - -[target.x86_64-unknown-none] -rustflags = [ - "-Clink-args=-no-pie", - "-Clink-args=-znostart-stop-gc", - "-Clink-args=-Tlinker.x", -] - -[alias] -xtask = "run --bin xtask --no-default-features --features xtask --" diff --git a/os/axvisor/.github/config.json b/os/axvisor/.github/config.json deleted file mode 100644 index b4de9a62c..000000000 --- a/os/axvisor/.github/config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "targets": [ - "aarch64-unknown-none-softfloat" - ], - "rust_components": [ - "rust-src", - "clippy", - "rustfmt" - ] -} \ No newline at end of file diff --git a/os/axvisor/.github/workflows/check.yml b/os/axvisor/.github/workflows/check.yml deleted file mode 100644 index 3debf4892..000000000 --- a/os/axvisor/.github/workflows/check.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Quality Checks - -on: - push: - branches: - - '**' - tags-ignore: - - '**' - pull_request: - workflow_call: - -jobs: - load-config: - name: Load CI Configuration - runs-on: ubuntu-latest - outputs: - targets: ${{ steps.config.outputs.targets }} - rust_components: ${{ steps.config.outputs.rust_components }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Load configuration - id: config - run: | - TARGETS=$(jq -c '.targets' .github/config.json) - COMPONENTS=$(jq -r '.rust_components | join(", ")' .github/config.json) - - echo "targets=$TARGETS" >> $GITHUB_OUTPUT - echo "rust_components=$COMPONENTS" >> $GITHUB_OUTPUT - - check: - name: Check - runs-on: ubuntu-latest - needs: load-config - strategy: - fail-fast: false - matrix: - target: ${{ fromJson(needs.load-config.outputs.targets) }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: ${{ needs.load-config.outputs.rust_components }} - targets: ${{ matrix.target }} - - - name: Check rust version - run: rustc --version --verbose - - - name: Check code format - run: cargo fmt --all -- --check - - # - name: Build - # run: cargo build --target ${{ matrix.target }} --all-features - - - name: Run clippy - # run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings - run: cargo clippy --target ${{ matrix.target }} -- -D warnings - - - name: Build documentation - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features diff --git a/os/axvisor/.github/workflows/deploy.yml b/os/axvisor/.github/workflows/deploy.yml deleted file mode 100644 index c55889c1b..000000000 --- a/os/axvisor/.github/workflows/deploy.yml +++ /dev/null @@ -1,116 +0,0 @@ -name: Deploy - -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+' - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: 'pages' - cancel-in-progress: false - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - -jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_deploy: ${{ steps.check.outputs.should_deploy }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check if tag is on main or master branch - id: check - run: | - git fetch origin main master || true - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Tag is on main or master branch" - echo "should_deploy=true" >> $GITHUB_OUTPUT - else - echo "✗ Tag is not on main or master branch, skipping deployment" - echo "Tag is on: $BRANCHES" - echo "should_deploy=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_deploy == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - - build: - name: Build documentation - runs-on: ubuntu-latest - needs: verify-tag - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Build docs - env: - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: | - # Build documentation - cargo doc --no-deps --all-features - - # Auto-detect documentation directory - # Check if doc exists in target/doc or target/*/doc - if [ -d "target/doc" ]; then - DOC_DIR="target/doc" - else - # Find doc directory under target/*/doc pattern - DOC_DIR=$(find target -type d -name doc -path "target/*/doc" | head -n 1) - if [ -z "$DOC_DIR" ]; then - echo "Error: Could not find documentation directory" - exit 1 - fi - fi - - echo "Documentation found in: $DOC_DIR" - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > "${DOC_DIR}/index.html" - echo "DOC_DIR=${DOC_DIR}" >> $GITHUB_ENV - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ${{ env.DOC_DIR }} - - deploy: - name: Deploy to GitHub Pages - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: [verify-tag, build] - if: needs.verify-tag.outputs.should_deploy == 'true' - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/os/axvisor/.github/workflows/push.yml b/os/axvisor/.github/workflows/push.yml deleted file mode 100644 index a18de4a04..000000000 --- a/os/axvisor/.github/workflows/push.yml +++ /dev/null @@ -1,147 +0,0 @@ -# ═══════════════════════════════════════════════════════════════════════════════ -# 组件仓库 GitHub Actions 配置模板 -# ═══════════════════════════════════════════════════════════════════════════════ -# -# 此文件用于子仓库,当子仓库有更新时通知主仓库进行 subtree pull 同步。 -# -# 【使用步骤】 -# ───────────────────────────────────────────────────────────────────────────── -# 1. 将此文件复制到子仓库的 .github/workflows/ 目录: -# cp scripts/push.yml <子仓库>/.github/workflows/push.yml -# -# 2. 在子仓库中配置 Secret: -# GitHub 仓库 → Settings → Secrets → Actions → New repository secret -# 名称: PARENT_REPO_TOKEN -# 值: 具有主仓库 repo 权限的 Personal Access Token -# -# 3. 修改下方 env 块中的一个变量(标注了「需要修改」的行): -# PARENT_REPO - 主仓库路径,例如 rcore-os/tgoskits -# (subtree 目录由主仓库自动从 git 历史中推断,无需手动指定) -# -# 【Token 权限要求】 -# ───────────────────────────────────────────────────────────────────────────── -# PARENT_REPO_TOKEN 需要 Classic Personal Access Token,权限包括: -# - repo (Full control of private repositories) -# 或 -# - Fine-grained token: Contents (Read and Write) -# -# 【触发条件】 -# ───────────────────────────────────────────────────────────────────────────── -# - 自动触发:推送到 dev 或 main 分支时 -# - 手动触发:Actions → Notify Parent Repository → Run workflow -# -# 【工作流程】 -# ───────────────────────────────────────────────────────────────────────────── -# 子仓库 push → 触发此工作流 → 调用主仓库 API → 主仓库 subtree pull -# -# 【注意事项】 -# ───────────────────────────────────────────────────────────────────────────── -# - 主仓库需要配置接收 repository_dispatch 事件的同步工作流 -# - 如果不需要子仓库到主仓库的同步,可以不使用此文件 -# -# ═══════════════════════════════════════════════════════════════════════════════ - -name: Notify Parent Repository - -# 当有新的推送时触发 -on: - push: - branches: - - main - - master - workflow_dispatch: - -jobs: - notify: - runs-on: ubuntu-latest - steps: - - name: Get repository info - id: repo - env: - GH_REPO_NAME: ${{ github.event.repository.name }} - GH_REF_NAME: ${{ github.ref_name }} - GH_SERVER_URL: ${{ github.server_url }} - GH_REPOSITORY: ${{ github.repository }} - run: | - # 直接使用 GitHub Actions 内置变量,通过 env 传入避免 shell 注入 - COMPONENT="$GH_REPO_NAME" - BRANCH="$GH_REF_NAME" - # 构造标准 HTTPS URL,供主仓库按 URL 精确匹配 repos.list - REPO_URL="${GH_SERVER_URL}/${GH_REPOSITORY}" - - echo "component=${COMPONENT}" >> $GITHUB_OUTPUT - echo "branch=${BRANCH}" >> $GITHUB_OUTPUT - echo "repo_url=${REPO_URL}" >> $GITHUB_OUTPUT - - echo "Component: ${COMPONENT}" - echo "Branch: ${BRANCH}" - echo "Repo URL: ${REPO_URL}" - - - name: Notify parent repository - env: - # ── 需要修改 ────────────────────────────────────────────────────────── - PARENT_REPO: "rcore-os/tgoskits" # 主仓库路径 - # ── 无需修改 ────────────────────────────────────────────────────────── - DISPATCH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }} - # 将用户可控内容通过 env 传入,避免直接插值到 shell 脚本 - COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - GIT_ACTOR: ${{ github.actor }} - GIT_SHA: ${{ github.sha }} - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "Notifying parent repository about update in ${COMPONENT}:${BRANCH}" - - # 使用 jq 安全构建 JSON,避免 commit message 中任何特殊字符导致注入 - PAYLOAD=$(jq -n \ - --arg component "$COMPONENT" \ - --arg branch "$BRANCH" \ - --arg repo_url "$REPO_URL" \ - --arg commit "$GIT_SHA" \ - --arg message "$COMMIT_MESSAGE" \ - --arg author "$GIT_ACTOR" \ - '{ - event_type: "subtree-update", - client_payload: { - component: $component, - branch: $branch, - repo_url: $repo_url, - commit: $commit, - message: $message, - author: $author - } - }') - - curl --fail --show-error -X POST \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Authorization: token ${DISPATCH_TOKEN}" \ - https://api.github.com/repos/${PARENT_REPO}/dispatches \ - -d "$PAYLOAD" - - echo "Notification sent successfully" - - - name: Create summary - env: - STEP_COMPONENT: ${{ steps.repo.outputs.component }} - STEP_BRANCH: ${{ steps.repo.outputs.branch }} - STEP_REPO_URL: ${{ steps.repo.outputs.repo_url }} - GIT_SHA: ${{ github.sha }} - GIT_ACTOR: ${{ github.actor }} - run: | - COMPONENT="$STEP_COMPONENT" - BRANCH="$STEP_BRANCH" - REPO_URL="$STEP_REPO_URL" - - echo "## Notification Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "- **Component**: ${COMPONENT}" >> $GITHUB_STEP_SUMMARY - echo "- **Branch**: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - echo "- **Repo URL**: ${REPO_URL}" >> $GITHUB_STEP_SUMMARY - echo "- **Commit**: \`${GIT_SHA}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Author**: ${GIT_ACTOR}" >> $GITHUB_STEP_SUMMARY - echo "- **Status**: ✅ Notification sent" >> $GITHUB_STEP_SUMMARY diff --git a/os/axvisor/.github/workflows/qemu-aarch64.toml b/os/axvisor/.github/workflows/qemu-aarch64.toml deleted file mode 100644 index 8ecf3ebfb..000000000 --- a/os/axvisor/.github/workflows/qemu-aarch64.toml +++ /dev/null @@ -1,24 +0,0 @@ -args = [ - "-nographic", - "-cpu", - "cortex-a72", - "-machine", - "virt,virtualization=on,gic-version=3", - "-smp", - "4", - "-device", - "virtio-blk-device,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-append", - "root=/dev/vda rw init=/init", - "-m", - "8g", -] -fail_regex = ["panicked at"] -success_regex = [ - "Hello, world!", - "test pass!", -] -to_bin = true -uefi = false diff --git a/os/axvisor/.github/workflows/qemu-riscv64.toml b/os/axvisor/.github/workflows/qemu-riscv64.toml deleted file mode 100644 index b228b6f1b..000000000 --- a/os/axvisor/.github/workflows/qemu-riscv64.toml +++ /dev/null @@ -1,24 +0,0 @@ -args = [ - "-nographic", - "-cpu", - "rv64", - "-machine", - "virt", - "-bios", - "default", - "-smp", - "4", - "-device", - "virtio-blk-device,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-m", - "8g", -] -fail_regex = [] -success_regex = [ - "Hello, world!", - "test pass!", -] -to_bin = true -uefi = false \ No newline at end of file diff --git a/os/axvisor/.github/workflows/qemu-x86_64-kvm.toml b/os/axvisor/.github/workflows/qemu-x86_64-kvm.toml deleted file mode 100644 index 7171f958b..000000000 --- a/os/axvisor/.github/workflows/qemu-x86_64-kvm.toml +++ /dev/null @@ -1,24 +0,0 @@ -# QEMU x86_64 配置 - KVM 加速(需 VT-x/VMX 支持) -# 用于物理 Linux 机或支持嵌套虚拟化的 VM -# 若使用 TCG(无 KVM),请用 qemu-x86_64.toml -args = [ - "-m", - "128M", - "-smp", - "1", - "-machine", - "q35", - "-device", - "virtio-blk-pci,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-nographic", - "-accel", - "kvm", - "-cpu", - "host", -] -fail_regex = ["System will reboot, press any key to continue ..."] -success_regex = ["usertests passed!"] -to_bin = false -uefi = false diff --git a/os/axvisor/.github/workflows/qemu-x86_64.toml b/os/axvisor/.github/workflows/qemu-x86_64.toml deleted file mode 100644 index d558ef66a..000000000 --- a/os/axvisor/.github/workflows/qemu-x86_64.toml +++ /dev/null @@ -1,21 +0,0 @@ -args = [ - "-m", - "128M", - "-smp", - "1", - "-machine", - "q35", - "-device", - "virtio-blk-pci,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-nographic", - "-accel", - "kvm", - "-cpu", - "host", -] -fail_regex = ["System will reboot, press any key to continue ..."] -success_regex = ["usertests passed!"] -to_bin = false -uefi = false \ No newline at end of file diff --git a/os/axvisor/.github/workflows/release.yml b/os/axvisor/.github/workflows/release.yml deleted file mode 100644 index 961ebf795..000000000 --- a/os/axvisor/.github/workflows/release.yml +++ /dev/null @@ -1,165 +0,0 @@ -name: Release - -on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+' - - 'v[0-9]+.[0-9]+.[0-9]+-(pre|preview).[0-9]+' - -permissions: - contents: write - -jobs: - verify-tag: - name: Verify Tag - runs-on: ubuntu-latest - outputs: - should_release: ${{ steps.check.outputs.should_release }} - is_prerelease: ${{ steps.check.outputs.is_prerelease }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check tag type and branch - id: check - run: | - git fetch origin main master dev || true - - TAG="${{ github.ref_name }}" - BRANCHES=$(git branch -r --contains ${{ github.ref }}) - - echo "Tag: $TAG" - echo "Branches containing this tag: $BRANCHES" - - # Check if it's a prerelease tag - if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-(pre|preview)\.[0-9]+$ ]]; then - echo "📦 Detected prerelease tag" - echo "is_prerelease=true" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -q 'origin/dev'; then - echo "✓ Prerelease tag is on dev branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Prerelease tag must be on dev branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - elif [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "📦 Detected stable release tag" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - - if echo "$BRANCHES" | grep -qE 'origin/(main|master)'; then - echo "✓ Stable release tag is on main or master branch" - echo "should_release=true" >> $GITHUB_OUTPUT - else - echo "✗ Stable release tag must be on main or master branch, skipping release" - echo "should_release=false" >> $GITHUB_OUTPUT - fi - else - echo "✗ Unknown tag format, skipping release" - echo "is_prerelease=false" >> $GITHUB_OUTPUT - echo "should_release=false" >> $GITHUB_OUTPUT - fi - - - name: Verify version consistency - if: steps.check.outputs.should_release == 'true' - run: | - # Extract version from git tag (remove 'v' prefix) - TAG_VERSION="${{ github.ref_name }}" - TAG_VERSION="${TAG_VERSION#v}" - # Extract version from Cargo.toml - CARGO_VERSION=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/') - echo "Git tag version: $TAG_VERSION" - echo "Cargo.toml version: $CARGO_VERSION" - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "✓ Version check passed!" - - check: - uses: ./.github/workflows/check.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' - - test-board: - uses: ./.github/workflows/test-board.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' - - test-qemu: - uses: ./.github/workflows/test-qemu.yml - needs: verify-tag - if: needs.verify-tag.outputs.should_release == 'true' - - release: - name: Create GitHub Release - runs-on: ubuntu-latest - needs: [verify-tag, check, test-board, test-qemu] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate release notes - id: release_notes - run: | - CURRENT_TAG="${{ github.ref_name }}" - - # Get previous tag - PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${CURRENT_TAG}$" | tail -n1) - - if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$CURRENT_TAG" ]; then - echo "No previous tag found, this is the first release" - CHANGELOG="Initial release" - else - echo "Generating changelog from $PREVIOUS_TAG to $CURRENT_TAG" - - # Generate changelog with commit messages - CHANGELOG=$(git log --pretty=format:"- %s (%h)" "${PREVIOUS_TAG}..${CURRENT_TAG}") - - if [ -z "$CHANGELOG" ]; then - CHANGELOG="No changes" - fi - fi - - # Write changelog to output file (multi-line) - { - echo "changelog<> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - draft: false - prerelease: ${{ needs.verify-tag.outputs.is_prerelease == 'true' }} - body: | - ## Changes - ${{ steps.release_notes.outputs.changelog }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish: - name: Publish to crates.io - runs-on: ubuntu-latest - needs: [verify-tag, check, test-board, test-qemu] - if: needs.verify-tag.outputs.should_release == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Dry run publish - run: cargo publish --dry-run - - - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/os/axvisor/.github/workflows/test-board.yml b/os/axvisor/.github/workflows/test-board.yml deleted file mode 100644 index 8f3ab7679..000000000 --- a/os/axvisor/.github/workflows/test-board.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Test for BOARD - -on: - push: - branches: - - '**' - tags-ignore: - - '**' - pull_request: - workflow_call: - -jobs: - test-board: - name: "Test board: ${{ matrix.board }} - ${{ matrix.vmconfigs_name }}" - strategy: - matrix: - include: - - board: phytiumpi - vmconfigs: configs/vms/arceos-aarch64-e2000-smp1.toml - vmconfigs_name: ArceOS - - board: phytiumpi - vmconfigs: configs/vms/linux-aarch64-e2000-smp1.toml - vmconfigs_name: Linux - - board: roc-rk3568-pc - vmconfigs: configs/vms/arceos-aarch64-rk3568-smp1.toml - vmconfigs_name: ArceOS - - board: roc-rk3568-pc - vmconfigs: configs/vms/linux-aarch64-rk3568-smp1.toml - vmconfigs_name: Linux - fail-fast: false - runs-on: - - self-hosted - - linux - - ${{ matrix.board }} - - steps: - - name: Clean up workspace - run: | - [ -d .git ] && git submodule deinit -f . || true - [ -d .git/modules ] && rm -rf .git/modules || true - [ -d .git ] && git clean -ffdx || true - - - name: Checkout - uses: actions/checkout@v4 - with: - clean: true - submodules: recursive - - - name: Install dependencies - run: cargo +stable install ostool --version ^0.8 - - - name: Run tests - run: | - echo $BOARD_POWER_RESET - export RUST_LOG=debug - # Use script to provide a PTY so ostool's crossterm can initialize (avoids "reader source not set" panic in CI) - script -q -c "cargo xtask uboot \ - --build-config configs/board/${{ matrix.board }}.toml \ - --uboot-config .github/workflows/uboot.toml \ - --vmconfigs ${{ matrix.vmconfigs }} \ - --bin-dir /home/runner/tftp" /dev/null - # cargo xtask uboot \ - # --build-config configs/board/${{ matrix.board }}.toml \ - # --uboot-config .github/workflows/uboot.toml diff --git a/os/axvisor/.github/workflows/test-qemu.yml b/os/axvisor/.github/workflows/test-qemu.yml deleted file mode 100644 index b445a3303..000000000 --- a/os/axvisor/.github/workflows/test-qemu.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: Test for QEMU - -on: - push: - branches: - - '**' - tags-ignore: - - '**' - pull_request: - workflow_call: - -jobs: - test-qemu: - name: "Test qemu: ${{ matrix.arch }} - ${{ matrix.vmconfigs_name }}" - strategy: - matrix: - include: - - arch: aarch64 - vmconfigs: configs/vms/arceos-aarch64-qemu-smp1.toml - vmconfigs_name: ArceOS - vmimage_name: qemu_aarch64_arceos - - arch: aarch64 - vmconfigs: configs/vms/linux-aarch64-qemu-smp1.toml - vmconfigs_name: Linux - vmimage_name: qemu_aarch64_linux - # - arch: riscv64 - # vmconfigs: configs/vms/arceos-riscv64-qemu-smp1.toml - # vmconfigs_name: ArceOS - # vmimage_name: qemu_arceos_riscv64 - - arch: x86_64 - vmconfigs: configs/vms/nimbos-x86_64-qemu-smp1.toml - vmconfigs_name: NimbOS - vmimage_name: qemu_x86_64_nimbos - fail-fast: false - runs-on: - - self-hosted - - linux - - intel - - steps: - - name: Clean up workspace - run: | - [ -d .git ] && git submodule deinit -f . || true - [ -d .git ] && git clean -ffdx || true - - - name: Checkout - uses: actions/checkout@v4 - with: - clean: true - submodules: recursive - - - name: Install dependencies - run: cargo +stable install ostool --version ^0.8 - - - name: Download images and patch vm configs - run: | - ls -lha . - - IMAGE_DIR="/tmp/.axvisor-images" - IFS=',' read -ra CONFIGS <<< "${{ matrix.vmconfigs }}" - IFS=',' read -ra IMAGES <<< "${{ matrix.vmimage_name }}" - for i in "${!CONFIGS[@]}"; do - img="${IMAGES[$i]}" - img=$(echo "$img" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') - config="${CONFIGS[$i]}" - config=$(echo "$config" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') - cargo xtask image download $img - img_name="qemu-${{ matrix.arch }}" - image_location=$(sed -n 's/^image_location[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' "$config") - - case "$image_location" in - "fs") - echo "Filesystem storage mode - no config update needed" - ;; - "memory") - sed -i 's|^kernel_path[[:space:]]*=.*|kernel_path = "'"${IMAGE_DIR}"'/'"$img"'/'"$img_name"'"|' "$config" - echo "Memory storage mode - updating kernel_path" - # NimbOS x86_64 requires axvm-bios for bootstrapping; ensure it exists in the extracted image - if [[ "$img" == "qemu_x86_64_nimbos" ]]; then - if [ ! -f "${IMAGE_DIR}/${img}/axvm-bios.bin" ]; then - echo "ERROR: axvm-bios.bin not found for NimbOS image at ${IMAGE_DIR}/${img}/axvm-bios.bin" - exit 1 - fi - fi - ;; - *) - echo "Unknown image_location: $image_location" - exit 1 - ;; - esac - - ROOTFS_IMG_PATH="${IMAGE_DIR}/$img/rootfs.img" - - # Check if rootfs.img exists after extraction - if [ -f "${ROOTFS_IMG_PATH}" ]; then - echo "Found rootfs.img, patching qemu-aarch64.toml file..." - sed -i 's|file=${workspaceFolder}/tmp/rootfs.img|file='"${ROOTFS_IMG_PATH}"'|' ".github/workflows/qemu-${{ matrix.arch }}.toml" - echo "Rootfs setup completed" - else - echo "No rootfs.img found, removing rootfs device configuration from qemu-${{ matrix.arch }}.toml..." - sed -i '/-device/,/virtio-blk-device,drive=disk0/d' ".github/workflows/qemu-${{ matrix.arch }}.toml" - sed -i '/-drive/,/id=disk0,if=none,format=raw,file=${workspaceFolder}\/tmp\/rootfs.img/d' ".github/workflows/qemu-${{ matrix.arch }}.toml" - sed -i 's/root=\/dev\/vda rw //' ".github/workflows/qemu-${{ matrix.arch }}.toml" - echo "Rootfs device configuration removed" - fi - done - - - name: Run tests - run: | - export RUST_LOG=debug - if [ "${{ matrix.vmconfigs_name }}" = "NimbOS" ]; then - python3 scripts/ci_run_qemu_nimbos.py -- \ - cargo xtask qemu \ - --build-config configs/board/qemu-${{ matrix.arch }}.toml \ - --qemu-config .github/workflows/qemu-${{ matrix.arch }}.toml \ - --vmconfigs ${{ matrix.vmconfigs }} - else - cargo xtask qemu \ - --build-config configs/board/qemu-${{ matrix.arch }}.toml \ - --qemu-config .github/workflows/qemu-${{ matrix.arch }}.toml \ - --vmconfigs ${{ matrix.vmconfigs }} - fi diff --git a/os/axvisor/.github/workflows/uboot.toml b/os/axvisor/.github/workflows/uboot.toml deleted file mode 100644 index ef36d350f..000000000 --- a/os/axvisor/.github/workflows/uboot.toml +++ /dev/null @@ -1,20 +0,0 @@ -baud_rate = "${env:BOARD_COMM_UART_BAUD}" -board_power_off_cmd = "${env:BOARD_POWER_OFF}" -board_reset_cmd = "${env:BOARD_POWER_RESET}" -dtb_file = "${env:BOARD_DTB}" -fail_regex = [ - "panicked at", -] -serial = "${env:BOARD_COMM_UART_DEV}" -success_regex = [ - "Welcome to AxVisor Shell!", - "All tests passed!", - "Hello World!", - "root@firefly:~#", - "^Phytium Pi", - "Last login: *", -] - -[net] -interface = "${env:BOARD_COMM_NET_IFACE}" -tftp_dir = "${env:TFTP_DIR}" \ No newline at end of file diff --git a/os/axvisor/.gitignore b/os/axvisor/.gitignore deleted file mode 100644 index ed539b02b..000000000 --- a/os/axvisor/.gitignore +++ /dev/null @@ -1,68 +0,0 @@ -# Build output and other log/bin files from arceos -**/target -/.vscode -/tmp -/.arceos -/.images -/.image.toml - -.img -*.bin -*.elf -*.tmp_its -*guest.S -actual.out -qemu.log - -/**/.axconfig.* -/**/.hvconfig.* -/**/.project.* -/**/.board.* - -# dev env -/crates/* -!/crates/nop/ -!/crates/nop/** -.devspace/ - -/Cargo.toml.bk - -# tools should be downloaded from github -tools/* - -# initramfs -*.cpio.gz - -# Python -__pycache__/ -*.py[cod] -*$py.class -*.so -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -*-schema.json -.qemu* -.build* -.uboot* - -# AI tools -.spec-workflow/ -.claude/ - -.idea/ \ No newline at end of file diff --git a/os/axvisor/.gitmodules b/os/axvisor/.gitmodules deleted file mode 100644 index e69de29bb..000000000 diff --git a/os/axvisor/.rustfmt.toml b/os/axvisor/.rustfmt.toml deleted file mode 100644 index 89ab2b7fc..000000000 --- a/os/axvisor/.rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -force_explicit_abi = false \ No newline at end of file diff --git a/os/axvisor/Cargo.lock b/os/axvisor/Cargo.lock deleted file mode 100644 index 4c69006f5..000000000 --- a/os/axvisor/Cargo.lock +++ /dev/null @@ -1,6916 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aarch64-cpu" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a21cd0131c25c438e19cd6a774adf7e3f64f7f4d723022882facc2dee0f8bc9" -dependencies = [ - "tock-registers 0.9.0", -] - -[[package]] -name = "aarch64-cpu" -version = "11.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44171e22925ec72b63d86747bc3655c7849a5b8d865c980222128839f45ac034" -dependencies = [ - "tock-registers 0.10.1", -] - -[[package]] -name = "aarch64-cpu-ext" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dad5cf7342926ce1c375ec680834e56dd3cdbe8b7adf8a6f99b2854cc52c17" -dependencies = [ - "aarch64-cpu 10.0.0", - "tock-registers 0.10.1", -] - -[[package]] -name = "aarch64_sysreg" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2c929f5025d9b8a0f549b187c4d3a39671f44015ff6ccddd0b134c874b3c1a" - -[[package]] -name = "acpi" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b763acdc1d85c36d61acf97a59938f23202d0e8efe45e8759de10c02db242744" -dependencies = [ - "bit_field", - "bitflags 2.11.0", - "byteorder", - "log", - "pci_types", - "spinning_top 0.3.0", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "getrandom 0.3.4", - "once_cell", - "version_check", - "zerocopy 0.8.42", -] - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "aml" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f8cba7d4260ea05671dda81029f6f718b54402a4ec926a0d9a41bdbb96b415" -dependencies = [ - "bit_field", - "bitvec", - "byteorder", - "log", - "spinning_top 0.2.5", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_rgb" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a730095eb14ee842a0f1e68504b85c8d4a19b1ef2ac2a9b4debf0ed982f9b08a" -dependencies = [ - "rgb", -] - -[[package]] -name = "anstream" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "arceos_api" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0ec20839aa76d3a2eedd79b11e444a83db4c160135f1016ef22b6399a627be" -dependencies = [ - "axalloc", - "axconfig", - "axdriver", - "axerrno 0.2.2", - "axfeat", - "axfs 0.2.0", - "axhal", - "axio", - "axlog", - "axruntime", - "axsync", - "axtask", -] - -[[package]] -name = "arm-gic-driver" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e30c6a0ffd23095c69f48afd996eb51156b2511b52a01bdbb0b418fdfd1d458c" -dependencies = [ - "aarch64-cpu 10.0.0", - "bitflags 2.10.0", - "aarch64-cpu 11.2.0", - "bitflags 2.11.0", - "enum_dispatch", - "log", - "paste", - "tock-registers 0.10.1", -] - -[[package]] -name = "arm-gic-driver" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dda00d35b3c85f4e994746587c4579e63c0ba350b843fca96d6531b609292ae" -dependencies = [ - "aarch64-cpu 11.2.0", - "bitflags 2.11.0", - "enum_dispatch", - "log", - "paste", - "rdif-intc", - "tock-registers 0.10.1", -] - -[[package]] -name = "arm_pl011" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efcf6afca4502993a737ba1e00952d1321078689da92bf7aab27d4e5756c0bec" -dependencies = [ - "tock-registers 0.8.1", -] - -[[package]] -name = "arm_pl031" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13696b1c2b59992f4223e0ae5bb173c81c63039367ca90eee845346ad2a13421" -dependencies = [ - "chrono", -] - -[[package]] -name = "arm_vcpu" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "979230a9de461189f361c5a41a01006b4e943dcf9ebbac1c1444fb6c29fdbae2" -dependencies = [ - "aarch64-cpu 11.2.0", - "axaddrspace", - "axdevice_base", - "axerrno 0.2.2", - "axvcpu", - "axvisor_api", - "log", - "numeric-enum-macro", - "percpu", - "spin 0.10.0", -] - -[[package]] -name = "arm_vgic" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c287b9ceddf2a9f36662df520a85e2fca93a194d303d98d50727c4b19647fa" -dependencies = [ - "aarch64-cpu 11.2.0", - "aarch64_sysreg", - "axaddrspace", - "axdevice_base", - "axerrno 0.2.2", - "axvisor_api", - "bitmaps", - "log", - "memory_addr", - "spin 0.10.0", - "tock-registers 0.10.1", -] - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "as-any" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "aws-lc-rs" -version = "1.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - -[[package]] -name = "axaddrspace" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2dffa605d03eb184604fb7fcc15f2e641cf21b7a17b8f71e23b22f5c04b7be" -dependencies = [ - "axerrno 0.2.2", - "bit_field", - "bitflags 2.11.0", - "cfg-if", - "lazyinit", - "log", - "memory_addr", - "memory_set", - "numeric-enum-macro", - "page_table_entry 0.6.1", - "page_table_multiarch 0.6.1", - "x86", -] - -[[package]] -name = "axalloc" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5bb0695cfda0a651f3b44026899987c51c76063a9a807666f8f6b5e93a67966" -dependencies = [ - "axallocator", - "axerrno 0.2.2", - "cfg-if", - "kspin", - "log", - "memory_addr", - "strum 0.27.2", -] - -[[package]] -name = "axallocator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3894f6027940d4b013f1d1f9e2e61b47a9e4a7dbf1a0ba10dd33e7bb265ea733" -dependencies = [ - "axerrno 0.1.2", - "bitmap-allocator", - "cfg-if", - "rlsf", -] - -[[package]] -name = "axbacktrace" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed127f24c722b06d19c9c1ff73907905bbc2f9463dfd7440652f3f0d6617a93" -dependencies = [ - "cfg-if", - "log", - "spin 0.10.0", -] - -[[package]] -name = "axconfig" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f74d126fc479e4a5acd4389e7ef2c9176c41e7ea3c54e985a7468489b494fb" -dependencies = [ - "axconfig-macros", - "const-str", -] - -[[package]] -name = "axconfig-gen" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8021a26bbd0b7e0760e28ded5dba2082fda8224c7cfd457ab370ff851626452" -dependencies = [ - "clap", - "toml_edit 0.22.27", -] - -[[package]] -name = "axconfig-macros" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2decc5437a10ddb659f0fbd819b9308362ea1d11455ddb5a1c47ea3973920d" -dependencies = [ - "axconfig-gen", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "axcpu" -version = "0.3.0-preview.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361edfc761188b19fb3d906b0b155942a6290068ee88d42f3b1f0ce31dcd099e" -dependencies = [ - "aarch64-cpu 10.0.0", - "axbacktrace", - "cfg-if", - "lazyinit", - "linkme", - "log", - "loongArch64", - "memory_addr", - "page_table_entry 0.5.7", - "page_table_multiarch 0.5.7", - "percpu", - "riscv 0.14.0", - "static_assertions", - "tock-registers 0.10.1", - "x86", - "x86_64", -] - -[[package]] -name = "axdevice" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55bcbe9825eecc55421eb205511a0d359f0c83141a4df53f9d3c2824b515fbba" -dependencies = [ - "arm_vgic", - "axaddrspace", - "axdevice_base", - "axerrno 0.2.2", - "axvmconfig", - "cfg-if", - "log", - "memory_addr", - "range-alloc-arceos", - "riscv_vplic", - "spin 0.10.0", -] - -[[package]] -name = "axdevice_base" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f466dbb1dcf24f0ad76f737b9b65425a1bdaef30daeff938c9d69d808eb1805" -dependencies = [ - "axaddrspace", - "axerrno 0.2.2", - "axvmconfig", - "serde", -] - -[[package]] -name = "axdriver" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aea5c6b6a48e7283ede40784ded0eedd371e98d59faf97e7d9f4a73dcec4fe00" -dependencies = [ - "axalloc", - "axconfig", - "axdriver_base", - "axdriver_block", - "axdriver_pci", - "axdriver_virtio", - "axerrno 0.2.2", - "axhal", - "axplat-dyn", - "cfg-if", - "crate_interface 0.1.4", - "dma-api 0.5.2", - "log", - "smallvec", -] - -[[package]] -name = "axdriver_base" -version = "0.1.4-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4614d9e505dae224e4e4cb934c8fa916d77ecfb7b770a8bf8eac78bdeec3dba" - -[[package]] -name = "axdriver_block" -version = "0.1.4-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cccf23999a9dff620ef87c08c571509d2e90cc9dc80f932381b0fd949f020f9" -dependencies = [ - "axdriver_base", - "log", -] - -[[package]] -name = "axdriver_pci" -version = "0.1.4-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129bd3a5ad486d989e9bdc211c86fd8d50720462ef46e2b0013d333338d50e98" -dependencies = [ - "virtio-drivers", -] - -[[package]] -name = "axdriver_virtio" -version = "0.1.4-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6c36cc900745f3bab9de0dd8d5a2d5ac720a253937b3c5f3ab81bbb9c9e139" -dependencies = [ - "axdriver_base", - "axdriver_block", - "log", - "virtio-drivers", -] - -[[package]] -name = "axerrno" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a88b1fa2ce97a6ff4ce31ba9fda3065730ca4d77a1ba50dec000fc04f1fb686" -dependencies = [ - "axerrno 0.2.2", - "log", -] - -[[package]] -name = "axerrno" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f961d2868582a092fb1e71b90c16cc6f2cbbe7bb5fa7e4bd6fe61d882ce6bb34" -dependencies = [ - "log", - "strum 0.27.2", -] - -[[package]] -name = "axfatfs" -version = "0.1.0-pre.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf1c27753a96a0f835cca49e6fb354912107d018c905d13c7eff39be757eb5a" -dependencies = [ - "bitflags 2.11.0", - "log", -] - -[[package]] -name = "axfeat" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc376f478b85111d8b9ba475040d1016708c35f15e634efbdf8f8495272bb2" -dependencies = [ - "axalloc", - "axbacktrace", - "axconfig", - "axdriver", - "axfs 0.2.0", - "axhal", - "axlog", - "axruntime", - "axsync", - "axtask", - "kspin", -] - -[[package]] -name = "axfs" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b7b9d07fd327e010fd3b9c09ea9ccdfaccf329afd859ff802d2448dfb685ac9" -dependencies = [ - "axdriver", - "axerrno 0.2.2", - "axfatfs", - "axfs_devfs", - "axfs_ramfs", - "axfs_vfs", - "axio", - "cap_access", - "fatfs 0.4.0 (git+https://github.com/Josen-B/rust-fatfs.git?rev=41122ef)", - "lazyinit", - "log", - "rsext4", - "spin 0.10.0", -] - -[[package]] -name = "axfs" -version = "0.2.0" -source = "git+https://github.com/arceos-org/arceos.git?tag=dev-251216#16096568f5ae6ad2d687eff2927a4cb69cef8133" -dependencies = [ - "axdriver", - "axdriver_block 0.1.2 (git+https://github.com/arceos-org/axdriver_crates.git?tag=dev-v01)", - "axerrno 0.1.2", - "axfs_devfs", - "axfs_ramfs", - "axfs_vfs", - "axio", - "axsync", - "cap_access", - "cfg-if", - "fatfs 0.4.0 (git+https://github.com/rafalh/rust-fatfs?rev=4eccb50)", - "lazyinit", - "log", - "scope-local", -] - -[[package]] -name = "axfs_ramfs" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50c26614485d837a3fc09a92f24a226caddc25a30df7e6aaf4bd19b304c399" -dependencies = [ - "axfs_vfs", - "log", - "spin 0.9.8", -] - -[[package]] -name = "axfs_vfs" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcba2006898d7879d456a9c34b9c9460cb536f5bf69d1d5d7d0e0f19f073368d" -dependencies = [ - "axerrno 0.1.2", - "bitflags 2.11.0", - "log", -] - -[[package]] -name = "axhal" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281f69e09ddb6eb04b7695fc8bdf5a58c760c630443aca8e69abc65ded5642da" -dependencies = [ - "axalloc", - "axconfig", - "axcpu", - "axplat", - "axplat-aarch64-qemu-virt", - "axplat-dyn", - "axplat-loongarch64-qemu-virt", - "axplat-riscv64-qemu-virt 0.3.0 (git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03)", - "axplat-x86-pc", - "cfg-if", - "fdt-parser", - "heapless", - "kernel_guard", - "linkme", - "log", - "memory_addr", - "page_table_multiarch 0.5.7", - "percpu", - "spin 0.10.0", -] - -[[package]] -name = "axhvc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6262e1b84fe6c912c07cc7d9dfa45d8f4511870d79a2886ce6217278bfb8c0dc" -dependencies = [ - "axerrno 0.2.2", -] - -[[package]] -name = "axio" -version = "0.3.0-pre.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ce41624ae4e7ef942ebe3ac3aa3ce5d64340e8f23fb29bbd0007e9765544b4" -dependencies = [ - "autocfg", - "axerrno 0.2.2", - "heapless", - "memchr", -] - -[[package]] -name = "axklib" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03bf328ee0dd583179ce7108584ec69da10e06bd4beb4d6acac7f6cb33754dab" -dependencies = [ - "axerrno 0.2.2", - "memory_addr", - "trait-ffi", -] - -[[package]] -name = "axlog" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b27e79e15984afd375b94328701719b8ab0d6ef4092a7999c8277f9b2f410d" -dependencies = [ - "cfg-if", - "crate_interface 0.1.4", - "kspin", - "log", -] - -[[package]] -name = "axmm" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e9513a2db8f0ad60733581f0ecd77c8c7f7db7b5fcaa1784752504ad924e00" -dependencies = [ - "axalloc", - "axerrno 0.2.2", - "axhal", - "kspin", - "lazyinit", - "log", - "memory_addr", - "memory_set", -] - -[[package]] -name = "axplat" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29d8d929fe41fcb361cf784819041f942aba4bcd2d26b9a26305df84e26b206b" -dependencies = [ - "axplat-macros", - "bitflags 2.11.0", - "const-str", - "crate_interface 0.1.4", - "handler_table", - "kspin", - "memory_addr", - "percpu", -] - -[[package]] -name = "axplat-aarch64-dyn" -version = "0.4.0" -source = "git+https://github.com/arceos-hypervisor/axplat-aarch64-dyn.git?tag=v0.4.0#05d5acd43d925807496255a9b9e1aa2d272bb591" -dependencies = [ - "aarch64-cpu 10.0.0", - "aarch64-cpu-ext", - "any-uart", - "arm-gic-driver", - "axconfig-macros", - "axcpu", - "axplat", - "fdt-parser", - "heapless 0.8.0", - "lazyinit", - "log", - "memory_addr", - "page_table_entry 0.5.7", - "paste", - "percpu", - "rdif-intc", - "rdrive", - "serde", - "somehal", - "spin 0.10.0", - "toml 0.8.23", -] - -[[package]] -name = "axplat-aarch64-peripherals" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a744097da129e66068e4fff6758726c98bf329a7182dcc65712ac80daed581ed" -dependencies = [ - "aarch64-cpu 10.0.0", - "arm-gic-driver", - "arm_pl011", - "arm_pl031", - "axcpu", - "axplat", - "int_ratio", - "kspin", - "lazyinit", - "log", - "page_table_entry 0.5.7", - "spin 0.10.0", -] - -[[package]] -name = "axplat-aarch64-qemu-virt" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff51219e3ff388368eec683f83b5a8184b3125bac709006f6ac22e435dc07e4" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "axplat-aarch64-peripherals", - "log", - "page_table_entry 0.5.7", -] - -[[package]] -name = "axplat-dyn" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e6762cfd943160d0baa8c2db303adc2b10d1dc3fb754bb8b6b364ac37c7ad2a" -dependencies = [ - "anyhow", - "axalloc", - "axconfig-macros", - "axcpu", - "axdriver_base", - "axdriver_block", - "axdriver_virtio", - "axerrno 0.2.2", - "axklib", - "axplat", - "dma-api 0.7.1", - "fdt-edit", - "heapless", - "log", - "memory_addr", - "percpu", - "rd-block", - "rdrive", - "somehal", - "spin 0.10.0", -] - -[[package]] -name = "axplat-loongarch64-qemu-virt" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb9c67d904ccf30561a6239b67411058b215cb6d6a8d3ac737e7a51230b40bf" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "kspin", - "lazyinit", - "log", - "loongArch64", - "page_table_entry 0.5.7", - "uart_16550", -] - -[[package]] -name = "axplat-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90dfaee06a112fe4f810c60af1a86bc080af2172185b491cacc307b84dff748" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "axplat-riscv64-qemu-virt" -version = "0.3.0" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "axvisor_api", - "crate_interface 0.3.0", - "kspin", - "lazyinit", - "log", - "riscv 0.14.0", - "riscv_goldfish", - "riscv_plic", - "sbi-rt", - "uart_16550", -] - -[[package]] -name = "axplat-riscv64-qemu-virt" -version = "0.3.0" -source = "git+https://github.com/arceos-org/axplat_crates.git?tag=dev-v03#0df0713b1c20eafaeebdc6b0e194b2985e857949" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "kspin", - "lazyinit", - "log", - "riscv 0.14.0", - "riscv_plic", - "sbi-rt", - "uart_16550", -] - -[[package]] -name = "axplat-x86-pc" -version = "0.3.1-pre.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df26719c444ca8302e9366b8dc5abe8735933ea756ff094e3ac5ce3b64c41a1" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "bitflags 2.11.0", - "heapless", - "int_ratio", - "kspin", - "lazyinit", - "log", - "multiboot", - "percpu", - "raw-cpuid 11.6.0", - "uart_16550", - "x2apic", - "x86", - "x86_64", -] - -[[package]] -name = "axplat-x86-qemu-q35" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "828cbdb755936681e60fefd3d4b16842d67a18e6ccc7661b3b9ef9e6322670cd" -dependencies = [ - "axconfig-macros", - "axcpu", - "axplat", - "bitflags 2.11.0", - "heapless", - "int_ratio", - "kspin", - "lazyinit", - "log", - "multiboot", - "percpu", - "raw-cpuid 11.6.0", - "uart_16550", - "x2apic", - "x86", - "x86_64", -] - -[[package]] -name = "axpoll" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b92f85c6903350f5146216ccb7d7a7e7b4dbd6f5927a1279db03ba52a53ae7" -dependencies = [ - "bitflags 2.11.0", - "linux-raw-sys 0.12.1", - "spin 0.10.0", -] - -[[package]] -name = "axruntime" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79659a480ddcacf786119394000822567e47093ab5f01ef1952565cc5bcf256b" -dependencies = [ - "axalloc", - "axbacktrace", - "axconfig", - "axdriver", - "axerrno 0.2.2", - "axfs 0.1.0", - "axhal", - "axklib", - "axlog", - "axmm", - "axplat", - "axplat-aarch64-dyn", - "axplat-riscv64-qemu-virt 0.3.0", - "axplat-x86-qemu-q35", - "axtask", - "cfg-if", - "chrono", - "crate_interface 0.1.4", - "ctor_bare", - "indoc", - "percpu", -] - -[[package]] -name = "axsched" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad6b7b0b8d9ad1d52a834d8b7721114413da8cf3430af928b1c8651f911287a" -dependencies = [ - "linked_list_r4l", -] - -[[package]] -name = "axstd" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488fb97366c35a4591bd6ac8536e0d17fb32fd31f43f8e15a7fef98ca589293e" -dependencies = [ - "arceos_api", - "axerrno 0.2.2", - "axfeat", - "axio", - "kspin", - "lazyinit", - "lock_api", - "spin 0.10.0", -] - -[[package]] -name = "axsync" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdadc56f8fc6d471c977997b4865d423ebf8b69256f183475af511bb4749d251" -dependencies = [ - "axtask", - "event-listener", - "kspin", - "lock_api", -] - -[[package]] -name = "axtask" -version = "0.3.0-preview.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678fe49dac66ef02c5ed0a8657e5fbc74d40b66e29b9864f5e4325741c0ad878" -dependencies = [ - "axconfig", - "axerrno 0.2.2", - "axhal", - "axpoll", - "axsched", - "cfg-if", - "cpumask", - "crate_interface 0.1.4", - "event-listener", - "extern-trait", - "futures-util", - "kernel_guard", - "kspin", - "lazyinit", - "log", - "memory_addr", - "percpu", - "spin 0.10.0", -] - -[[package]] -name = "axum" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" -dependencies = [ - "axum-core", - "bytes", - "form_urlencoded", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "serde_core", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axvcpu" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "506c3ab0ce353e76d3279927b88a43ee0effd4368ce86e8bc72a0ee9430e0709" -dependencies = [ - "axaddrspace", - "axerrno 0.2.2", - "axvisor_api", - "memory_addr", - "percpu", -] - -[[package]] -name = "axvisor" -version = "0.3.0-preview.2" -dependencies = [ - "aarch64-cpu-ext", - "anyhow", - "arm-gic-driver 0.17.0", - "axaddrspace", - "axconfig", - "axdevice", - "axdevice_base", - "axdriver", - "axerrno 0.2.2", - "axhvc", - "axplat-riscv64-qemu-virt 0.3.0", - "axruntime", - "axklib", - "axplat-x86-qemu-q35", - "axstd", - "axvcpu", - "axvisor_api", - "axvm", - "axvmconfig", - "bitflags 2.11.0", - "byte-unit", - "cargo_metadata", - "cfg-if", - "chrono", - "clap", - "colored", - "cpumask", - "crate_interface 0.3.0", - "driver", - "extern-trait", - "fdt-parser", - "flate2", - "hashbrown 0.14.5", - "jkconfig", - "kernel_guard", - "kspin", - "lazy_static", - "lazyinit", - "log", - "memory_addr", - "page_table_entry 0.6.1", - "page_table_multiarch 0.6.1", - "percpu", - "phytium-mci", - "prettyplease", - "quote", - "rd-block", - "rdif-block", - "rdif-clk", - "rdif-intc", - "rdrive", - "riscv_vcpu", - "riscv_vplic", - "spin 0.10.0", - "syn 2.0.111", - "timer_list", - "tokio", - "toml", -] - -[[package]] -name = "axvisor_api" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7959b2dd8afbac2a0cda189986df6ceb753a92e985c28ce2c279cf432cbe2dfc" -dependencies = [ - "axaddrspace", - "axvisor_api_proc", - "cpumask", - "crate_interface 0.3.0", - "memory_addr", -] - -[[package]] -name = "axvisor_api_proc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5351791fec23f5545518f88d4d8a134c564085a6f37ed6953bbd4f35d4f32c7" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "axvm" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6406a71e7af560b7d544fb697f3071e045d304a20d8525502b5a906dbbaab" -dependencies = [ - "arm_vcpu", - "arm_vgic", - "axaddrspace", - "axdevice", - "axdevice_base", - "axerrno 0.2.2", - "axvcpu", - "axvisor_api", - "axvmconfig", - "cfg-if", - "cpumask", - "log", - "memory_addr", - "page_table_entry 0.6.1", - "page_table_multiarch 0.6.1", - "percpu", - "riscv_vcpu", - "spin 0.10.0", - "x86_vcpu", -] - -[[package]] -name = "axvmconfig" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35f2cf8bfcabc84e636f5cf3cd7b1c1ddcf1f584d6fa91791a572056204db35a" -dependencies = [ - "axerrno 0.2.2", - "clap", - "enumerable", - "env_logger", - "log", - "schemars", - "serde", - "serde_repr", - "toml", -] - -[[package]] -name = "bare-metal" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" - -[[package]] -name = "bare-test-macros" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e585a01076fee271c5aabcf36212acb349fb3e638561d842fffa8ca013f4fdd8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bit" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b645c5c09a7d4035949cfce1a915785aaad6f17800c35fda8a8c311c491f284" - -[[package]] -name = "bit_field" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" - -[[package]] -name = "bitmap-allocator" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11e01b72b2a40aab84454fb6b71090c2a6873e1b5ba22a8e3d74e83086a2304" -dependencies = [ - "bit_field", -] - -[[package]] -name = "bitmaps" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "borsh" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" -dependencies = [ - "borsh-derive", - "cfg_aliases", -] - -[[package]] -name = "borsh-derive" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" -dependencies = [ - "once_cell", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "buddy_system_allocator" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b672b945a3e4f4f40bfd4cd5ee07df9e796a42254ce7cd6d2599ad969244c44a" -dependencies = [ - "spin 0.10.0", -] - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "byte-unit" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6d47a4e2961fb8721bcfc54feae6455f2f64e7054f9bc67e875f0e77f4c58d" -dependencies = [ - "rust_decimal", - "schemars", - "serde", - "utf8-width", -] - -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta 0.1.4", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "bytemuck" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "camino" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" -dependencies = [ - "serde_core", -] - -[[package]] -name = "cap_access" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b24894fa5f73bbf9c72196e7f495a1f81d6218a548280a09ada4a937157692" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "cargo-platform" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "cargo_metadata" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror 2.0.18", -] - -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - -[[package]] -name = "cc" -version = "1.2.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "clap" -version = "4.5.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "clap_lex" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" - -[[package]] -name = "cmake" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" -dependencies = [ - "cc", -] - -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "colored" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "compact_str" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" -dependencies = [ - "encode_unicode", - "libc", - "once_cell", - "unicode-width 0.2.0", - "windows-sys 0.61.2", -] - -[[package]] -name = "const-default" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" - -[[package]] -name = "const-str" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18f12cc9948ed9604230cdddc7c86e270f9401ccbe3c2e98a4378c5e7632212f" - -[[package]] -name = "const_fn" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413d67b29ef1021b4d60f4aa1e925ca031751e213832b4b1d588fae623c05c60" - -[[package]] -name = "convert_case" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "cpumask" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4266f1bd910c087ff8c7848882217b2b079742877b21d29614c11c16087d70" -dependencies = [ - "bitmaps", -] - -[[package]] -name = "crate_interface" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70272a03a2cef15589bac05d3d15c023752f5f8f2da8be977d983a9d9e6250fb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "crate_interface" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e5c7109dea31fc91ab584e99752baa997f76d46e49bab0a17b5e9679248df7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "crc" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags 2.11.0", - "crossterm_winapi", - "mio", - "parking_lot", - "rustix 0.38.44", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" -dependencies = [ - "bitflags 2.11.0", - "crossterm_winapi", - "derive_more", - "document-features", - "futures-core", - "mio", - "parking_lot", - "rustix 1.1.4", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ctor_bare" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e5ae3c454dc1efb0e5821dc17344539849391b2de18c89596ea563f1909f93" -dependencies = [ - "ctor_bare_macros", -] - -[[package]] -name = "ctor_bare_macros" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49d5cd78b1c748184d41407b14a58af8403c13328ff2b9f49b0a418c24e3ff" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "cursive" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "386d5a36020bb856e9a34ecb8a4e6c9bd6b0262d1857bae4db7bc7e2fdaa532e" -dependencies = [ - "ahash 0.8.12", - "cfg-if", - "crossbeam-channel", - "crossterm 0.28.1", - "cursive_core", - "lazy_static", - "libc", - "log", - "signal-hook", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "cursive-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac7ac0eb0cede3dfdfebf4d5f22354e05a730b79c25fd03481fc69fcfba0a73e" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "cursive_core" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321ec774d27fafc66e812034d0025f8858bd7d9095304ff8fc200e0b9f9cc257" -dependencies = [ - "ahash 0.8.12", - "compact_str", - "crossbeam-channel", - "cursive-macros", - "enum-map", - "enumset", - "lazy_static", - "log", - "num", - "parking_lot", - "serde_json", - "time", - "unicode-segmentation", - "unicode-width 0.1.14", - "xi-unicode", -] - -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core 0.20.11", - "darling_macro 0.20.11", -] - -[[package]] -name = "darling" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", -] - -[[package]] -name = "darling" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" -dependencies = [ - "darling_core 0.23.0", - "darling_macro 0.23.0", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] - -[[package]] -name = "darling_core" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] - -[[package]] -name = "darling_core" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core 0.20.11", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" -dependencies = [ - "darling_core 0.21.3", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" -dependencies = [ - "darling_core 0.23.0", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case 0.10.0", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.117", - "unicode-xid", -] - -[[package]] -name = "device_tree" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18f717c5c7c2e3483feb64cccebd077245ad6d19007c2db0fd341d38595353c" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "dma-api" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624f4a84cc1031cfceb56780b82570a785f6cfdcee4f34c06c4e8f1fba25c970" - -[[package]] -name = "dma-api" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d51274fe946b4c27cdada87cc6de09a50c0d9192c0f30ba455b29dae09caa0b" -dependencies = [ - "aarch64-cpu-ext", -] - -[[package]] -name = "dma-api" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3816a852c6c71653e1941cfe22f215eca41c14fd45ec9b32c0fa82fe57fe0989" -dependencies = [ - "aarch64-cpu-ext", - "cfg-if", - "spin 0.10.0", - "thiserror 2.0.18", -] - -[[package]] -name = "dma-api" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e9ce2657334f873c12845789d5a4f1b34bef91fe531437284b8387d77bd925" -dependencies = [ - "aarch64-cpu-ext", - "cfg-if", - "derive_more", - "log", - "mbarrier", - "spin 0.10.0", - "thiserror 2.0.18", -] - -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - -[[package]] -name = "driver" -version = "0.1.0" -dependencies = [ - "axklib", - "log", - "phytium-mci", - "rdif-block", - "rdif-clk", - "rdrive", - "rk3568_clk", - "rk3588-clk", - "rockchip-pm", - "sdmmc", - "spin 0.10.0", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "embedded-hal" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" - -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enum-map" -version = "2.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" -dependencies = [ - "enum-map-derive", -] - -[[package]] -name = "enum-map-derive" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "enum_dispatch" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "enumerable" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05481ae0c653c7de79f2f62d515b09049fed59cbfc5755a4afe55c95181492b" -dependencies = [ - "enumerable_derive", -] - -[[package]] -name = "enumerable_derive" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef47b28174f7ef4ccb124c8766565006263114c1cc129cd119a66f1e15635125" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "enumn" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "enumset" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" -dependencies = [ - "darling 0.21.3", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "env_filter" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "pin-project-lite", -] - -[[package]] -name = "extern-trait" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3bc9641ace0d852a93f2699f342c935b825146acb63049b92d06e57aa9aebd" -dependencies = [ - "extern-trait-impl", - "typeid", -] - -[[package]] -name = "extern-trait-impl" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89ddc8cd109db4196b4751cce7fc34380d55f095fcd5d634c708485fe4c820f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "fdt-edit" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda8ff88f0dd8770b247b17d9e0faf293c52cb5733e2500294ffa93ac1f0b384" -dependencies = [ - "enum_dispatch", - "fdt-raw", - "log", -] - -[[package]] -name = "fatfs" -version = "0.4.0" -source = "git+https://github.com/rafalh/rust-fatfs?rev=4eccb50#4eccb50d011146fbed20e133d33b22f3c27292e7" -dependencies = [ - "bitflags 2.10.0", - "log", -] - -[[package]] -name = "fdt-parser" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f95f0bda5ff920492f6573294d8e3a99b75ee2e5ef93ab313fc6d517fa46785" - -[[package]] -name = "fdt-raw" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e6747052012035c3473585be1d706207893d8db73101825d8a3f75effa9843" -dependencies = [ - "heapless", - "log", - "thiserror 2.0.18", -] - -[[package]] -name = "filetime" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" -dependencies = [ - "cfg-if", - "libc", - "libredox", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "fitimage" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e99ae4a85d0135904ef62da3dfb2b4cc90846622a7b8dd5c7a0cd5fea79b10cf" -dependencies = [ - "anyhow", - "byteorder", - "crc", - "device_tree", - "flate2", - "hex", - "md5", - "serde", - "sha1", - "thiserror 2.0.18", - "vm-fdt", -] - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "libz-sys", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "handler_table" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702cb690200d6303c1e1992bc648f3f3bf9c1d6a27fcf50551c513d61f339c99" - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash 0.8.12", - "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "heapless" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", -] - -[[package]] -name = "indicatif" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" -dependencies = [ - "console", - "portable-atomic", - "unicode-width 0.2.0", - "unit-prefix", - "web-time", -] - -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - -[[package]] -name = "instability" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" -dependencies = [ - "darling 0.23.0", - "indoc", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "int_ratio" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6045ea39e8d2862506c0dff6c65d068da362335df698bb1634033492740d2170" - -[[package]] -name = "io-kit-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" -dependencies = [ - "core-foundation-sys", - "mach2", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "iri-string" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" - -[[package]] -name = "jiff" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" -dependencies = [ - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", -] - -[[package]] -name = "jiff-static" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "jkconfig" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8af2ca0c0ea6ee42a80e380d44e2a18a06185484236a8af0cf766682b17176a" -dependencies = [ - "anyhow", - "axum", - "cargo_metadata", - "chrono", - "clap", - "cursive", - "log", - "schemars", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "toml", - "tower", - "tower-http", -] - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "kasm-aarch64" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791dc7a2b079d81b8e3615521fccbd75c0c9f068b53f7d891a2e300222c7cada" -dependencies = [ - "darling 0.20.11", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "kernel_guard" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d10c55bedf6789bc3748e0d8756ee639df1ae25144fd3525ed311044bd9a739f" -dependencies = [ - "cfg-if", - "crate_interface 0.1.4", -] - -[[package]] -name = "kernutil" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baabe1075575d14a32c232dbe55699a2908326f0b2022243ae4a069ff02ccb9a" -dependencies = [ - "heapless", - "num-align", - "ranges-ext", -] - -[[package]] -name = "kspin" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d841fd3aeaa9a55871068f5c3ce48497a6dbcf14e20ca7784a9f68bfdb4c825" -dependencies = [ - "cfg-if", - "kernel_guard", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin 0.9.8", -] - -[[package]] -name = "lazyinit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f03abfebdaaf0fad16790237a0348baf84886d3ade460db13bae59e614a180" - -[[package]] -name = "lenient_semver" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de8de3f4f3754c280ce1c8c42ed8dd26a9c8385c2e5ad4ec5a77e774cea9c1ec" -dependencies = [ - "lenient_semver_parser", - "semver", -] - -[[package]] -name = "lenient_semver_parser" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f650c1d024ddc26b4bb79c3076b30030f2cf2b18292af698c81f7337a64d7d6" -dependencies = [ - "lenient_semver_version_builder", - "semver", -] - -[[package]] -name = "lenient_semver_version_builder" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9049f8ff49f75b946f95557148e70230499c8a642bf2d6528246afc7d0282d17" -dependencies = [ - "semver", -] - -[[package]] -name = "libc" -version = "0.2.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" - -[[package]] -name = "libredox" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" -dependencies = [ - "bitflags 2.11.0", - "libc", - "plain", - "redox_syscall 0.7.3", -] - -[[package]] -name = "libudev" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" -dependencies = [ - "libc", - "libudev-sys", -] - -[[package]] -name = "libudev-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "libz-sys" -version = "1.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52f4c29e2a68ac30c9087e1b772dc9f44a2b66ed44edf2266cf2be9b03dafc1" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked_list_r4l" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1730c4ce817dc3edb092739ca5c109fe551018e5ea5a8361a8ddaa13d79ac8ed" - -[[package]] -name = "linkme" -version = "0.3.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e3283ed2d0e50c06dd8602e0ab319bb048b6325d0bba739db64ed8205179898" -dependencies = [ - "linkme-impl", -] - -[[package]] -name = "linkme-impl" -version = "0.3.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5cec0ec4228b4853bb129c84dbf093a27e6c7a20526da046defc334a1b017f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "loongArch64" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9f0d275c70310e2a9d2fc23250c5ac826a73fa828a5f256401f85c5c554283" -dependencies = [ - "bit_field", - "bitflags 2.11.0", -] - -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "lzma-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" -dependencies = [ - "byteorder", - "crc", -] - -[[package]] -name = "mach2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" -dependencies = [ - "libc", -] - -[[package]] -name = "matchit" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" - -[[package]] -name = "mbarrier" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9257ea6fe9726d1f1a67fddbda4c06cc97b4fb18716b78ec03ba05e29d625e28" - -[[package]] -name = "md5" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memory_addr" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f0625c50adb5f6aaf47f05cae3c4dbc13a74c659241b06c4576f3d7e1da940" - -[[package]] -name = "memory_set" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50a49ecd4114cf87f7e442ec5dd03bd590e7094541f987057310dbb32a6341ad" -dependencies = [ - "axerrno 0.1.2", - "memory_addr", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "mmio-api" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c7c68327571e1807adf7b5bdf7809a281402f879fc641f0e051fb0b960a41e" -dependencies = [ - "derive_more", - "thiserror 2.0.18", -] - -[[package]] -name = "multiboot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87ad3b7b7bcf5da525c22221e3eb3a020cd68b2d55ae62f629c15e8bc3bd56e" -dependencies = [ - "paste", -] - -[[package]] -name = "nb" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" - -[[package]] -name = "network-interface" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddcb8865ad3d9950f22f42ffa0ef0aecbfbf191867b3122413602b0a360b2a6" -dependencies = [ - "cc", - "libc", - "thiserror 2.0.18", - "winapi", -] - -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", -] - -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-align" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4b86e8ef968de2261141fc760ee57cae8fabb3a0e756b3390a4c4871b16c3d1" - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - -[[package]] -name = "numeric-enum-macro" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e4bdb6b46b592948e700ea1ef24a4296491f6a0ee722b258040abd15a3714" - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "flate2", - "memchr", - "ruzstd", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "ostool" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "906b3258004531f252829818fd568c1e2d7345463c2c7a550d0953d071988a5b" -dependencies = [ - "anyhow", - "byte-unit", - "cargo_metadata", - "clap", - "colored", - "crossterm 0.29.0", - "cursive", - "env_logger", - "fitimage", - "futures", - "indicatif", - "jkconfig", - "log", - "lzma-rs", - "network-interface", - "object", - "ratatui", - "regex", - "reqwest 0.12.28", - "schemars", - "serde", - "serde_json", - "serialport", - "sha2", - "tar", - "tftpd", - "tokio", - "toml", - "uboot-shell", - "ureq", -] - -[[package]] -name = "page-table-generic" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062719187d8cadefaabc0c07c12bd486f8e88760fc02b756da3053f42dff0b4d" -dependencies = [ - "bitflags 2.11.0", - "heapless", - "log", - "num-align", - "thiserror 2.0.18", -] - -[[package]] -name = "page_table_entry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9a63b9b86d32f64c3874a90936939281d045ef1751d0aca3d82d5e4e06b2ef" -dependencies = [ - "aarch64-cpu 11.2.0", - "bitflags 2.11.0", - "memory_addr", - "x86_64", -] - -[[package]] -name = "page_table_multiarch" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42c5b75d5d9bdbee44c827b0dd2766fa3d478a76b9c6735419228089d1b24536" -dependencies = [ - "arrayvec", - "log", - "memory_addr", - "page_table_entry 0.6.1", - "riscv 0.16.0", - "x86", -] - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.18", - "smallvec", - "windows-link", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pci_types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c2a105c657261a938ff68ee231c199a3d80eef33976004829de761ef5b1a9b" -dependencies = [ - "bit_field", - "bitflags 2.11.0", -] - -[[package]] -name = "pcie" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98b1339d53c73a04833791fa907b60ff074b95cae8db9f94f3173ed2b30b5b6c" -dependencies = [ - "bare-test-macros", - "bit_field", - "bitflags 2.11.0", - "log", - "pci_types", - "rdif-pcie", - "thiserror 2.0.18", -] - -[[package]] -name = "pcie" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b092a434cb4a9dff1321bc486f0d324ce2425f217eb1fe43d34b1d21b03294" -dependencies = [ - "bit_field", - "bitflags 2.11.0", - "log", - "mmio-api", - "pci_types", - "rdif-pcie", - "thiserror 2.0.18", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "percpu" -version = "0.2.3-preview.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c03ecfaf97c11a791d8b65e34a6353d012a735a5cbfebba34ee6668da16ce38" -dependencies = [ - "cfg-if", - "percpu_macros", - "spin 0.10.0", - "x86", -] - -[[package]] -name = "percpu_macros" -version = "0.2.3-preview.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6660d83b91174e6d39fae0cdf893889dcdbffda6e99664f8ee8a45fde6a6936c" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "phytium-mci" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca870f654bd7302211a92507bd8301f66f83d1e5c97236de79dab7a6d41fcbf1" -dependencies = [ - "bare-test-macros", - "bitflags 2.11.0", - "bytemuck", - "dma-api 0.2.2", - "lazy_static", - "log", - "nb", - "rlsf", - "spin 0.10.0", - "tock-registers 0.9.0", -] - -[[package]] -name = "pie-boot-if" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d979b0d1208dd8a57c5adb7d3c4e07bf15cbea3840123e864f6bfcb655c5e05" -dependencies = [ - "heapless 0.8.0", -] - -[[package]] -name = "pie-boot-loader-aarch64" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de8836eb8759cd65e70c73dc0f519345d8a734284e8e4cfc5889a6e445af9f09" -dependencies = [ - "aarch64-cpu 10.0.0", - "aarch64-cpu-ext", - "any-uart", - "bitflags 2.10.0", - "fdt-parser", - "kasm-aarch64", - "kdef-pgtable", - "log", - "num-align", - "page-table-generic", - "pie-boot-if", - "prettyplease", - "quote", - "spin 0.10.0", - "syn 2.0.111", - "thiserror 2.0.17", -] - -[[package]] -name = "pie-boot-macros" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513f5ca7603771d7524bfb7e6ba8dded83d2e8ac02d46f15815e0c1144bca566" -dependencies = [ - "darling 0.20.11", - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - -[[package]] -name = "portable-atomic" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - -[[package]] -name = "portable-atomic-util" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy 0.8.42", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit 0.25.4+spec-1.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive 0.1.4", -] - -[[package]] -name = "ptr_meta" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79" -dependencies = [ - "ptr_meta_derive 0.3.1", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "range-alloc-arceos" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187083342c080f6150a1e8e6e6a687b49239df195aaf866ff4ed535961ab860e" - -[[package]] -name = "ranges-ext" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dbbaa030b3eec58088755e56d3acb775fc74f4726565eda8633a415bfc44e0" -dependencies = [ - "heapless", - "thiserror 2.0.18", -] - -[[package]] -name = "ratatui" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" -dependencies = [ - "bitflags 2.11.0", - "cassowary", - "compact_str", - "crossterm 0.28.1", - "indoc", - "instability", - "itertools", - "lru", - "paste", - "strum 0.26.3", - "unicode-segmentation", - "unicode-truncate", - "unicode-width 0.2.0", -] - -[[package]] -name = "raw-cpuid" -version = "10.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "raw-cpuid" -version = "11.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "rd-block" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c20952f27dc5aaf853e8c00026ba7f2633e149ce47f9a58115704b342756307" -dependencies = [ - "dma-api 0.7.1", - "futures", - "rdif-block", - "spin_on", -] - -[[package]] -name = "rdif-base" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f8c32d8cbc18633a412130719b07d31135215d1715ac48fc3888ca835a811ba" -dependencies = [ - "as-any", - "async-trait", - "paste", - "rdif-def", - "thiserror 2.0.18", -] - -[[package]] -name = "rdif-base" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda8c6ace8e0124ec777028cf86a56049f039c5d6f7a19b307a3f564b01ed858" -dependencies = [ - "as-any", - "async-trait", - "paste", - "rdif-def", - "thiserror 2.0.18", -] - -[[package]] -name = "rdif-block" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2a2573c1fb0af5daf596a32b9e10284f1fc018adfd0413e2213f35151ebff1" -dependencies = [ - "dma-api 0.7.1", - "rdif-base 0.8.0", - "thiserror 2.0.18", -] - -[[package]] -name = "rdif-clk" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b043140cf3c809e5904b5205f7b7205283867da44a44a29820916df08abc362" -dependencies = [ - "rdif-base 0.8.0", -] - -[[package]] -name = "rdif-def" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e44a12e44c2f439be06af72a3e03d4e57f6c79211a68058158a02670e63853c" -dependencies = [ - "thiserror 2.0.18", -] - -[[package]] -name = "rdif-intc" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f3b9bf119dea83dcc7e4f0e2487d7635d373f55500bde982defa934dd17e9c" -dependencies = [ - "cfg-if", - "rdif-base 0.8.0", -] - -[[package]] -name = "rdif-pcie" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ab8ecf37d86123500a1d99519424b7dcf2489c0df6b62b56af9f436224259f" -dependencies = [ - "pci_types", - "rdif-base 0.8.0", - "thiserror 2.0.18", -] - -[[package]] -name = "rdif-serial" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6668610226177053d66d9d3e55436cfa44f6428a5b755b9d7593288b738e3f12" -dependencies = [ - "bitflags 2.11.0", - "futures", - "heapless", - "rdif-base 0.7.0", - "spin 0.10.0", - "thiserror 2.0.18", -] - -[[package]] -name = "rdrive" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9bdca8365115779b803244844c4db3c8e2ae3355edbdb5ce6547de540b29dd2" -dependencies = [ - "fdt-edit", - "fdt-raw", - "log", - "mmio-api", - "paste", - "pcie 0.6.0", - "rdif-base 0.8.0", - "rdif-pcie", - "rdrive-macros", - "spin 0.10.0", - "thiserror 2.0.18", -] - -[[package]] -name = "rdrive-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab3105c9af32e901a2adc7d920b39ff8b6ee0f6f0b7dfdeaf18f306ec12606f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "redox_syscall" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", -] - -[[package]] -name = "reqwest" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-core", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "mime", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "rgb" -version = "0.8.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "riscv" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1671c79a01a149fe000af2429ce9ccc8e58cdecda72672355d50e5536b363c" -dependencies = [ - "critical-section", - "embedded-hal", - "paste", - "riscv-macros 0.2.0", - "riscv-pac", -] - -[[package]] -name = "riscv" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9251433e48c39d2133cbaff3ae7809ce6a1ecbc8225ca7da33d96d10cf360582" -dependencies = [ - "critical-section", - "embedded-hal", - "paste", - "riscv-types", -] - -[[package]] -name = "riscv-decode" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b59d645e392e041ad18f5e529ed13242d8405c66bb192f59703ea2137017d0" - -[[package]] -name = "riscv-h" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bf93901d1af87f984c7be7a348cd27571e1f6f29293b8f3b3193f4c40d4d40" -dependencies = [ - "bare-metal", - "bit_field", - "bitflags 2.11.0", - "log", - "riscv 0.14.0", -] - -[[package]] -name = "riscv-macros" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c4aa1ea1af6dcc83a61be12e8189f9b293c3ba5a487778a4cd89fb060fdbbc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "riscv-macros" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d47d1fb716349455b8e5e3ebbf1eff95344dbdf9f782a4e1359d2f16f51e3dce" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "riscv-pac" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" - -[[package]] -name = "riscv-types" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3f2ad9f15a07f4a0e1677124f9120ce7e83ab7e1ca7186af0ca9da529b62e80" - -[[package]] -name = "riscv_goldfish" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07aac72f95e774476db82916d79f2d303191310393830573c1ab5c821b21660a" - -[[package]] -name = "riscv_plic" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e701d1c6ea06c35a19cb80d213fab87d264798f9bac0aed2730c0e86d297394a" -dependencies = [ - "tock-registers 0.10.1", -] - -[[package]] -name = "riscv_vcpu" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f7d192465c7a3e2f674c56d3e49cfeb00f84d0616137cbecb8aacb250e40c53" -dependencies = [ - "axaddrspace", - "axerrno 0.2.2", - "axvcpu", - "axvisor_api", - "bit_field", - "bitflags 2.11.0", - "cfg-if", - "crate_interface 0.3.0", - "log", - "memoffset", - "memory_addr", - "page_table_entry 0.6.1", - "riscv 0.14.0", - "riscv-decode", - "riscv-h 0.2.0", - "rustsbi", - "sbi-rt", - "sbi-spec", - "tock-registers 0.10.1", -] - -[[package]] -name = "riscv_vplic" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec84bba7d814f072afdd11a61c64ad55bedaeb17937a78bb6b9cec44b63a70fb" -dependencies = [ - "axaddrspace", - "axdevice_base", - "axerrno 0.2.2", - "axvisor_api", - "bitmaps", - "log", - "riscv-h", - "spin 0.10.0", -] - -[[package]] -name = "riscv_vplic" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193b9d354fe0680a7e151b069929ab7e5722519b5623b24f2bf0211236742a5f" -dependencies = [ - "axaddrspace", - "axdevice_base", - "axerrno 0.1.2", - "axvisor_api", - "bitmaps", - "log", - "riscv-h 0.1.0", - "spin 0.9.8", -] - -[[package]] -name = "rk3568_clk" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9786b01d79acb045652d4cb451b049b26a509171d4afe5c19767d06a366585" -dependencies = [ - "aarch64-cpu 10.0.0", - "bare-test-macros", - "fdt-parser", - "kspin", - "log", -] - -[[package]] -name = "rk3588-clk" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df59d8451a4f331a53fbd7c0c349cf4e844ebc051b7081342341a85910d7ac08" -dependencies = [ - "bare-test-macros", - "log", - "tock-registers 0.10.1", -] - -[[package]] -name = "rkyv" -version = "0.7.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta 0.1.4", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "rlsf" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1646a59a9734b8b7a0ac51689388a60fe1625d4b956348e9de07591a1478457a" -dependencies = [ - "cfg-if", - "const-default", - "libc", - "rustversion", - "svgbobdoc", -] - -[[package]] -name = "rockchip-pm" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9e2f942ea3a4bf3443b3abb70cf3c179274fb1ef4894c1426289fef959ed4c" -dependencies = [ - "bare-test-macros", - "dma-api 0.5.2", - "log", - "mbarrier", - "rdif-base 0.7.0", - "tock-registers 0.10.1", -] - -[[package]] -name = "rsext4" -version = "0.1.0-pre.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c9e89b7c4314992d8b2f6147e8637cc7a578a8d0df083e516bde4b23a0b25d" -dependencies = [ - "bitflags 2.11.0", - "lazy_static", - "log", -] - -[[package]] -name = "rust_decimal" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f703d19852dbf87cbc513643fa81428361eb6940f1ac14fd58155d295a3eb0" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand 0.8.5", - "rkyv", - "serde", - "serde_json", -] - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.11.0", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.11.0", - "errno", - "libc", - "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" -dependencies = [ - "aws-lc-rs", - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustsbi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c13763120794ed11d64bac885fb31d384ae385c3287b0697711b97affbf8ab" -dependencies = [ - "rustsbi-macros", - "sbi-rt", - "sbi-spec", -] - -[[package]] -name = "rustsbi-macros" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71347da9582cc6b6f3652c7d2c06516c9555690b3738ecdff7e84297f4e17fc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ruzstd" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ff0cc5e135c8870a775d3320910cd9b564ec036b4dc0b8741629020be63f01" -dependencies = [ - "twox-hash", -] - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "sbi-rt" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbaa69be1eedc61c426e6d489b2260482e928b465360576900d52d496a58bd0" -dependencies = [ - "sbi-spec", -] - -[[package]] -name = "sbi-spec" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e36312fb5ddc10d08ecdc65187402baba4ac34585cb9d1b78522ae2358d890" - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "schemars" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" -dependencies = [ - "dyn-clone", - "ref-cast", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.117", -] - -[[package]] -name = "scope-local" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a7d5ed5013e6436fcd78f2bcd3892a6286ef9ce6c9b61504d4c4a08d6a40eab" -dependencies = [ - "percpu", - "spin 0.10.0", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sdmmc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0dbda75ab3bc25f210eb7ad16f2bd1f877a05444e1aeea083c60221bf76f15" -dependencies = [ - "aarch64-cpu 10.0.0", - "arm_pl011", - "bare-test-macros", - "bitflags 2.11.0", - "cfg-if", - "dma-api 0.3.1", - "fdt-parser", - "kspin", - "log", - "paste", - "smccc", - "spin 0.10.0", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_repr" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_spanned" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serialport" -version = "4.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acaf3f973e8616d7ceac415f53fc60e190b2a686fbcf8d27d0256c741c5007b" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "core-foundation 0.10.1", - "core-foundation-sys", - "io-kit-sys", - "libudev", - "mach2", - "nix", - "scopeguard", - "unescaper", - "winapi", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "simd-adler32" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" - -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "smccc" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c73e0ca8c566478040487791c9f488f86c5aec846ca1ab18484be8a1d8c55cd" -dependencies = [ - "thiserror 2.0.18", -] - -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "some-serial" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ad586f110c679176859fe111f2c3dd176b95bbe731e9b4c3d1c6382cb301c9" -dependencies = [ - "bare-test-macros", - "bitflags 2.11.0", - "dma-api 0.5.2", - "enum_dispatch", - "heapless", - "log", - "mbarrier", - "rdif-serial", - "thiserror 2.0.18", - "tock-registers 0.10.1", - "x86", -] - -[[package]] -name = "someboot" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afbf93ac5c9501d93d1496f58ff089ac3aa02648adedad5777a767bcfd448ad" -dependencies = [ - "aarch64-cpu 11.2.0", - "aarch64-cpu-ext", - "acpi", - "aml", - "ansi_rgb", - "anyhow", - "arrayvec", - "bit_field", - "bitflags 2.11.0", - "buddy_system_allocator", - "byte-unit", - "byteorder", - "derive_more", - "fdt-edit", - "fdt-raw", - "heapless", - "kasm-aarch64", - "kernutil", - "log", - "loongArch64", - "num-align", - "numeric-enum-macro", - "page-table-generic", - "prettyplease", - "quote", - "ranges-ext", - "rgb", - "smccc", - "some-serial", - "somehal-macros", - "spin 0.10.0", - "syn 2.0.117", - "thiserror 2.0.18", - "tock-registers 0.10.1", - "uefi", - "uguid", -] - -[[package]] -name = "somehal" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2e441db2f693b803ee5fdf85b89bcc1830c83ae483406d4f0bb74cf90473a6" -dependencies = [ - "aarch64-cpu 10.0.0", - "aarch64-cpu-ext", - "any-uart", - "bindeps-simple", - "fdt-parser", - "futures", - "heapless 0.8.0", - "kasm-aarch64", - "kdef-pgtable", - "log", - "mmio-api", - "page-table-generic", - "rdif-intc", - "rdrive", - "someboot", - "somehal-macros", - "spin 0.10.0", - "thiserror 2.0.18", - "tock-registers 0.10.1", -] - -[[package]] -name = "somehal-macros" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e26764471ab7fe1c7cafcc1505b61ed59a3e9d55d4aa4a3d42648f0d4638d78" -dependencies = [ - "darling 0.21.3", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spin_on" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076e103ed41b9864aa838287efe5f4e3a7a0362dd00671ae62a212e5e4612da2" -dependencies = [ - "pin-utils", -] - -[[package]] -name = "spinning_top" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spinning_top" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros 0.27.2", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.117", -] - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "svgbobdoc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" -dependencies = [ - "base64 0.13.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-width 0.1.14", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tar" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" -dependencies = [ - "filetime", - "libc", - "xattr", -] - -[[package]] -name = "tftpd" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee6f92408c23beea910b60784f6dc323b488c477bf6fa88aff8083675eda26c" - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "timer_list" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158b52ace9609dd94f4af338f4828ff23b600d9160def8c001f2c73885521936" - -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tock-registers" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" - -[[package]] -name = "tock-registers" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b9e2fdb3a1e862c0661768b7ed25390811df1947a8acbfbefe09b47078d93c4" - -[[package]] -name = "tock-registers" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2d250f87fb3fb6f225c907cf54381509f47b40b74b1d1f12d2dccbc915bdfe" - -[[package]] -name = "tokio" -version = "1.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.9.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" -dependencies = [ - "indexmap", - "serde_core", - "serde_spanned", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" - -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_datetime" -version = "1.0.0+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "toml_datetime 0.6.11", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.25.4+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" -dependencies = [ - "indexmap", - "toml_datetime 1.0.0+spec-1.1.0", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.9+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" -dependencies = [ - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "toml_writer" -version = "1.0.6+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags 2.11.0", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "http-range-header", - "httpdate", - "iri-string", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "tokio", - "tokio-util", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "log", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "trait-ffi" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87d49469ee333631b3130bec28965c47dcf0d4f3a792f8ed425dd036cf84be7" -dependencies = [ - "convert_case 0.8.0", - "lenient_semver", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "twox-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" - -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "uart_16550" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d293f51425981fdb1b766beae254dbb711a17e8c4b549dc69b9b7ee0d478d5" -dependencies = [ - "bitflags 2.11.0", - "rustversion", - "x86", -] - -[[package]] -name = "uboot-shell" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5fe43e54f4e6cf96cd577556bd0cd1726b135a2563e19ca3ed8f98fa163b5" -dependencies = [ - "colored", - "log", -] - -[[package]] -name = "ucs2" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79298e11f316400c57ec268f3c2c29ac3c4d4777687955cd3d4f3a35ce7eba" -dependencies = [ - "bit_field", -] - -[[package]] -name = "uefi" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe9058b73ee2b6559524af9e33199c13b2485ddbf3ad1181b68051cdc50c17" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "log", - "ptr_meta 0.3.1", - "ucs2", - "uefi-macros", - "uefi-raw", - "uguid", -] - -[[package]] -name = "uefi-macros" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4687412b5ac74d245d5bfb1733ede50c31be19bf8a4b6a967a29b451bab49e67" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "uefi-raw" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f64fe59e11af447d12fd60a403c74106eb104309f34b4c6dbce6e927d97da9d" -dependencies = [ - "bitflags 2.11.0", - "uguid", -] - -[[package]] -name = "uguid" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8352f8c05e47892e7eaf13b34abd76a7f4aeaf817b716e88789381927f199c" - -[[package]] -name = "unescaper" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4064ed685c487dbc25bd3f0e9548f2e34bab9d18cefc700f9ec2dba74ba1138e" -dependencies = [ - "thiserror 2.0.18", -] - -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "unit-prefix" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "ureq" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" -dependencies = [ - "base64 0.22.1", - "flate2", - "log", - "percent-encoding", - "rustls", - "rustls-pki-types", - "ureq-proto", - "utf-8", - "webpki-roots", -] - -[[package]] -name = "ureq-proto" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" -dependencies = [ - "base64 0.22.1", - "http", - "httparse", - "log", -] - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "uuid" -version = "1.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "virtio-drivers" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a39747311dabb3d37807037ed1c3c38d39f99198d091b5b79ecd5c8d82f799" -dependencies = [ - "bitflags 2.11.0", - "enumn", - "log", - "zerocopy 0.7.35", -] - -[[package]] -name = "vm-fdt" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e21282841a059bb62627ce8441c491f09603622cd5a21c43bfedc85a2952f23" - -[[package]] -name = "volatile" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" -dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" - -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x2apic" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db5cbcb7faedfa15f90376004ffc0cb42e427623ab56629f0073d275ee8e7043" -dependencies = [ - "bit", - "bitflags 1.3.2", - "paste", - "raw-cpuid 10.7.0", - "x86_64", -] - -[[package]] -name = "x86" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" -dependencies = [ - "bit_field", - "bitflags 1.3.2", - "raw-cpuid 10.7.0", -] - -[[package]] -name = "x86_64" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7841fa0098ceb15c567d93d3fae292c49e10a7662b4936d5f6a9728594555ba" -dependencies = [ - "bit_field", - "bitflags 2.11.0", - "const_fn", - "rustversion", - "volatile", -] - -[[package]] -name = "x86_vcpu" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "906f6ead2156dde21b712f0eb654b620d1c2652e62b6b0731a7807a565454692" -dependencies = [ - "axaddrspace", - "axdevice_base", - "axerrno 0.2.2", - "axvcpu", - "axvisor_api", - "bit_field", - "bitflags 2.11.0", - "cfg-if", - "crate_interface 0.3.0", - "log", - "memory_addr", - "numeric-enum-macro", - "page_table_entry 0.6.1", - "paste", - "raw-cpuid 11.6.0", - "spin 0.10.0", - "x86", - "x86_64", - "x86_vlapic", -] - -[[package]] -name = "x86_vlapic" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73364dacf1435c64e3395567405311456c3087f7bb4852904332e0fa1379eb65" -dependencies = [ - "axaddrspace", - "axdevice_base", - "axerrno 0.2.2", - "axvisor_api", - "bit", - "log", - "memory_addr", - "paste", - "tock-registers 0.10.1", -] - -[[package]] -name = "xattr" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" -dependencies = [ - "libc", - "rustix 1.1.4", -] - -[[package]] -name = "xi-unicode" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" - -[[package]] -name = "yoke" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" -dependencies = [ - "zerocopy-derive 0.8.42", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/os/axvisor/Cargo.toml b/os/axvisor/Cargo.toml deleted file mode 100644 index 4e1a8dfa3..000000000 --- a/os/axvisor/Cargo.toml +++ /dev/null @@ -1,170 +0,0 @@ -[package] -name = "axvisor" -authors = ["Keyang Hu ", "周睿 "] -edition = "2024" -license = "Apache-2.0" -version = "0.3.0-preview.2" -description = "A lightweight type-1 hypervisor based on ArceOS" -repository = "https://github.com/arceos-hypervisor/axvisor" -homepage = "https://github.com/arceos-hypervisor/axvisor" -documentation = "https://docs.rs/axvisor" -readme = "README.md" -keywords = ["hypervisor", "virtualization", "arceos", "type-1"] -categories = ["os", "no-std", "embedded"] -include = [ - ".cargo/config.toml", - ".github/*/**", - "doc/**", - "src/**", - "build.rs", - "configs/**", - "scripts/**", - "xtask/src/**", - "rust-toolchain.toml", - "README.md", - "LICENSE*", -] - -[profile.release] -lto = true - -[[bin]] -name = "xtask" -path = "xtask/src/main.rs" -required-features = ["xtask"] - -[features] -default = [] -ept-level-4 = [] -fs = ["axstd/fs"] -dyn-plat = ["axstd/plat-dyn"] -# Driver features (from former driver crate) -rk3588-clk = ["dep:rk3588-clk"] -sdmmc = ["dep:sdmmc"] -rockchip-pm = ["dep:rockchip-pm"] -phytium-blk = ["dep:phytium-mci"] -xtask = [ - "dep:anyhow", - "dep:axvmconfig", - "dep:cargo_metadata", - "dep:chrono", - "dep:clap", - "dep:colored", - "dep:flate2", - "dep:jkconfig", - "dep:ostool", - "dep:regex", - "dep:reqwest", - "dep:schemars", - "dep:serde", - "dep:serde_json", - "dep:sha2", - "dep:tar", - "dep:tokio", - "dep:toml", -] - -[target.'cfg(not(any(windows, unix)))'.dependencies] -bitflags = "2.2" -cfg-if = "1.0" -cpumask = "0.1.0" -kernel_guard = "0.1" -kspin = "0.1" -lazy_static = {version = "1.5", default-features = false, features = ["spin_no_std"]} -lazyinit = "0.2" -log = "0.4" -spin = "0.10" -timer_list = "0.1.0" -hashbrown = "0.14" - -# System dependent modules provided by ArceOS. -axstd = { version = "=0.3.0-preview.3", features = [ - "alloc-level-1", - "paging", - "irq", - "multitask", - "task-ext", - "smp", - "hv", -]} - -# System dependent modules provided by ArceOS-Hypervisor (bare-metal only) -axaddrspace = "0.3.0" -axhvc = "0.2.0" -# axruntime = { version = "=0.3.0-preview.1", features = ["alloc", "irq", "paging", "smp", "multitask"] } -axvcpu = "0.3.0" -axvm = "0.3.0" -axdevice = "0.2.2" -axdevice_base = "0.2.2" -axvisor_api = "0.3.0" - -# System independent crates provided by ArceOS -axerrno = "0.2.2" -axklib = "0.3.0" -# axcpu = { version = "0.3.0-preview.8", features = ["arm-el2"] } -byte-unit = {version = "5", default-features = false, features = ["byte"]} -crate_interface = "0.3" -extern-trait = "0.4" -fdt-parser = "0.4" -memory_addr = "0.4.1" -page_table_entry = { version = "0.6", features = ["arm-el2"] } -page_table_multiarch = "0.6" -percpu = { version = "0.2.3-preview.1", features = ["arm-el2"] } - -rdif-intc = "0.14" -rdif-block = "0.7" -rdif-clk = "0.5" -rdrive = "0.20" -rd-block = "0.1" -pcie = "0.5.0" - -# Optional driver dependencies -sdmmc = { version = "0.1", default-features = false, features = ["pio"], optional = true } -rk3588-clk = { version = "0.1", optional = true } -rockchip-pm = { version = "0.4", optional = true } -phytium-mci = { version = "0.1", default-features = false, features = ["pio"], optional = true } - -[target.'cfg(target_arch = "x86_64")'.dependencies] -axplat-x86-qemu-q35 = { version = "0.2.0", default-features = false, features = [ - "reboot-on-system-off", - "irq", - "smp", -] } -axconfig = { version = "=0.3.0-preview.3", features = ["plat-dyn"] } - -[target.'cfg(target_arch = "aarch64")'.dependencies] -aarch64-cpu-ext = "0.1" -arm-gic-driver = {version = "0.17", features = ["rdif"]} - -[target.'cfg(target_arch = "riscv64")'.dependencies] -axplat-riscv64-qemu-virt = "0.3.1-pre.6" - -# xtask dependencies (only used when xtask feature is enabled) -[target.'cfg(any(windows, unix))'.dependencies] -anyhow = { version = "1.0", optional = true } -cargo_metadata = { version = "0.23", optional = true } -chrono = { version = "0.4", features = ["serde"], optional = true } -clap = {version = "4.4", features = ["derive"], optional = true } -colored = { version = "3", optional = true } -flate2 = { version = "1.0", optional = true } -jkconfig = { version = "0.1", optional = true } -ostool = { version = "0.8.4", optional = true } -regex = { version = "1.12", optional = true } -reqwest = { version = "0.13", optional = true } -schemars = {version = "1", features = ["derive"], optional = true } -serde = {version = "1.0", features = ["derive"], optional = true } -serde_json = { version = "1", optional = true } -sha2 = { version = "0.10", optional = true } -tar = { version = "0.4", optional = true } -tokio = {version = "1", features = ["full"], optional = true } -toml = { version = "0.9", optional = true } - -axvmconfig = { version = "0.2.2", features = ["std"], optional = true } - -[build-dependencies] -axconfig = "=0.3.0-preview.3" -prettyplease = "0.2" -quote = "1.0" -syn = "2.0" -toml = "0.9" -anyhow = "1.0" diff --git a/os/axvisor/LICENSE b/os/axvisor/LICENSE deleted file mode 100644 index b1a313f58..000000000 --- a/os/axvisor/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to the Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/os/axvisor/README.md b/os/axvisor/README.md deleted file mode 100644 index 0002161d9..000000000 --- a/os/axvisor/README.md +++ /dev/null @@ -1,116 +0,0 @@ - - -

AxVisor

- -

A unified modular Type I hypervisor

- -
- -[![GitHub stars](https://img.shields.io/github/stars/arceos-hypervisor/axvisor?logo=github)](https://github.com/arceos-hypervisor/axvisor/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/arceos-hypervisor/axvisor?logo=github)](https://github.com/arceos-hypervisor/axvisor/network) -[![license](https://img.shields.io/github/license/arceos-hypervisor/axvisor)](https://github.com/arceos-hypervisor/axvisor/blob/master/LICENSE) - -
- -English | [中文](README_CN.md) - -# Introduction - -AxVisor is a Hypervisor implemented based on the ArceOS kernel. Its goal is to leverage the basic operating system functionalities provided by ArceOS as a foundation to implement a lightweight unified modular Hypervisor. - -- **Unified** means using the same codebase to support three architectures—x86_64, Arm (aarch64), and RISC-V—maximizing the reuse of architecture-agnostic code and simplifying development and maintenance costs. - -- **Modular** means that the Hypervisor's functionalities are decomposed into multiple independently usable components. Each component implements a specific function, and components communicate through standardized interfaces to achieve decoupling and reusability. - -## Architecture - -The software architecture of AxVisor is divided into five layers as shown in the diagram below. Each box represents an independent component, and components communicate with each other through standard interfaces. The complete architecture description can be found in the [documentation](https://arceos-hypervisor.github.io/axvisorbook/docs/overview). - -![Architecture](https://arceos-hypervisor.github.io/doc/assets/arceos-hypervisor-architecture.png) - -## Hardware Platforms - -AxVisor has been verified on multiple hardware platforms, covering extensive support from virtualization environments to actual physical devices. To facilitate rapid user deployment, we provide one-click build scripts for each platform in the [axvisor-guest](https://github.com/arceos-hypervisor/axvisor-guest) repository, which can automatically generate corresponding image files. - -| Platform Name | Architecture Support | Key Features | -|---------------|---------------------|--------------| -| QEMU | ARM64, x86_64 | Virtualization platform, supports multiple architectures, for development and testing | -| Orange Pi 5 Plus | ARM64 | Development board based on Rockchip RK3588, high-performance ARM platform | -| Phytium Pi | ARM64 | Development board based on Phytium E2000Q processor, domestic ARM platform | -| ROC-RK3568-PC | ARM64 | Development board based on Rockchip RK3568, suitable for industrial applications | -| EVM3588 | ARM64 | Evaluation board based on Rockchip RK3588, enterprise-level applications | - -## Guest Systems - -AxVisor supports multiple operating systems as guests, with good compatibility from lightweight microkernels to mature macrokernel systems. To simplify the user deployment process, we provide one-click build scripts for different guest systems in the [axvisor-guest](https://github.com/arceos-hypervisor/axvisor-guest) repository, which can quickly generate adapted guest images. - -| Guest System | System Type | Architecture Support | Feature Description | -|--------------|-------------|---------------------|-------------------| -| [ArceOS](https://github.com/arceos-org/arceos) | Unikernel | ARM64, x86_64, RISC-V | Rust-based componentized operating system, lightweight and high-performance | -| [Starry-OS](https://github.com/Starry-OS) | Macrokernel OS | ARM64, x86_64 | Real-time operating system for embedded scenarios | -| [NimbOS](https://github.com/equation314/nimbos) | RTOS System | ARM64, x86_64, RISC-V | Concise Unix-like system, supports POSIX interface | -| Linux | Macrokernel OS | ARM64, x86_64, RISC-V | Mature and stable general-purpose operating system, rich software ecosystem | - -# Build - -AxVisor is built based on the Rust ecosystem, providing complete project build, configuration management, and debugging support through the extended xtask toolchain, offering developers a unified and efficient development experience. - -## Build Environment - -> **Quick Start**: To quickly run AxVisor on QEMU, see the [QEMU Quickstart Guide](doc/qemu-quickstart.md) for a complete walkthrough from environment setup to running guest OSes. - -First, in a Linux environment, you need to install basic development tool packages such as `libssl-dev gcc libudev-dev pkg-config`. - -Second, AxVisor is written in the Rust programming language, so you need to install the Rust development environment according to the official Rust website instructions, and use the `cargo install cargo-binutils` command to install [cargo-binutils](https://github.com/rust-embedded/cargo-binutils) to use tools like `rust-objcopy` and `rust-objdump`. - -> If necessary, you may also need to install [musl-gcc](http://musl.cc/x86_64-linux-musl-cross.tgz) to build guest applications. - -## Configuration Files - -AxVisor uses a layered configuration system, including hardware platform configuration and guest configuration, both in TOML format. - -### Hardware Platform Configuration - -Hardware platform configuration files are located in the `configs/board/` directory, with each configuration file corresponding to a development board (or QEMU platform architecture) that we have verified. They specify the target architecture, feature sets, driver support, log levels, and build options. - -> The guest configuration item `vm_configs` is not specified by default and needs to be specified in actual use! - -### Guest Configuration - -Guest configuration files are located in the `configs/vms/` directory, defining the runtime parameters of guests, including basic information, kernel configuration, memory regions, and device configuration details. - -The configuration file naming format is `--board_or_cpu-smpx`, where `` is the guest system name (such as `arceos`, `linux`, `nimbos`), `` is the architecture (such as `aarch64`, `x86_64`, `riscv64`), `board_or_cpu` is the hardware development board or CPU name, and `smpx` is the number of CPUs allocated to the guest. - -## Compilation - -AxVisor uses the xtask tool for build management, supporting multiple hardware platforms and configuration options. For a quick build and run of AxVisor, please refer to the [Quick Start](https://arceos-hypervisor.github.io/axvisorbook/docs/category/quickstart) chapter in the configuration documentation. - -1. **Generate Configuration**: Use `cargo xtask defconfig ` to select the target hardware platform configuration from the `configs/board/` directory. This command copies the corresponding board-level configuration to `.build.toml` as the build configuration. - -2. **Modify Configuration**: Use `cargo xtask menuconfig` to launch the interactive configuration interface, where you can adjust the target architecture, feature sets, log levels, and other parameters. - -3. **Execute Build**: Use `cargo xtask build` to compile the project according to the `.build.toml` configuration file, generating the target platform binary file. - -## QEMU Quick Run - -To quickly run AxVisor on QEMU with a guest OS (ArceOS / Linux / NimbOS), see the [QEMU Quickstart Guide](doc/qemu-quickstart.md). - -# Contributing - -Welcome to fork this repository and submit pull requests. The existence and development of this project is thanks to the support of all contributors. - -
- - - -You are also welcome to scan the QR code below to join the discussion group (please send a note with: AxVisor). We look forward to consulting on issues, exchanging experiences, and receiving feedback suggestions. - -![group](https://arceos-hypervisor.github.io/axvisorbook/assets/images/group-c0e9fb6c8a7720a1f7eb55d3f4f40b4c.png) - -# License - -Axvisor is licensed under the Apache License, Version 2.0. See the [LICENSE](./LICENSE) file for details. \ No newline at end of file diff --git a/os/axvisor/README_CN.md b/os/axvisor/README_CN.md deleted file mode 100644 index 43a5d2a1f..000000000 --- a/os/axvisor/README_CN.md +++ /dev/null @@ -1,116 +0,0 @@ - - -

AxVisor

- -

一个统一组件化的 Type I 类型的虚拟机管理程序

- -
- -[![GitHub stars](https://img.shields.io/github/stars/arceos-hypervisor/axvisor?logo=github)](https://github.com/arceos-hypervisor/axvisor/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/arceos-hypervisor/axvisor?logo=github)](https://github.com/arceos-hypervisor/axvisor/network) -[![license](https://img.shields.io/github/license/arceos-hypervisor/axvisor)](https://github.com/arceos-hypervisor/axvisor/blob/master/LICENSE) - -
- -[English](README.md) | 中文 - -# 简介 - -AxVisor 是一个基于 ArceOS 内核实现的 Hypervisor。其目标是利用 ArceOS 提供的基础操作系统功能作为基础,实现一个轻量级统一组件化 Hypervisor。 - -- **统一**是指使用同一套代码同时支持 x86_64、Arm(aarch64) 和 RISC-V 三种架构,以最大化复用架构无关代码,简化代码开发和维护成本。 - -- **组件化**是指 Hypervisor 的功能被分解为多个可独立使用的组件,每个组件实现一个特定的功能,组件之间通过标准接口进行通信,以实现功能的解耦和复用。 - -## 架构 - -AxVisor 的软件架构分为如下图所示的五层,其中,每一个框都是一个独立的组件,组件之间通过标准接口进行通信。完整的架构描述可以在[文档](https://arceos-hypervisor.github.io/axvisorbook/docs/overview)中找到。 - -![Architecture](https://arceos-hypervisor.github.io/doc/assets/arceos-hypervisor-architecture.png) - -## 硬件平台 - -AxVisor 已在多种硬件平台上完成验证,涵盖从虚拟化环境到实际物理设备的广泛支持。为方便用户快速部署,我们在 [axvisor-guest](https://github.com/arceos-hypervisor/axvisor-guest) 仓库中提供了各平台的一键构建脚本,可自动生成对应的镜像文件。 - -| 平台名称 | 架构支持 | 主要特点 | -|---------|---------|---------| -| QEMU | ARM64, x86_64 | 虚拟化平台,支持多架构,用于开发和测试 | -| Orange Pi 5 Plus | ARM64 | 基于 Rockchip RK3588 的开发板,高性能 ARM 平台 | -| 飞腾派 | ARM64 | 基于飞腾 E2000Q 处理器的开发板,国产 ARM 平台 | -| ROC-RK3568-PC | ARM64 | 基于 Rockchip RK3568 的开发板,适合工业应用 | -| EVM3588 | ARM64 | 基于 Rockchip RK3588 的评估板,企业级应用 | - -## 客户机系统 - -AxVisor 支持多种操作系统作为客户机运行,从轻量级微内核到成熟的宏内核系统均有良好兼容性。为简化用户部署流程,我们在 [axvisor-guest](https://github.com/arceos-hypervisor/axvisor-guest) 仓库中提供了针对不同客户机系统的一键构建脚本,可快速生成适配的客户机镜像。 - -| 客户机系统 | 系统类型 | 架构支持 | 特点描述 | -|-----------|---------|---------|---------| -| [ArceOS](https://github.com/arceos-org/arceos) | Unikernel | ARM64, x86_64, RISC-V | 基于Rust的组件化操作系统,轻量级、高性能 | -| [Starry-OS](https://github.com/Starry-OS) | 宏内核操作系统 | ARM64, x86_64 | 面向嵌入式场景的实时操作系统 | -| [NimbOS](https://github.com/equation314/nimbos) | RTOS 系统 | ARM64, x86_64, RISC-V | 简洁的类Unix系统,支持POSIX接口 | -| Linux | 宏内核操作系统 | ARM64, x86_64, RISC-V | 成熟稳定的通用操作系统,丰富的软件生态 | - -# 构建 - -AxVisor 基于 Rust 生态系统构建,通过扩展的 xtask 工具链提供了完整的项目构建、配置管理和调试支持,为开发者提供统一且高效的开发体验。 - -## 构建环境 - -> **快速开始**:如果你只想在 QEMU 上快速跑起来,请直接参见 [QEMU 快速上手指南](doc/qemu-quickstart_cn.md),其中包含了从环境搭建到运行客户机的完整步骤。 - -首先,在 Linux 环境下,需要安装 `libssl-dev gcc libudev-dev pkg-config` 等基本开发工具包。 - -其次,AxVisor 是使用 Rust 编程语言编写的,因此,需要根据 Rust 官方网站的说明安装 Rust 开发环境,并使用 `cargo install cargo-binutils` 命令安装 [cargo-binutils](https://github.com/rust-embedded/cargo-binutils) 以便使用 `rust-objcopy` 和 `rust-objdump` 等工具 - -> 根据需要,可能还要安装 [musl-gcc](http://musl.cc/x86_64-linux-musl-cross.tgz) 来构建客户机应用程序 - -## 配置文件 - -AxVisor 使用分层配置系统,包含硬件平台配置和客户机配置两部分,均采用 TOML 格式。 - -### 硬件平台配置 - -硬件平台配置文件位于 `configs/board/` 目录下,每个配置文件都对应了一个经过我们验证的开发板(或 QEMU 平台架构)。其中指定了目标架构、功能特性、驱动支持、日志级别以及构建选项。 - -> 客户机配置项 `vm_configs` 默认并没有指定,在实际使用时需要进行指定! - -### 客户机配置 - -客户机配置文件位于 `configs/vms/` 目录下,定义了客户机的运行参数,包括基本信息、内核配置、内存区域以及设备配置等详细信息。 - -配置文件命名格式为 `--board_or_cpu-smpx`,其中 `` 是客户机系统名字(如 `arceos`、`linux`、`nimbos`),`` 是架构(如 `aarch64`、`x86_64`、`riscv64`),`board_or_cpu` 是硬件开发板或 CPU 名称,`smpx` 是分配给客户机的 CPU 数量。 - -## 编译 - -AxVisor 使用 xtask 工具进行构建管理,支持多种硬件平台和配置选项。快速构建及运行 AxVisor,请参见配置套文档中的[快速上手](https://arceos-hypervisor.github.io/axvisorbook/docs/category/quickstart)章节。 - -1. **生成配置**:使用 `cargo xtask defconfig ` 选择 `configs/board/` 目录下目标硬件平台配置。此命令会将对应板级配置复制为 `.build.toml` 作为构建配置。 - -2. **修改配置**:使用 `cargo xtask menuconfig` 启动交互式配置界面,可调整目标架构、功能特性、日志级别等参数。 - -3. **执行构建**:使用 `cargo xtask build` 根据 `.build.toml` 配置文件编译项目,生成目标平台的二进制文件。 - -## QEMU 快速运行 - -如需在 QEMU 上快速运行 AxVisor 并启动客户机系统(ArceOS / Linux / NimbOS),请参见 [QEMU 快速上手指南](doc/qemu-quickstart_cn.md)。 - -# 贡献 - -欢迎 fork 本仓库并提交 pull request。这个项目的存在与发展得益于所有贡献者的支持。 - - - - - -您也可以扫描下方二维码加入讨论群(请务必发送备注信息:AxVisor),进行问题咨询、经验交流与反馈建议。 - -![group](https://arceos-hypervisor.github.io/axvisorbook/assets/images/group-c0e9fb6c8a7720a1f7eb55d3f4f40b4c.png) - -# 许可协议 - -Axvisor 采用 Apache License 2.0 开源协议。详见 [LICENSE](./LICENSE) 文件。 \ No newline at end of file diff --git a/os/axvisor/build.rs b/os/axvisor/build.rs deleted file mode 100644 index ff8acb01e..000000000 --- a/os/axvisor/build.rs +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! This build script reads config file paths from the `AXVISOR_VM_CONFIGS` environment variable, -//! reads them, and then outputs them to `$(OUT_DIR)/vm_configs.rs` to be used by -//! `src/vmm/config.rs`. -//! -//! The `AXVISOR_VM_CONFIGS` environment variable should follow the format convention for the `PATH` -//! environment variable on the building platform, i.e., paths are separated by colons (`:`) on -//! Unix-like systems and semicolons (`;`) on Windows. -//! -//! In the generated `vm_configs.rs` file, a function `static_vm_configs` is defined that returns a -//! `Vec<&'static str>` containing the contents of the configuration files. -//! -//! If the `AXVISOR_VM_CONFIGS` environment variable is not set, `static_vm_configs` will call the -//! `default_static_vm_configs` function from `src/vmm/config.rs` to return the default -//! configurations. -//! -//! If the `AXVISOR_VM_CONFIGS` environment variable is set but the configuration files cannot be -//! read, the build script will output a `compile_error!` macro that will cause the build to fail. -//! -//! A function `get_memory_images` is also provided to get every vm image from the configuration -//! files. -//! -//! This build script reruns if the `AXVISOR_VM_CONFIGS` environment variable changes, or if the -//! `build.rs` file changes, or if any of the files in the paths specified by `AXVISOR_VM_CONFIGS` -//! change. -use std::{ - env, - ffi::OsString, - fs, - io::Write, - path::{Path, PathBuf}, -}; - -use anyhow::Context; -use quote::quote; -use toml::Table; - -/// A configuration file that has been read from disk. -struct ConfigFile { - /// The path to the configuration file. - pub path: OsString, - /// The contents of the configuration file. - pub content: String, -} - -/// Gets the paths (colon-separated) from the `AXVISOR_VM_CONFIGS` environment variable. -/// -/// Returns `None` if the environment variable is not set. -fn get_config_paths() -> Option> { - env::var("AXVISOR_VM_CONFIGS") - .ok() - .map(|paths| env::split_paths(&paths).map(OsString::from).collect()) -} - -/// Gets the paths and contents of the configuration files specified by the `AXVISOR_VM_CONFIGS` environment variable. -/// -/// Returns a tuple of the paths and contents of the configuration files if successful, or an error message if not. -fn get_configs() -> Result, String> { - get_config_paths() - .map(|paths| { - paths - .into_iter() - .map(|path| { - let path_buf = PathBuf::from(&path); - let content = fs::read_to_string(&path_buf).map_err(|e| { - format!("Failed to read file {}: {}", path_buf.display(), e) - })?; - Ok(ConfigFile { path, content }) - }) - .collect() - }) - .unwrap_or_else(|| Ok(vec![])) -} - -/// Opens the output file for writing. -/// -/// Returns the file handle. -fn open_output_file() -> fs::File { - let output_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let output_file = output_dir.join("vm_configs.rs"); - - fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(output_file) - .unwrap() -} - -// Convert relative path to absolute path -fn convert_to_absolute(configs_path: impl AsRef, path: &str) -> PathBuf { - let path = Path::new(path); - let configs_path = configs_path.as_ref().parent().unwrap().join(path); - if path.is_relative() { - fs::canonicalize(configs_path).unwrap_or_else(|_| path.to_path_buf()) - } else { - path.to_path_buf() - } -} - -struct MemoryImage { - pub id: usize, - pub kernel: PathBuf, - pub dtb: Option, - pub bios: Option, - pub ramdisk: Option, -} - -fn parse_config_file(config_file: &ConfigFile) -> Option { - let config = config_file - .content - .parse::
() - .expect("failed to parse config file"); - - let id = config.get("base")?.as_table()?.get("id")?.as_integer()? as usize; - - let image_location_val = config.get("kernel")?.as_table()?.get("image_location")?; - - let image_location = image_location_val.as_str()?; - - if image_location != "memory" { - return None; - } - - let kernel_path = config.get("kernel")?.as_table()?.get("kernel_path")?; - - let kernel = convert_to_absolute(&config_file.path, kernel_path.as_str().unwrap()); - - let dtb = config - .get("kernel")? - .as_table()? - .get("dtb_path") - .and_then(|v| v.as_str()) - .map(|v| convert_to_absolute(&config_file.path, v)); - - let bios = config - .get("kernel")? - .as_table()? - .get("bios_path") - .and_then(|v| v.as_str()) - .map(|v| convert_to_absolute(&config_file.path, v)); - - let ramdisk = config - .get("kernel")? - .as_table()? - .get("ramdisk_path") - .and_then(|v| v.as_str()) - .map(|v| convert_to_absolute(&config_file.path, v)); - - Some(MemoryImage { - id, - kernel, - dtb, - bios, - ramdisk, - }) -} - -/// Generate function to load guest images from config -/// Toml file must be provided to load from memory. -fn generate_guest_img_loading_functions( - out_file: &mut fs::File, - config_files: Vec, -) -> anyhow::Result<()> { - let mut memory_images = vec![]; - - for config_file in config_files { - if let Some(files) = parse_config_file(&config_file) { - let id = files.id; - let kernel = files - .kernel - .canonicalize() - .with_context(|| format!("Path {} not found", files.kernel.display()))? - .display() - .to_string(); - let dtb = match files.dtb { - Some(v) => { - let s = v.canonicalize().unwrap().display().to_string(); - quote! { Some(include_bytes!(#s)) } - } - None => quote! { None }, - }; - - let bios = match files.bios { - Some(v) => { - let s = v.canonicalize().unwrap().display().to_string(); - quote! { Some(include_bytes!(#s)) } - } - None => quote! { None }, - }; - - let ramdisk = match files.ramdisk { - Some(v) => { - let s = v.canonicalize().unwrap().display().to_string(); - quote! { Some(include_bytes!(#s)) } - } - None => quote! { None }, - }; - - memory_images.push(quote! { - MemoryImage { - id: #id, - kernel: include_bytes!(#kernel), - dtb: #dtb, - bios: #bios, - ramdisk: #ramdisk, - } - }); - } - } - - let output = quote! { - /// One guest image data from memory. - pub struct MemoryImage{ - /// vm id in config file - pub id: usize, - /// kernel image - pub kernel: &'static [u8], - /// dtb image - pub dtb: Option<&'static [u8]>, - /// bios image - pub bios: Option<&'static [u8]>, - /// ramdisk image - pub ramdisk: Option<&'static [u8]>, - } - - /// Get memory images from config file. - pub fn get_memory_images() -> &'static [MemoryImage] { - &[ - #(#memory_images),* - ] - } - }; - let syntax_tree = syn::parse2(output).unwrap(); - let formatted = prettyplease::unparse(&syntax_tree); - out_file.write_all(formatted.as_bytes())?; - - Ok(()) -} - -fn main() -> anyhow::Result<()> { - let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); - - // let platform = env::var("AX_PLATFORM").unwrap_or("".to_string()); - - let platform = if arch == "aarch64" { - "aarch64-generic".to_string() - } else if arch == "x86_64" { - "x86-qemu-q35".to_string() - } else if arch == "riscv64" { - "riscv64-qemu-virt".to_string() - } else { - "dummy".to_string() - }; - - println!("cargo:rustc-cfg=platform=\"{platform}\""); - - let config_files = get_configs(); - let mut output_file = open_output_file(); - - println!("cargo:rerun-if-env-changed=AXVISOR_VM_CONFIGS"); - println!("cargo:rerun-if-changed=build.rs"); - - writeln!( - output_file, - "pub fn static_vm_configs() -> Vec<&'static str> {{" - )?; - - match config_files { - Ok(config_files) => { - if config_files.is_empty() { - writeln!(output_file, " default_static_vm_configs()")?; - } else { - writeln!(output_file, " vec![")?; - for config_file in &config_files { - writeln!(output_file, " r###\"{}\"###,", config_file.content)?; - println!( - "cargo:rerun-if-changed={}", - PathBuf::from(config_file.path.clone()).display() - ); - } - writeln!(output_file, " ]")?; - } - writeln!(output_file, "}}\n")?; - - // generate "load kernel and dtb images function" - generate_guest_img_loading_functions(&mut output_file, config_files)?; - } - Err(error) => { - writeln!(output_file, " compile_error!(\"{error}\")")?; - writeln!(output_file, "}}\n")?; - } - } - Ok(()) -} diff --git a/os/axvisor/configs/board/orangepi-5-plus.dtb b/os/axvisor/configs/board/orangepi-5-plus.dtb deleted file mode 100644 index 88d5fdcc1e0535d2c4d294bb98b556cf4ea00170..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 280066 zcmeEv37lj_k@tHw2Xh}V9G(=Hf=)4g%<0P7q8utHA}YFSPjyxGbTQpsMOAmtj28o5 zhzEFIsEF*U3nH$!uDhe4>w4mPyY7mLe!AnXexC?<41WKAWMsa4M_t`LfG+v#*Lg)o zL`FtN#>A4^X&JPy+_in@|;#!Mq4K9T9FGG2rsbyLcEIfzVU|BvG zVjP}!>j>jS2sOU|aUau-T4`r#tXf~+*g3dq>()Z8QJmaSsaFe|3ia{nX0o15*mfbV z<+zB?<{yYOdA9k7d-+3=-g52}NFx8FbE^4G`E!U}LRcEBH7k>nX4@sWPGN3&<<_ea z*|I0HO#6qj@N8s@jaqH$l&3!RagRM^usE`@ud=nTuyJ5;TVdmt%^M3NTSrF=rA=Ea zmA);TwiU};^Oa&_d{3!1dCGLLF|~DIQ{VcDiE3?dV13{2(aOlCK7>3`?kZNNBr)2Y z+EcHbQrSJVG2g6@7n@_J%v2g9U^ovn&Dwb7l&PsbPZ?O>FOB>B(tev6881%mJf$x` zUfVHVnW>DQ(x0Cm0hKzKIHkWhx~V+4ZF6BurGFIvHue=pMz-}AilbXf8@FvL_VxE~ z%1<{(3e71D&W_5-7fx3jJDaEEOB2OY`Uke2ozItcG-}iJ;mTxj1pJih)6L41Eu5%K z)Eaw0F%D#k^UYnwda+chO;3Um$`^rS7@B&?Q~vvKT_Cx<{11M0i^Nu+Y-}p;z5m&l zLvT%c{geI{$E(F=MKqEyD{(z{L#bIED3nX}u{|ZdQjz%asADH){+H)_vX9 z4aVyZL*2JGRHnu%jmgT?&}gN<!9>hhnE#i8*_fhrX9`B9Bsh)wd)J2QK#8nwbbHbqPMPc7zY#(AZh3jus}WFz@3t zWi`LT5A1XJ$Yi}LHphVVXD91LTa9d4|D0sKXsxL~0`)c2&y*{rLaElMU<^uw?3@gb z@IaVdNtlgkn2QKgnRIwY67nOSmnC5;aTy;Dn9Da@P_OK$SawD{kqJM9d1DfWYG+t> zFmF%7L@Ya)YmzY32M-U&=h_X;N~2mFr+RurD>zvHAy;3)N|$c`DX%`#d?SChztOAT zoYDSEUj3Gg`fqsko?1Ho{@tt3;{PXJeNxusZ@*XXO(lo_H(tFrour=g_YWJIb=bLz zwDt4~b-BRS^+z3}UbAkIt&2>P#hF`X>mrln>Q-xA$z#^yT&HydiMrLGcfSo|SW_^{ zSn3f(3)qfzN4vUc1XzdlD0jk!vb0K$E$dFwy5zXBe}xSd)6f%|<#WK+CFT(9e{M_6 z{zo0LZ6Cb>D;(rq_K4cLGi_be4Urq{MD9uGBWzG+Th>3*uTQk6jLwsK$}iGF(q-MS ztBW*@b?{Ad6{&0N#)O}oYShNZD-9xXmVt1jKLOhjvy-yQIy;wpVX|IUTT@SYUvB#z zvFCK=jkeC4AXa8?-!L(OwW%_YF-}+8I>&89?>)B88$*8+F$3GtM+ru&yDQ~_P-0f`h`IXJKrxmnN(g1xpKe7)Sw30#)e?Etd01~yKxN$>p%CpX)6 zmX?pp(z*jcLTjuvzH@k{)(_3wQtI1UDY3Pon`a_?lrE3ggvD5eQ|v2ABBb z2cEy=mu!c?GS*SP`z^4R;hrj|Jmkt+M&9m1+PjBKjT^><_zdqw;k{g+9v-jl8m`MC z&Q6R(HY^X=+4SKCwnON$58*!omVNEA58<7F_2u8E@-LTAfk0e&CT1c3AFv%lm%Qhw zX+7{R^E2WdcY?}$ajTG>6YuXeD7xmoN8Jg32mBH5lQR>_`<8i-`}2F_J$y9adyzX~ zg?G{WdsglgS1-KNP(*UCHtYS~eBEpmhxqciW~1|CY=_XL&WAq&Y!Cd0zXyCI`+J`M zTYBXG4>A7DeNUe^1jRl8Ak1Y9t`ZF0k3p|C^N+wE>CnBS5C5bx>(=fcuzip@Z2I6& zg`(%%LB@gi-NV=ksFf=XZ&KX*{Rj?& zXP3-Agg+Lgd6?JxNCcr;w6iK8QZ?e%iOL#;co) zZT$i8GjuryIo=obG6sZ=^Y4#)mQp%m7 zOZkUA+M`_jp5~tCzsEHpw-~aC=5VuItwnZp+W<#%PQJy`^@5?j{J946o|1aXi+Qe9 zv@bTF+z0pxVEFt+co*H7sr3!-XpA0&`!VXM<%4|MeAfLjdC%ZA;O>dfYrn_qeBQfT z-pB`erGAq4UU;3dyiQ%AJnB51qZ%|tY8klJ$c`4e2&S1|I4 zX!cu}xzg-!L33%E=D>W>9B4svS(@hHe9;_iL6iG+qI;BWZF))u6!T=$*3w|VckhqA zbI)%DYyrS5L*N+FhMK*i^KU$gg$P#PynUfy8_EgKvGwP|VaP{10Nlea8z+Am8?G1Lc;xWRAk~L~gEt;>e#O$y8V@t?T8{_i z&Gw9v_o2AFf=S4iJ!JnlMmTsh zu^#xQD!U8CP38fR9V4-|3XAo7!D;nosmeA8Rz6PwDcVfttqrCgGgz2Wq)E%>pNsSw zFAtl#7g<;TCZvbF{QHpJ>*YV1fWHxSLtg#2klyR%e~dJ3n5F+4q$wZGFJ!vxPOjWjPirk5i5^C&O;u0fvmlUF!CP$(5kV>kuk##SbS z6aoOj>h~+9MU;OF>V~}fYYj>VPrLZws9tE(?@Q1X*PS#DCSCeM3A*CmA8fkxFA{VI zQ6w@tnEmCN9j9v^g0=52y`Q$@>(&V*7W^2n$?@enn4sIm_|Ba!*M_{P}*-F zKgR}v?InH@(r-nI_}_->ACM;g58?VE(!~EITt7yd_o3&Dbzu1^4 zLOc=f{YW3{@4D65FGNVHzP)0EuV|2XMTQ5 zu)4U{tA9$oAB)F=;IK=*dW_jasl3|n#jlI^bMf^-cozES>OT`4_41)8FS^cg45@b; z`{abJ@peobv|aoksQ+CM{E24{R9=h!2kGFbC%=&q%Z>uvZ>68?>#7(2I+k|y4*~3z zLx6`K7@Bg{*&W+<_*>FE?#9#6bZ#`a_A6Cliu%-&X5YKlTew;G6MHkhaIG9$(tPB) zAIzPm9Kd(N?MU;Cm%cJV^M@OQWw>NKy|k16A?t!=*Ke0gV7zn$b3zv|n}cO9c%@u| z$4f_e&WPz0^~~y81ap2DFx3t)n2%{a?cW$vnvSwOZ&b`Q;f&5Kzxu@iFZRz*N4&3- zxl21GFoIim|0$kH2gKONzLEZf5PvZO2! z4&nWwoBxn%t9b!?leRg--=*pG=KqR2&k1<3uYTIWKu1^n%k$+D7%v^coDi&d<9p>2 z7%v^cYz|gbzA2Z$c#=uC4F#!E*qn}e0VoRW+5{B#6! zMzHczSIH$XUOIv~KUjI$m*o-|FCD>DgO$&?Q!at=(gr3|mKPKz+O3E1d(yg@S`RsY z?Hu;g9q6`M6Eo?i)7G`w{bbpo?j}I{Q)O_&oJ#`DsHJ>&+oA4VHi7e7UIeema7|dee4I zYB?|3tc96!ORt4(=Y`?Fz5OE$`0v<9te0K)F>`&Q9cZ&gW%f6DP>pIvd_VyDpxFwhNu-SN{A4CL8y4Z=F*tvh}?!h?0nTX+0)1cP%3bGn8nhiHq^{Z1EG32HfH&;o{9WVJC3mnLQCLeg&@gA`SgnjOX;ScfgHz;@%Dr;A2?) zR~kPOY2XrC=O8_XG-=^n0dWZ}+zjFV+0gm`(qBWGv~I@rYotl*|0ZY=B)Lz4xhs5t z=S4-JJIl*IFMdD1sEd7ya243bB{)|AXN!G`Lv#peHe)830m`efE} zd*3W$uJ_P!80?8$Z}wIM#!DN$Bp&REc;$XNg28>f?xzSIFCD=|dm;j(dn-&wFkg+$ z4PUMX2KLR(6Q`$be5cIuOZiULetxbOZxGReex_@zN0t z{8aTp1;$H9Fz{2=2Nf7E9l^lw*Lw`Y_~{7dtL`2{U{ZRNE$@Ne_5xq@Ho9$|yjQOs z599)0e%itCzTlQO%SFEYbOeL_s}I~Hm%w=G2xha}CleU%bKzn-f;poLJm+_TryA3p zY(4sFOn1_H)I#2y{GAB;HcOzld?t~p-!I+uO;q{vJ{5A#xC~4-&B2!Uewj31e%=sB z&XwjS^&*H$aJB8{EBmgqaAqE7^)VXj?0)JUZpJ-sW_ z)_0J7GH+k1jSkD6Q0wP;nRHXW@NDU}_J=d+ru^aA(rxV%XVRV3o}XQpWh}gXyYyIO z-iOW0UY+$f$b+ADa})b;*2fS&y*jo2R@MZY|l?e{J;nDp4<6p1JhFGZT!wmnWz0u^+zZx|B85nAC#!G`3MGnkoSDg zPe(BDgS_W^ema7I-`R1W6@F*OeOBy`cHC!$AJlfAH7RqsPt`rtfqB|P<+=6~aox9v z8Z7=x+(SJ{{j~gFz#i)T(U<%7P$2{a|NbX)Zz|r0+Tcps8jIU`<5}3ieS4pobN;@)&)MzGv^fKixlfSt zUuU}~bl={meSVNB=d>@|c6_;p;k;(s{X*ZDxo__?^0n^U`;_089CF{@r~RFc-N#JE zkw!yt zL;En~AC7CimcxcFyrEhS_}VUfPp*mI%u2e1S0V#MX~jQ3Mta=;isw9mjEeK+<%O?}QN5f8ej@Ug2OEcOE=T;_n{;%Y z@*MFQjmN!-&%;xEQY*~(+?)91nQ+lTH}2Gudmo=iruf9;5=9hybW_g*cH3LcY#D}Xf~j=Q;z^Bd#d(C-p!2PcLeq~6Zri{Za+U~hS$%J?HT3= zvY5l`&^4!R;YTbPLVBy_xwbyB3||JHgsq{BL1~J=aX;ikeAIPHyaAgol`EW`SPa;6xwR^V97+>pk3jnPe(6O+NC_EElfNG#0b_l--A zs_iW5t*_;K$?s727=-}_c^C0xqrBLN;i617;dA<<*H!Yn{uaGk2ZK9LKoM zSsBI05jxjhpW5hMW}ogJz02%(T<>iEOuwlQq7$Rl#>B2-LvI`L&F0cXl@lLE8)%e4xErkS79# z^7O=n?_afPpL?j^zOTStEtDAE?$1Q>`^rMcfl1$?2lDLVOn}qBeLSuw;5rM}*|^w; zrR+nSXA>s^#`UBBp5x;rpAWpk`JR#opFiFG&laM+06X`+xh8w!PXu48@1lP+7EZtK zrhWM*C3twM$AjaAdWu*0-c#epXNOLoH{Nr6@ZQ7mvFV=Uqu}^}Z{KXpAS-%wI1|ps zOt7-jn5mZ3>{?x0x&vTXOI&_0(pERy*tdXAwDjM5S3LFCt*b0=vo1*6#!kE`xlo>| zqagy@AI5~^I}V`pLu}$X3Xl1;^sPugS@Rw2VQ?h=o4(N_-9W=8D)aJ_-FK$*bjRp$8%r~x=g-EtJAu|XRqDz|8&J^`RDzf zmL3kyQ4hPN&*!-v=+81XLm`L z`=m;DrZQP_8^Y`>&k^~3ohbkUXUrS;=92{^T)RDw&!FDmua$O|#;Vv;na0Aaob6IO&mj1|8+dS<}?s2G3@FOSu3KO-- z>Qqe(GX{Jaqn4Z>35Y1&#g_GuLe3E1&;gEVK$y+e15SG z7kNS-mmQDf&%Hb*-4fm(u^xS2_E^Nv!Ns6$ga-G&!>LBK-jphnHlOe+$f6a&FFP*? zkH0g@FP_03%FUXme5dQhX0tj|K{7^z`|GB^prg3`du8$u+_7&ob%{F|wzw&$QlmOW z;s{nQ!220$jOJrJW9|jhY=aMsjk6JgFB|S7lW*a@RIA~!$_{=HT}2gyNGD~q9q{Cd zc%TQ%D2&B_(xBz+FZe{HxPd1rkL{>S;#t-O9x=9PYx|#tXGIry#E^Etv%LczC%cjA z4t^%R&;O8^hr69F`?~ARY0+JK-xwH#f*HdndALAwYknjaN|3E)xt=1v{7sNwh}xV{C^-I zLtg%q&haW^7H|!rj2Y-l*fjkarU?(bx8%w*&UtEZ_SsJj9{;3MQ6%Mguy_a3oG;WX z@EdYplJOB5uR}~6uw%LXI5#*GRbtzU@r^1qAna(?l_UjAQ@rXGmvP;WdeRL!hvT2!6x@_myYFtIPjZ&946q8WV{nnD0?Oy(Tbtzj zSHOo@@SbUg6}e!+(fp4%kBe(bh92VW2VTYlkG9SQuX!RcSMeluj`;mc+gugu8iMf5VV)6a= zDlX9K^*m?Bw=`q25xpG&emU;b(j2J$c5? zp2uWb#@oKLO^L9jY1@{@k>7c-h3lGzJw<3tweZ)L#)G~>)$Xky^Y;CWHnPGu+)W^N zUC8RBPw?oC-;e9J%b8lC;iVl6-cf45vmh{f&eV(<#~(7s@AUn#^wSZZ=sTC`{#ZQt z4uPJ_O~YVL8U2XA>Z{(y(;in^B@eVM3_p88o z=?Erzk1%2EJB68!V50X33yi)aBn}h3M_6F=tYaJo?-BOK#!q{4!Mh1Hu)Cf0<*(lQ zMGI%_32A0sQGcR$qY0e8uQ$$X^nD3|(f9SnVQ_X+-_a{DUOJ)`eP2Rgu#U-C#pD}( zUqWDFsvSYXgao!hf0li8dTOFNysK*7;$v{L>YwqCFbK=OgMT*7dvZ-L zKg3I)=%x9*!@}(J(wBMZ_h~w7EKs%l(^`*y%D6lY`GfU*xEiMwz65#dk$B?RyGa6y)d|jc;t*&5gb93YEC$mw9nMdVK1vT_ln zLY`r6I(4Z*A`(f<>iek8lGQs=PMx|6@g%Of=u{2uRmVhjbN&YZDXLr7sz8PWnl?z_#fAKdKASJLjT#c7?$D zd-HwK+7(`iTFL|=?a7trX8M#rkRB=RavHXxpu`MY<2em0Ekc@U<|mZatwp09l5+HQuH1b2T2-_+rGQiDwaMA57Mn z`dB@vSL+o3A?Ubv?UQvH^S=R3wU=da%!G=e13#vNUv{%X4@pek{I&K!k+*uwHO~5A zTTo6|;8!0^R#%jma;$yJu8)cY7VTwz+xf!w*7gmGA12Km-Y)#FuKcW9l_x~Yt^H>D zGA-{U-)8}ib4unFj-HfilS89roQQ$2Q7rsEoQb5bgEv_A6?Qb>luM(aS>^jY3KOGc zLQA_fh%aE~!1;R_A>{c_bwvETXCogrDkHVp6pp+YZg&k!xun8Nf1iVVmb{Ceyz_v`l6Su^Z-5Z5C*!M-WytfNM>C`hlH<$u)51NB ziRAb~_CC49@Fr>RBMJnvdwb_b5!9Yo#Bcqfz!tFNn8^4ff;o8w` zucyz*69+U>W~(I4 za&y|B8w4SL9=*LRLrBqM9}@I9^<5t`>G3&l+y2oKN=VVubsWx{v+YHH95d)XQaQ+ z+=nc~5dZn~rjbw36EEDtWzvI-*q$D}JPBkWMo;}xUw=n@eTvIhey&%>W>=D&;EekB zH$y@#y5)9 z{`>^_x1umXujI>fcJ!V#L}Cajdg@dA<5%+MN9H;L{Vdnh-Hhw#v-S$bAQ)oo>1g;` zQOZ%T_cSZ)dA^00Y8~(_yBhrsiuDl`S={#iF!vEHgMXXb z4>*0@p1@(RC3iFU-?+7Jg71&*BLW25#}ApT&98V9^0XVQZ?kV0z7F*v_O@}HyCKe> zwn2YZT!%D6D;%g=@iDEpYXkShsR#D**1)o_gY%>hdqIj}i2qa;-RMJZJK!UIu$&nj zJ?O+V)xzogC!-Ng&a3{Op)fw?p1rdrF|P?5G3O zldqvY_olRs_y`loTODBR>w#};=SdItfds=4|Mj8=MZiaTU^z25deDP?q?=_MIQO&k z;PEJ@9=rnaeCxp=a5C8O@NGjITOj+<^Q{MU@X);hAOz2+4&UDHq?l&1frj#Zpa`fo!MN@_${J3?Dmm`i~2UqA_Ng-jMkMX6$2une0zQ|M|!jaO?-egL0SKQU+hx96Gi;Fk)=eiBaU z^T# z6+ac6yRlD7kH^Vn#qVdz#WUc|2)3-~$36gGy<4^K!RVWqWC7%!E{;Lt{0 zELzA!YL$PH$z#w~^Rtpk|J^7P(U~4(B67hi!K%e^PNUd<-$N+DoxuWVz$9=e{%ariqmCIEsz^d z%rWW|Vc8EIqm`dP+QYq)d&AGgwOC`?M#8q6hqQZy!Q5{9tgr+akzsw;1O@0eDYSj2 z9Rh4ju7d;kQWwg!4dkIRq^D#8%gB!}!}F21{iGbeq%ed-A6HydEKTW8TCHqkf14`~ zVSKs&E%KEAHCiV&jXWjf-ANv0g9m%vP9FVzC}SYaxyplbV3<#Nya4zPmOLuJozRbg z`IJW<_!;I?9xnvG*~+8Mx)lB!@Frw2IG?hxxU4L&KG?Hyp`9yWA$$h@&1T+Qbdcn7 zo0Cgz7hXg#QJgI9s7&B!0}YDXK`>*IX_NW=KeO+id^~E$QSHgA_JR}xyDxP}$Xfax zYs)m(EJS=e^BFp(3tn{#Q$N9 zKcex+H2x@J>QG+w|20Uz7T4==Q71V5ymxsMt~cX~%YPs>7mfrB@p(2w-VqKSK90Nf9LalfY&S=I8oka9kN+@hZuq#f=fptD9vz|m_I3V~rtcx|6?~j_ z-yOE4*D}oByrmf1~mLX#C$M79RY*zulB)=)}NgRK@7uU-KUQ+{vbWZiB`J zjVUMgl{(7!;To3_lfLcyJCJ^-sY~{M3yNd(u{q@O?PT8vdR87kGPuJZYy3lv|5M|i zYJ4kV@&$d7F}Vuqcj3Ak*Sm2g#s(sYc;VQPAGXhXw2wg^eGgG4vCoSygKJR#w+1Fj zM;sQy(%zBIgK#nLuU+3po^-yY@%J?TuEy-UPw(%Le~+nSy97<+p8GT(tnK}|q;ovv zd{kJG+j|vnTwa4IJASmb^K>a3HEl!6o&BL)7^{xT?-Q6;eF?8ZJ!#u9qy7GST-V}K z8=mZ2%7$``hy(iSbjI>gMLFqF?ue7PQnuk(z|0a?O4mGjGCj>=@YAlV$Gn$dlj0wQec0e1D6- z_rdyDpX7(6C`2H>c75tEwCWRQx`ZD3B-6+BpM7HEWS_o(GV)8C%et=mB)|0qFaq1q zzNE&XUmqWbkp_O!bN9&|3C~ zo}Ppr`Xi<#)*tpI(VtM)!SE!l%j%D|v1i6ZyH@=`+brYx3VtEI!AIG`r^v|H)e-sa zyQw3-Od0U(UrQN$$TaS0OvNO|WWcc{{UsPiyC%cdTA0|oV=zB$pBMH42K`w`zk==C z_8DVIlat(sr4N`I_r^{@v%=sd9nPoV09OqL8FJ*gf-u-Ttt^Adlc(dOy(W34%)T#ns@1-IyEJJ@{@1=qa?YMR0 zb$nU)=|6M1w0R#;rd(3*1M+3zrz5$*E_Adne@^dm!+V8Vz9THMg`MB!hCX+Em)m{s zX^X#0$;$V>_q1idPw&3>w8^p;Tle5^r{ArWTK9ZC^V51y!FTKOy>o5WkxV+Nb;QT* zrxSGA*yT(*DZA|B_R|SEJhUvKOS#SFon(~THtU#lNoVUjzqc2lI^Xe*P5XUxdH-PM z`w;QYMDHDle%j%IAKUqThCY&hX#rHIN=?D+rnb`3ib+A$2Fjx8=b-YiNdsThU z2X<0;CQyXKJ|D`II|m{__;Q9+i%f=))IF3dOsQ^?9JuzXc>cOX{{68$X<=W(p5N(l zPX+HFF?%YS7#L=Pfq2myxBt}3UJBbj6m9pX_fD###Zsj&kf`58vAqR(|A?jEVlP3? z;oJZqk^f9ZHnK(%`^?-gi{#JmSx4V5vG~^@VaDJiEMmazu<1MQVVz8OYX$p-J(DF@ zVk>HxbP0^Wa^P~Z+J`)N3qPZE;Kj&dc!r-)Dao#pO)Ct1{8ej?+wt+e$TJWp%48pj zxb|ro7+iXq!YN(5kCgiYU;vx@SB1r1JNAjj@KN{j@C@E8RvRt`vFsQC6&BH|hj{7J zG);Qk-=Ty^5BXKw5r5RnPaw@;>tABBwhwN<>2QTUU`fr82SOD&vV_iCO25>n^Qec{Hw|i#&tkx^G+|3y%9-#+9lj<7(|CCM=dNidp2` z$zFPM71P3mAwFrT*a6=%hq{Zu@7qIt1(c(WZHmmN|!;aYn#P6Br+(@ zG|Pqh_;eFH@FSb}UQ0)V=LI6W1wer-sFDkq)k~}66b%DWr{s~Ty8d^;P@HCJThY{6!Jp9Q4`SNFEyqv*N>dBD_8MwrtoqdsQ|2oK?A%7=B`*a5Hq zUE2Z5$k!3MRgi9&{j9SRMJe=G*DWPQb)xV7Jd`nzZc0Z6TGd~!B0dntF?zC^)(_R)kk*NM zV#g9^AR_%R&q%2q16DVTmv8X-_M;o0aNN+sp`vv?tDUoqTLrfzHfN3R#4Wsk5AZ%U0%jHa3*ObsmXz1p?q_E(rE1d=Ss?$9DZ$+9C zQF@vBOZnUScOY$jo?dOxB-+YYI(;tA^8V$MAZ@}BBJ$?z^B&~w7>ZvPjiFO$wrMwx z2TPwR>nwft-&fS5J`VvUL#94IUn|((xIRaIhA)Tf0Y`oQ0AjY84}B(1;sS#$_4&)F z2i@FP6m~B9{Erd6gR0Lp!!zCsVBS*qu?(Op@&y+^^H>7QSeSRLwvh}&O zkChz9gQd@ub(TKU*Fu%mppD5D%cq;>OQpNX@RKHqHU z=Dx15;Az(S{7(_RgR0LL8lHW9=6m{3+Os}?K^o!Tkhb;p`685M>+@hse5^%d>WasyP8fCWsaLmm815jC5>gXNF zP;dWU?P*5V0cYFiI>ec!zo6^x@xBEZ(9eBS<8N!5L#fAd z449|dhsWeUH#D>!k-Ohx+WOAmDRnOg8D#1+o>e>k=x_hC=G)!-H2`DA&H5H2v-Fie z1y&_w?(g|yhcR_yXBAiVAand!*Gg|8i`YG0mb}{ZUt}cxZ|uZ}7x2k?z43D|y##I2 zPw!hq&nTslblt#4`b;Y?VLSg5kFU=a~%P<$P;s-0F1*((Huu~M1(nY1<{noh3ETOf{Q7fm40$Wh#M~3f5#FDBUql|f zhX1PZmk<-?V9Y&B%hKpD_ZC3LX;#;{RbLzNU5c zjp>)h&Z=dfTkG<>scpZfDPsith(iU1Qi(qUEV*AkvJVd=|Ist*oiov+lmRwiHQ^{{ zvj*MBA_j`w%6au#4;FH9>&2@T2G5(+$522mr)}DVH24fHEXFPN3`Veq@0G z-9$KW<6en9d7^Kt*W!JJ&0xGbxzp4Vhrj*|BToccZv0_0&V6jb@)NyrxB_{~_;QV3 zr}1kvel23Qc?7^Z`-0*W7DtLDdE_@%nJEpIBnu1%59a@>H99W>KF{LieWNw+jl1Qo z6;IH;rKb^B1|Ia$>Bfse8-2{ZNaL4l{4$L%Ma({KjdZ6N*ym=RPpL`ikXFZfNE`^h zo{7KtFDPUk#wByjqdsZ<>H@SfjPk3lMH>CI^{eQQV$Rt7>LH}9-CqLzc;cUajS{vn zFz29bJJRSQqX2#1$M{u2xSjDUHUEkrw~zUkX#S-}HTjMyn*HFiAQ)u)>R=(=R!6@f zqBVKY?`0n4XhdWlS^pO^2vTm%!gU5v1q=UI;dmb(%0?A7E!lEhSrv=E#gMLk))y1 zn=QI2VT*3s3o;B;P2sy6TT}$Bl{4h;%emj;IT7sJy_GkhkTQO~#&6d6jT*lRF=cj7 z*dohYE1sZ7UD+bFWjWea^fjmWa@xn7?c>&iWQ)G2GVIP4w3z#`T%O(M;gmB;s65$`k zg*;b136f`9@VI*IOsO1{ilwniP#hg82>|Pk_x3-=kvB-p_jPTWbeSeR{9iK`?2GJy z7^K_{v4{zWF|+o|zB^9dCC9p>P}kCDQab3|sx|ywv-jI(;IzJP3Mb%~o|cBUzRto^ zffr-2@HE8_TrHn-ps(Ved|6?UPx&YGH=ti46VP_+`;l6<6mn{J7IqilL?_(5az|eT z=V7Ribi%Ch5IIYw9{j65P`e~7=6}l9U(?B_9lMqO*x5;>ZQ6}jt9b;xE!Su(hc>n) zpnOFJjh)qIy)ZqA4}x=gB%fs@@3{u?$MZoWS(gLE_!>R*J>Zuw?15s!oP+M%ff#*q@7VdF=Ko#eA85?| zJ;LQRK1O5iy|Mhk8Xt|AxUmlEoB5Z@8FqIuES z8R-Hm49#e6SUm_XvUUErzX*kPtY6}#;p0m=>A_ZY9QPB!axddfyr=u)X%3Y`*s<}8 zWpps(WikLjhCrOY{COYGGAsYDYQEikdSRuFju967US=-okL~O$ zjP#dxfG}{rJUxAm**t){y~7T=9dHhae4Wi7aZ=Sjdb(|FzW=*OPp?xl^liSCA#su? z+O6nhzv-K2?_;)#ZCcrTUml)*wUkFYU86omx<(C)K88acdYk9gzGu(#`Lgf+JkJ~c z!%lYlV9#?;FU8J&4Sd;gZ{vRie`ENM_W4VtS?XeXKKr_ODd^ZaY<1C_y27iki+RJB z$hBKt^kmRd7dy>O$}Q4I9;qm~N7Cej{!I3|Q)i9shx#9!=@NK*Aa%Y>UV$sud`092bRn*k{9cJND?iaG$S-N%x{)FE&flxD zui9eYTgYS9`b8Ng^~-KW%|gHS0v$u9e!U76Rz98d>$`xZetl2l?;|EZv(+!+vAWZ( zetlirbkeV911_OoTdh|@nw@+SjJKVimm|rLsb8-~g_WPxucXcL^{bT(?@s-q3{}5M z)lr(S=&3$sBhMGzp0WDPfJBvyS0o#*%o3nFrVcW|^Q!9TusmZimBi`Ww6`H2lNsO( zRebEufD4Ti!J@Tmx1yYS_fw63qOtl&q4igBd!crY%<>G`FcbWglO(E4@Ee@)S;mw(W5!fD zDih9l0!<*0>yOn9#BzhN+{Rdr%ps6I2FBTQ%8g}^ed-zD#V9Z0@_e<}^If)~eSb18 z;=p*yGv;HE{x@9T(DH|({Dx+|f-d0ZajIII4C=+wPJCcyc)YgDrN^o}#?ZpxYUlf% z0~pBN^F1m(oq@}vt+~(tDnubc=5cV~t5DS_uYGd&78jz*>L?m6xsqd|FZCcS=WCXJ zvaT1O7~t-n0Q6#OMmxE197HbMSm-O1n$>~A*e=ZFGJgi&hgu_BuFol7L_Wi*O<0eh zeNtI{-4q&=Cv_`jUN@_pKFPU_IpX9-V@bI&T!G8V5Hkhu1EYAd#lV!gz%Yk{2C1(eNQ9Jgv zTUovauvV6@)im4pBFmcr&oiaq8C{{MB!&E%g3X=ljWc<%jgC7?y)1d>#n8Jf--K3HmT%T{ud=jjZ?|*&M9?9P*~;=P zv~{xF~A$a1E}jf8Ua zi+sm>di@40nhBqI#Kynj-IRn=<^O^2oum0RjCH*A?9-0o#6)p;e0oYiSm*oaS0Zm^ z2PAe~&XFj1&DAt!Yv!jlp3!)h#*-RfsBuH%CSuaW4Tbva?2!2DOo+cegXa#K=(mXh zn;4Wg#Sc5+W}`Ih8kmeWGMNDoLm*A~8P*?`{;&*vHRlc#rq8<%(DiJA`UnejycR$% z?E8WzYkaP@T~8mRxVst@$3`r>ZF{2a$OCoO>L2)Ydn~*MQ9hGC`RZ#>N9&{Yt3QG? zXOPW*9_jCBKFbF})oN=$z=NEr--AMYi-li#kf69)e)~l0Yq*(A(3d$9!7n))>F-8);oY8Zjd1h> z!qE^OX_b_w;NYzDcnEn@LB1m#M)|B9$cNW2!SVe#j_7+ht}lataCC$Z;`90V_bAW4 z$h!#Qd~75N+eUC;ylr0i_(7Bx93MkIj)SA?+6WGBOa#Y&MD>E>!^jhd;Gs?p#bwI< zSWl+0Z;q2c%FC-4z3}=jda(>JDhtsI51u^ao<|%`mT|qz(u@C$@UeZc4Od(a@jfJY z6gm7bjw2+GaU7lX;+6ys>UNEn7psm@uB6i~a!K@Y$Ye`YSs4vpA0MkQ9#2`tyI{ID-G1!qHiOexAT_dkRNq z{rN>4M|gb-$7Yl`xr^QK#!=+)Us1i-rcWRrAD1meVaHBzP-Z%of@6P_7aSi!o;U=L z=y6=8-RLp#D-F?4t|2xL{mku+@}i#}Pvjx@0@U}SpTCUo$y|H{@^LwI*3bV=;P|T) zj?Vh|KM5RvtvIYag(v&`yS!}+cBuhjWOFDJZjUU13@q=Tz)rjP_EK!=xqAxrmvSCn=bu~O0<@eh_sX1q zd_ScJ9BW1l8KKigp5ZqDXY^};Zx1a;CkhPj^tt0w!#T3*K(jDao?um6Za-iVL#Et5 zjtbj9;I(}Z|A2Dv;No8+PrQd)xeY^@l;3b;wGcn#9sXs?j_aqDou}8i(^1a;+xSf6NxMhA&OJukL4HnFc3kXOQLouw z;>Q)UKY($1ZRN=_tJgo*cCGaKGOb6n=Ivg(mA<#LH{U_~*tpo2$td0)$7LUyF=UR* zr%{nLE~lcL<6>j3x3d|SM`=6A(v1uHF1S$U?9Gu{CgTDV#z1-;gNqRtab=9l!ow6! z#-)+AH#cZ~uXAHB+Q}Fgd(+?=Do+#pP=P?+B39O{I)!=L<8eeNo#tF-8DN&s zzuBO9_(@Avl}q)pJt)vJz{sB7x1xO8eYQ?(>LtL_%if*9w%;pj%MbXMdyp5AZV!7b zypN=eI`k*_KY+nFdAK{@n>?aR+`m-ZfPLEH4oE?J|^wWPEg8Y2xf{t}p_TZ(Y z+k-veo)GbOvIjp040S8Q-Ps=eRhPK`TX75iS=s}8FRykn_=AVj;Jy3o0oK7aH-rA2 zxYpw0tf7oq&O27#H_9B;@^*Uldxg<{)ih5Bu`vPi;S{8w4IvrX?r8Tv2fnCI4S3srAI?uCG_ zko#JUb$toB)-i9_iuK5vv&3?WSYCD~Pd|57_Lv`AO*JNWm}i`lQko}ve#k`z=Ykh# z{Ctg{r!jpI@_!6%m-KgRSfZkdm>woQOF-eF1I{V^FVc<#%nr_6Q!czx8p+J?N6F-BlF-^Z6~;UUlU{vez}K(cmx+GAnjG# z-*@Z%r%YTlL?Hi>y`pz)!d^tITj8C>yb5_cM?8C#<9#`J$WaFw^L~XfeNe_HYJ3u6 z;^h3AtG)Vy!MT9`DrhYDoW?h4{6)m1b<7;>71uK2K%d24eOvP#^fGrD;J{OWHDp3= zO67f2C_o@S$|r5Jb`=QDD#GM(QOAM zUi!Ky9gb<5c+LUaYWH%Gn~YsW<6|{GPUF)xro+fFp4NCq<6Vd`4}#6Oi2EskG55n` z^>R=tHt@AQba!eu4*!nAV3xZ&+@fcC`sHOy?yj2np~KEz-i$nWT6m?>UP2upnK834 z5T0chBQD~y6Sk+F zSY-!Y%TU+xnFHn|{jHGl1wILiZ|$PFP2TMlR*X}Vd|tE; zFew`WSQe8O^iu7^FImLEBB_IZI3M%fD6{%T|HRI3`tRT&$3~0~MNEAnZunHW{U9aY zr02t+%F=do?MEm>`vtdX9sHtYSEH?NWeGg!i{4{?Nggz?ZzWxkeAZk<8<|UX?sSlC z?v=oOcjbt1yuS*Mo)z8l;>OyTaR(_B-!!u?alExgatpW!|O z+xu5|VU5q5MhEHbqmF@nrQaQ3OSOa_0x__RI&9X;-<-oThCD9TL677SQFWE$^FQ4zmi&ihz>;B#UF+KoQkqM#`~qt6x72R@PL z9`BD^;eB5UZ%=x5Yb(6;3*-Fuq-R{xB;@hmm+y7R`*^)=lrH3Zo8aWXJT4#a-yt8z z+lzc}V^axv{P*#`2l+VOUgUcl%!~h>;o94?+qfi4$m73{_g%=x z@%A!qw=EW=_?O4!|#2laR-MAMeYNkKyG*ErB#}?e+I|3!38hvLs#_;O_AbDQF7sTatKt@*i&B z-U{z)Qh0mPhmlrzUz@_)lRlJN;eA~SZ!h-u_R&^&U!TI;i~YTQtQFojr10XMe+o=} z?4W1jUK2cz%h%sGA|KP=&A5|A8ABI%#|>l}@0(B&$7@gPc7@m68~{fim(TB;lX$sb ziZX^S_^lhrG~TzOBF68Qp7giT0&gy9C%x_6F8Jl$z6p6mz8qeJywC5Jp6vP^YYf)3 z9A2En+moHVBi{<|-W1+mXL8XA*Bu`fz6+%_ZdV-`8J#HWpaFWa~gr{I=8w?itIxD34<6 zzS5WU`@});&+YL%8DPmI`J;fvSl(q_?2uKK_qBL_94^;9ocor0oR0>K@KG;S@nX_S zzo$T?BOOTcLD--Hq1;|3XN7fCY1wR=-nLEu)=Tp)lI6sEB)WJd_Kkx2L@AhP?g+*!JBp<}L8W0# zuhL%2{&U2b$5^iAIQW6IZ^E-**jJIRt~4=+;?uGr?pP9NuJA8y~NT zq7oiq?bq>7+seFVq2;3wX*+(@0n)O0@ZjbX=ZY-{oIEE&y^J%*I%Gd-d%TwjkjAmN zQqLwtCUdhrLqPi^T%>_MINNhB^57?Yy2fW~{A`U!G`<8e+a3(t!!s9@CBq+a*)-3C z*!1tcG@tvioc!Efwr4eHB?INga*nefz?YPxJ{G{Ul#_2>$cychdPaY+jCo9ngurYY|k=N6WCZdM;Up_<1)7A zen>GGX)k-U$(e2%*j{YUS~N$$b9uz1Gat4Gc1dK3NN{32#r9l*{M~7LI9Dho_Dgwi z`pB^kr>ixZp5${;$j`Gq04nU5z^9EQ5En<)jKu>@p^k;pS^G1frd*k)F12}2ZSFOI z5nd`&W0l5aWh%iCanNT<*+OASZ6UZTY&?0OuSh^cw;hjIy_|jiFTiu&G5i7-)1m5&O0VTvIjxtu^;)11Ju>|*;B_us)kmv4h&w&>OC1Ge^zh+?Vi@8- z%E8l>;H${n{-KT2Po8_C?EYHYV2#S{r(q=-IrbK(2c5EKh(op|9pH_)O0y@9lv^=e^syx1HY>OF9doBnwF#k67p7ld9Xzr$}u`pW3I*E zHTPMKZ$wNS4?@}gk?M3%GFPG8paDg$&@4{W$7vrJ2+MK9y_59b=0CUO*I(bO^^i|W z@4&z4-4SR9UPAavAw6!k2fXtePSmn2{YKScu+h?nY4*#~|31>T->kFx&GbVRj_Ivl z`Z1bDyThIgemvjguxBIxfT&({?oG&ZobC9!7yT>T{ETk_*dz?`Kl|s8@0XFc{mg?M zjxTr$xqdT#tBG^_H2w@?;+XUJF5gd;5nNg03!Y?rIk!m9AJY@Gtb-3{zuo8L>%IO# zaBcve*gTQ_+g+vj{3)A)LbFWIgMs`+^XFtKgQz4<%J(ZMwep3IIy?PWD5rcMsqrHa zYulx#Q8wY^=D?E?^w^CJVcC|t$9S8@K2GAoe89Sa?=b}<)6IY{4jdV;m39VG)d{nK z1Q@~rxAn!yB4zlOXM*kh zIrMep?HK1lpR%GY;r$I`z)DR2mw9VT;NR$PALM62P5v^xzOx7jLxVd1HjD-4ntWh^ zbb%9cmp&EnI(LE!Kb_`+&f_4&ufIeXe1;&cqrOISfj`_bA9*89PY!#v2~;E{hwuuN zSvi1rCkOVQc$Trh;=e>c3}d<9Px8bv*kzv6Z&pkfrL2YS$aq6e)LXpEo$n^8Y;=RI z_%Qd<OACHdv_@CUSl{FCIRFi@=MP2`+=qmDT*sh2X+V~TeFFJoH4h#S zyAgSXwJ;rj3jsXTh42>OVIQn6c#q2jS8uv-CNN1r7Yx6?E_?)K8M+{6(7{9Q3e+>c zRNHo`3vNu=4?7l=73M{1Or!Bz5j;%jtq8d46TaK=gUqAx)3zt-m>maE9*+y_ZU~(T z;s1oF3s1rYdLiy3!u8q)vWSgSC%bed+WBMUJzfz0K__$GMz#vSW9!?zneWdcDErKT zx`3Ix9>@O-nPc}+RM@ctfSbqk@5s}!xQGwBo!bxm@*^Y0Vy;nvz=UxB_o#u7cm5<{fazyn{Z3^i;?Pad5uT|55&BzV)Guwnt%l8ppSz zoj;DzS40iIxsBuFfn7r8IDQOOS>wn#K%S1n#Xj{ij?il>o3`Uf{2WK(=XJ2haR6g9 zsI=@jZqz)+TE-FUMz}eG>1iC_fp#*E_3{MQ(d53zI@HJJ9(`uOvY7qn0GYY>1Qs#4 zF+;-6vD`aR0XjL-b7KQMGRGLF=a{pJcC67Z_P(KR*C4FV^quK9hmTPDR+jXk$&ZyK z{BwU#r6*ZxAF;1sWSP8w?_QQ?fg=f7vJBMk=Xck;m8G*+UVl@14jDLm)k@}oN!h4w z$KkIb+ehIV!u4q7-HyYlnz!Td7{EL>g6U}-u0lJ}>6s!vdtKR>ywA=Y2f(74$TQOE zvsuL8#sU1|o7NF5_;osd6#%JA@YRgpi!=kvd>`VA$ODg*BR=daNHY+Q`CFJlu=(Gb ztj+f%i+7=|$ilp*jPLraZ1ir7NrP*7YL1)V{>65iTE6S^t|t-$gOiir-_M}J_Sfnd zeH`o4f`{0<_Lfn8?cenoP=DAj=#c%6Z zaxc}er(7skcT}f}<4}5eb4VX*4c}M~(mEe&6U~TNaUttI`5a6gZGG zn|%O6U_0)~1jWY0`W@9$VTPWR!zbtc2&4GrIR$asJnnOZ7V}JAhku%OXRz`pGkw^O z6oYgD_ab8^Yg9toS(``*=zyD>kUje*EyD?X%`7_h#UAn38`2}vUX9D^Pob(Wa8bV2 zJq*=M1Lr#UJ5GL)tjj26U{4*a+()m}(hMHlQP}z6VN_^m@vu*MkTEV#6yvfex~IkD zL+}?{~3?VTk{R(Uhy+*RM+4XhG|_+Q(*zMYRU z+qY+EnlOIf_Gr7g^o?ta^sQ8!gyI(6%bFB7{6x>@0;n)>vQAauIU1|KB_sojq|VMA zWW%T8AN#{T0Kkn;9eL8PX`S5vjo|$vG_x7wg|^PNenQ)RT-$;k_FapU0ol|S8mP=# z+R`(%EqGf;4Yzv0{1o!A+rl$soO$*L`? zkN|O{Wo_q0Qr3J-OZpdTn_gu7DQ){nZ95xTKTg}uhpgF`cCw~~d|4;=n!A_vdw@wo zrmTIt0|2Mj)E(02h8OwlMb@9zw*RPYXCv#!Yunk%8tp`9*?W;AJhR$F9lQP9x-Yuz zTK4ToT#@HBwyO}qvPSw(;h8n}y;6pMS@H~gJAt*r$#WVF$j2^?XAqM%)>eH!BNoG1 zch+HUT3f+q&jQb~tIP7xViHAC)@p8B8IWUcXkGl>UnEBW>3+_7Yi-B+ZBU0ldU@XcT3e6g%DUDueTk;gPS*bq zA|Ha0>G9%Jtx;%B6{jkNiOK|ug?~gV--v%4Cwt+!hUkiOt`+t~#L-;Q_q%aCJy{L5 z)Rn(yYX%Xo%Gv44t5HGvFU7^WZghqB-5d|XV{Ww6m9rFvu&;NmXIl*%TYzzFT>TXe1-tqjeO2loSo#ei1MlJEK=7Q$cL2;xesS1`ZsPX z73%#%C0ln=gs>OS~c_s$V~p__;h zJbuC_AunT9o9Q3ix~Z?wKh)nRPzH|o9zktps=8hQyt+1eApCHENDDe=^_TBzlQQ^R zx&6Cm+>N%vwbV_{Wx}A3^1j>OAuac@QTaNqsVwJndIs&M$in8cWC5GA?hB7O`w4}| zoY&XizuC0~-|KEozk~U3;1IbfezB3)p_RPrN?Idr->;^5h2^_6!*fu^Jn%2sfSBWC z<)V7xo;|Z%#;3e%+m`-~F`bbKP_9kx*j1}d;^&0~0Jz}pG&V_HvF$+raN=F2c$j}U z(pZ-u4$XptxdqmKsufHCmxmIy;`^6yBOVi z(s=`Y{hJG?Kdql#w7j>}uO};v$};zUfugPVp1x1a)n zvOScE9mVnD?mawVwBCTQ(9~n>v+SJAeWJOyjSpJM^pk>H+4QgT$3$7 zjN8pEzwhq`Ecvs~|5>*m^pjh2%8PsU54$Pnp|HLQpRlc|^HA__Qg#45%k#UZ!E`uh?b{FlvBcs#^fSTgQ-`9wvEN=EHaL8a=E0NL z@I#PCT5ufc&*S<89@y1uyDE)BZFCe61_m+{8NjZG+i?|fjc6TYA?MsmQU35UOuyaJ zr>vmQ%U|BUGTmFuyB%De^s44LQbv#Cg528P?pyanzong3%3?@)G>w$Q~ExHn3R|IgcZz)4nA>-U`* z5!OX_7X_9$F(Wan9xkm%q}Jt#GG?@;==_qis7lxjAFuw ze=#Q%pQ0E*F$?~`@7y|d&#haxdv;l@-_+cDs?IreQk|-MZ};uIKY33o^e3mx&hn03 ze}1ySd#rn`IOUfzKPMx+yT(ch?^vvSD#E*Gtd#JN#mZF?{#|3Ggtu$X&CgFqcz2DJ z65g>``Amd&*H|gx9gCH#9q%+2^*Kietw_eFoP5g36OTIe!6wI|**`Bq&R)v1m*ML` z#Mr`qB7KIlx$q2UU+5^`Pck{LQl|_%@$ALRYvv*4y~vHvbFL(AjGdAl`+!vTI%J<} z%?Hn*xOT~Xv0aka_=_@@%e(9}vp2$XuDhN0UZc-R_)1g3)>E^aW3rwCx~0hKY_;!? z-B^qhR&Q$;u*5^@B74_obo>6ji!OVs?H3!(nYwD@{V4m9&tmYU=u?p2O^V*~hqmAQ zW0zt~XCFReEZV-&m{VyTp2W$?OO(~o!1l}kdM0QJvZvd5kpbON+w;1b&rL@sL>P%Q zBa6HPu!r6D+b1^m1~j6O7fN*OG(Q}2@pd4%F%h#Fyh8mtx;Q-1o@&fabVB)LZ@d)w zGFzqm3%1$_A)FjIbdh)TiI+M&%70iTgVX#~uTP9EjyX|PFNwj=gKpxld&Xb(FEvZiDCcvM-JJg?I= z+sy&2BN^=Xl3bG8Q&W*WLf=<=+cucn!5%^7UGm&s{)MMwOe3#{W&c&YfsJ(oPcGyA3UJA;kyH^TRY9R7d1D%+Ch_mbl>l&$Y;J1Lvmk4Jre;XjCu zHu~?LsP?|EeE&N7?C1l;WLd#~q=ha0v$}@@hOIRwmi+AKt)QWNZ+5g`-t*bf?}57# zmB&?%ZmIX#>HinQ9JuS8Y)0MU=pp-(o*e;C^4Zrxm)aGt3zjb-P3FGKaWwjKcIC6t z1-z5WKV-xQ&(j8Cirr|~a|qo2Km5;<@Ly=_)6k~2roDJynzHadE06C&B@JwZ_coP( zMjkx&zS7YUpD^zo8s7d{9q;2m6%~12cmpmxmjj*Hw&ieuTlQHqvEjAA3vB4WRzP{~ zm<>M&4pO2vd<43Tp5BIfkMIig?`|9N^H8)Q_82{F$T;Bmy=?dm-~=}GUl+LJHssR* zDN!4qj;@#u_1@gO(Z9QGi1{lv#5&W{hUYq*UN-!Hzzb~XzXou}ZOB1D&~fEU=%e}#Z!+%X$6vrCEE@R8_>+3?F~r%dlf-Q70i z9HtHNtgELDXB+iS?KML+rqBeXKx?(o`I@&4I`%rhM4Q~d&yQ1>Q z^_jf8&E;8#)60h623}x8|MB}fZbPoHQld6|G`eCo)co>(^zUvP!X9G7rR2l58^4zg zzXQC$hW_K1cie`2+9)Mz!^fa2WzOA4RvY5D zH}fEISTX08Ur?*h^ChS(PG-@q<6!XgdrJC)dN8($i`c>E6(Ljh(LinQx?ldDy`>w6So^J8F+XGD6v%_E-SF zJIfwF0Pnya`09LGc85Kbm)c|h$b7+Q@|nP0nLCRhzO!ueL-4k?$v(-vJ7R^OqDYI{ z#^#bIqia{}u@!WoJsyWP>hYhhZ&!24j~pM(C9ubC%q5>pZxUgQ-D!_)PG|7+xpjBh zJN^|1pbxH6<&*6Q2bu5?BSmYx?PD52Nxr=z7#ms2@e;wafS2@fSGq8jX8O9zTxC>knRkb5GCbWIX=Z9e;=; zuInm0P+y4p8&u{N-ru24zeA1Kv^7D~<;P?krTu;eQy1^%vzn~%yRsM|1>+%0Z z<+UYwu_p7KT7z#)kgMrow50s^<%#_clzbPBxa>%Am`~GdzpL29LXkv1yI;dKs*f9+ zAKr;}%J2?Ha}R(wi9Go?5K&*~;=f}=I<{f3y-v-4rb*vBQu#Fc$@?l);$mF9t6Suk z|Mopa;IrR$;Mn+iO(nVRDzv$A_Io$ZSrq+JkPYp_tmw9oqIC1|7LR5$Kb z)E3rXm!O?8z0u*a55ECL`Em{;ANb4r;*$;^afjbB6MQwlj9Frb{ceF67dl$VN4q57 z04#QyuC=BnXZecGx_?09aEEa$#ID27wU6N~o!ahbp_8-O=gEbI!-<^WtM@Z1_eC3R z#&SPYJ-@#>A9v@@9)6E~8rs>mH|@uJ7Wh>-Q3h=+>;u33Zo^UifH~atUIz0=Xczy` z#qr!dcKHIhNr~F!RCK8=9%RlRY|bCz&O^IA0`0roF24Xzu}k`$?Ye)6;>J1_$6QQs z9xeI3Fkd7!3&Tca$BFmzE6~QVU*P)S!=B&SJ{h>QpR-NrntGALAuiV`jT6FL8(GMs zWN!6cRoUZ!?G6VtvOfM2aI$HkGdYHr3XP5eN_ikxU{R}g*0?@x=UB}Dhof>V!kFWQ z@2wq(cI1PeIz7wbfY1K0m8`qnbhrU>-v}D{zCRaR6qTdQCW7m}sHeEpfyKWcB;POZ z2YEL79lqq}7GU~TYl3&g%wU&gf0Ijs0_LpN`DVtfmwfiXM=k#J*hfFGdNKeMw!v5X z->b1uo068S?a)Zu@%YN&xdx9mJ1PI2!u=mUki4wnc(ebp1If$RWU%=BSAdh+)ciDF z0qabtdX~nO;yW{We6^#q)Bd$e)}Ec4-<^H~Zx%Pc`0Y6vEbGUs z0O!UZm#v`ENIX+!9}75Vu{@uf!NT*^fR(2`CR@&*-njn6vGMiU7<@KY*;1Q5e_@U5 zWrOE0<3BQk`#vDOzQ6CPN@Y8nC=cdT-?fH|Yb(te&vSH$h3Guxo=@j#S>Ct-rnw1t zs;7IORNX&Vbz>Zhx!&8x3O2giRQDs@J#=$Thf0Uq`4Wuxai9O9Pc5c23^ zpT1wCKWTP0j&0mHK9ag##x0@Ay({|=&{mp@O}r^@eoe|F2ab*r!N-Hd%gPMq+LOG% z!5GiI8=MrLhWlub13j^a{@8ZyhyP3CN9_$7iTj^|h<-@q-B_LaT(dbdp1$r#^p#lO zJUiQHZCkT>{-n_j#ul_M^Tj<;X-~p;L;b6RA&2a>mI2n7#5%M;X^0cF%loI;jy;tW z@Ww`v>BV{*J}|m%v3zH1;)ry$C4O zYbmjRD)4Au;&=0}@+5Az*vFV%_oBZ6y~IWOj)%H`*zIJX&vUt<^!*xNCMJuw1!6$= z;K{?#p?dX_84Ucs>}{Z&P?stKUZncHX%gXIvVG|2Yt7Asv zLf7~~W8>F=hH~iI_*w_Q9!JWLoYi*|#+55aypDRZ(%y1D+F97Ye|G(t91j@0lH=U* z1cw8EEZdRefezOPp=G}Q4se3+4Ppn@XpU!lTIQ5deCQBwn$wrL}p>IC7{I(kU_yYQcy-8z0ENiA>0 zn&+duIUaFXmG}R{x$y3^oAuVFR=vjGY|U$sa_`L_tmNL3<$sv+Z#Bj@5CO%?c~`)g zPdS;K|586-Y3o3u@sT=7bF?<+sE*{VRduBQMMwB`a=1FxtkFy3BQbgQ1FpuW(jRH* zM>6`2XlJqdAPx)k$DmEepUTL23o=K$IzoG>(&D?J8EK8AIobmNhjKHI%eeRsZ$>*w z(i|a_J?CgR_GdkPdts_N+iq7| zGn;0|$0!qvrRRMW#yZ;uAo2}34x*~PCx=He4(rm*kflEvxFP*fmVR{1(5D($`iDm7 zX%laEFaEksXwJ!gMufJhw6wdYT`%dwj{DdM?FW>$FwEawX?(E|C3B+d`)|uUQpYCSx6T1|Cuc48Yu(3q7Am}V+^W3H_bIS@n*E*4`%y_ zfoBW;a`&5H%PaSBFmn#+S8+c<+SW{KzEYo_sZ7;oIDt{v4;%1zW9+*UmR}*gX+Lb& zZC{*VO-p^hr7_(qu@TpYavN1iDI+>t>^3`LCD8lz120MC>}oIAec(k7@5MM$p2KNB zda`K4xmkSIt}<2Ue<@3`=Ur&e_&b{u#&CjL=XmWEz5|rwnY(Awg-3;(Ea;K?#Q7t< zKOBu&+|z5c%VPGsP8zO)K5q6$mP%f3;Gg1r!N7k&|0^zVpmKGR1V9GCKQzmb~WLq_|+ zMUFS_ODx-wpBL?VXM&z?PuJuK%v9f)&C#`*@$dYeyEnKTeb}=kpp&&52f<@IoX`<21YBQkPR3+G?8v@?&sV5>vM{#GJhmtg6#}r6BYLfb?(E)T{yuX$ zmos{~f6428qjT~cz#_B0%ev(LrK1rZuMT)T*YO}9;pJjKypO?sWFZ$JbG53CI;S}c z$I0^b_oWRQ%F)RnIrB9cuEw(F!#>6|arc5a@;t1sW#%(C9`7&5 z<^DI;aDBUM$Ml3|q0MufDjk7<=j2&(dt5qnt%Y5xFSH? zgANZlLgMyy8N8bN-_JW3zUj8s%Wq6`+{`v3Y8x&w=t6UZXx-s3Hl1qZr8mSD?{m%@xVKbX?3uk(g6PqO; zE7waK{>j8*A7qkw&IZEJQDdj?6M&(2@@#ixt|897(5&kx7B()U?JdD~3J`;OFHZQl zvGzLkE`RzdSg2es{`%zp3C0@uw(FDm=M<3-K^UbRua%zyF6V@US3d-@zQMs=Y?~l= zh(Fb~;X7b=&9*-SS1C)fEoG-|-5SI1h3}GW$)}fXxhJ4)x#J@4uG;p$96ju}`Yabi zR8|yreO>G>pVNXIcqhPI4PPXYk8A47cPZC0)XMa9Yqm-R0;#<8mt5!kOO{YTcfVQt z5Wms=17#vl(8_Nt62Bt!Ia=8tVNZ!K2AzvHchzNN`@9x$?DiXX-HubWwd{+o0WA7h z+6_K!32sVH+Pk(xdfv4sz3_1FIaM6yn!;KT;Ann%BT%WU+JgKP)_Z`2QT*>s0+%`@ zm!L7>XR^jOH8z@!4e|FOg}=W*r0QhNJxyo7_seZBASR2CeXKirU7a9bh-{9==a-Ao z4q5w{U)YCI`c7AjwR)SAH$|;f265u({BLJMua(1SH#QJG5wEG8Q(c>NGwI1=$-3VD z8DL9^>dN(#eh74}JOgc%>6xw%hb8Me2xQhJ>MFk@!?B!Sn|1T@jh)l=YH*Pf)s=gO zn64L~o#Ssubuuqm*KyDcW^}#$D$h7`y85?aq+>7Lub;)JQlh$YKE`yt5bYfQBGjGL z^=!}#W^}!DP3{idoUZ<@6+5fz=fFuyR9EhIV!A#H?HvDN)ScCJJ7@+ox}LgX9Z7X< z*Eemh;(qv4bwLtn#$&b00a;~^Usj}P%x;}QtCx4RxwjxRmltVfX5Gy!r{n5N9leh8 zrZ`QtF}H|E28+$9Dl^4ixfs{QCZgbMb4YD`hbJWJ{?wxAF)-I??jG zwI{z(V3f;8Sq#7AZGgMWH%4})@{yO}*Y_vD-TAE}yHb9zTaMqK0e9!Op6p8bG1jbn ze*xT`--%>b%5M+Xr~dsb;DFyk+juvPmWzE8ZG7gf_O>Z+Jj#jE#>^E zo3UT=-#z%H(NfMYjF-Rn;Fm^AIlnMo{?UV98ZG7g!g%>-4}NL1l=BPXC3#DcAM;ot zVrVB6o71B4avP9>_zU>u^NY!m(fo3IPk#CQ67mb;rSD%o`Q`IV$S;T&F-v!S^Z6y@ z7sN|H=Sz2f`TP>{3*x1pyt?zt=a-OQ5HI~yIN*o;(wUw|JB!AN<`p!p{Y(37@{$(ZpU z>B(R7XM}(9=AQgVd-A7zs#{>6es?X8&)oQZQLtAS>&c(I%lPwpwYz=R_2kc3D&z0& z)pn78eNX;6pD;}5-{jJs^5Z9}+{{oJNyBrEADv%l3iK3eEl3JBS<#h`H%UPb@(OLTvMVodk|?iBL2GTY zN_pXRh>cEqQ!7c&tGJ!hLAT(KLyjBWIr9trlkhr&yl5W_JN~{OeYt+;+vLHHR_r@D zKQiLJ4VEeG@ck|)d)VbT54$<% zVKlU8QAda-v{kh-VwkoLOG%=sDpY#^r(fEGmC)>~bWMSqfg{_^b zE5}OZo;r7IRKfr;A?NrsCpu9`qj5pl`qA>EtqiX!R#b#ToE~_ExT0+XdPxCxeD;Sv zhCO6GXI^1{%A>u2Q|N-vXX?TQn7jr}u5&gJSsyki{CE9+ZCJ0N>F z8;RqL2R?@%ywBhdexKjN<_d+a9D`^t$l-3-cscMfAh&fX*n>Gv z`O+R-ODxQ_#KK$)6}EC5hW1R3wnPR#!!&G`f!sXeSR5PnFT~p6j+f8}zO?g@xi6OI zkFfNC50swq#}XU=NVFSWB)TArU4nDE&<>m*Fk*!agd zdg_xjr>E;UvdDQApBI^NFLpp1^-%i|pD_3q{BtbYJ-;dzCzt4nzunC>_UqcB_ZM|- zQCQa&g>`LFnE8z3D@+?(n7P2hhZ>l&AjTvw9*6TxUbonzEz}phJU>f+e4c)~0hlGE zXPe4n=}$m=Hh!ZvU#oWJ=Vm9HBtQxI2rgLZ^@QrN2Dt1?dChO}`@<&AvnihAN8WZl zv~pD~zfJCC{%A{hZYC~i&NhApNEy6eTu%0|+Y5MD*BJGqTPJyaZ10dJeGbaN1sg|;+*aD;W_4w%pJ-{*zIe*JZQ@GGfp_y6rcXEu;c0b)qlgl z6W@t_N2@V?NXuLh(}&t;^qI#=bGGph*9x@B_{3+PwJ_ts!pP@2m}`^m=h~z&jykX8 za{^YLm_F?1ni8S!=W@w@;jwwTR(AoN$a!_T)tW}H&{hVGzj%$#d8YG&*G(2ydvh7g zcypKD^4X4hXbkb3>s*Y-x;&4$ZOnIWohKc9>hA;4j+O`QXkpsX!Yb!_v>Q3`wSn?6 z=v&K!zO^uIZDAdQcc{%6bDLpZd)+c<^x%95c!aRz)eSM85FO3*cV*o0l(E59ea3z} z2EZJQz8tK2aH%nRwA-C{J}3W`B|J_l@R*-#ZAK#s>1%}ry=u`S zAMTs1zN*i?^L*yFHRsS$r00GxMt>i)Q;t*?x98{g==_c^&kLwNzo;)%mit;>{u>z> zm*-dy5d8IKogDH!K`(QhIWXjTKlF)g8ILXPMtWssZA~7WgE1cWFY2HwQe3m*;`c0+;B*H7BOadviQS5MyXS zA@6aMKj;%nPd^jhfg^Fbhp;gBjuzkP-Ty$eXEJj<@4}q@$uk^NZTHb4k7!JjM~NKC z$BI1Eg#|f6T~&^cM|i|yI%Xf`@ri&(Sxf^>?1A;AFt<*Q@QB5M+5^V|k5eK%)F7nO zoQzYs*5drU6zA-7y7ZrlbL(5`qLV!2LBEhk5Yzn+D)1Qb0TS|{Ut;!naD+#hJ#>DC zdGjF=9x;1RCf?&?tY3Lb|Ck~@FFOkK z$X8MN#}?^%Nl~EZwL)zC|18q;a-l%awix~6(4OU);p+Uhcq|f!v4~hI`0()&9M@!Kb%(*DZW%$Q90WaBCeG=#7&&qd*Ng(Sv z_R!llr5k+G47&XdPPrD5M)os=xnEM)@b7<8+Me>B9xdg^y$k3(Kdw~@8-5jTx4f)) zCK!_sy2<*0_!oYJslUR8U;mRGKjkSC5pA>uZ=lBbqg#Bc{0MU$P}uT+3fevI5_w6_ zxvusj3|=0#{LV_-oqXl?eYt;8dcw>z3R`}4v}f{_+mk$$p64tI8-9u34~YHZ^M~WDH1vea z?AJga`&C9+NC4A6V-Z$f*Ow*0oBo&74KEYyJMpLHev zAwBcC{}`O1$l*KdW5eEm~sXFn@tl#P&{ckz#vm;7veS^m?Ocggz0__XU6 z;cz~oP41!2A z`w!)B`OTp{;}^Fl@hOv~2bhDMzWp7vXZ+&v!+xtT;WB;a9Y5u{0Wb^wv!TR4l-14` z!X^1Qc|Q6)?~?pR{q25+aM^xlA;+)WKQ_P9KV|uSF~={Sk4SIrNw_TkZgu=r=Z%0_ zXwQu$_9Q>6KjAX{x1rDTF0m*5!FwVaPlS1YLtzu2$&1jQ@hi8d-49ScyZL*<8~zn=}8%Jn4umeEi9hg~5(?H8ke zVU8YG)FtCn{un*uBpbieXgLFgLNYo zON{;*X?sRLM(QFx^Hq%gnQ6PHpDod!>k;kuCdXU-cUVYYqCfLzguefRkiJBJ${(ZO z9@3ZSPy5B_FLd;}U2@$bUi~)t~nRWAra}@@ovr;KW5f9R)w#u`p%_-Ts2-j3*Qy54@hX zu#4|x2#uaM_{HXj^20Hl9|@O7OCOsbq}Tbm1LuTwe!V21kIfI_Q~ucabfz62I|j6* zW9WX1^kw;}ioQHOuc(XkW%+4sjy~H#a?C;J5AugBEaDHsyk17}{!x3p6pfiY@%2{u z;TZa(|78Jv%pas@e8lKq9?-}9L3+v`qklz6AMpq2_i^-LzItVWzUU9ws+9g!j-F$1 zj>u%wb3a>B{|=h@LVOhO56s0Jtn+0ZT0L(+TZ-{QoLIcB&(W)Qiu4>jLf`-D9KCv{ zNKg4A^a=03Wb*55Dbmx%q!0X)F#lNk)H_9b&_?O$57G;dbLMNCan6$P9Q#B47#rya zoqs3`^E38nA`f9+KPXIG=b!%9;N0^@J}cwL^+xH*kL!oRmfvf0{PLeE9(3{nMe-BI z75ND3{0i*X|2p(}{ij;<___|!D@l5ulUMT>`>8K{8v3LE^=S9}rt88i!O@tKe#qI9 zYnAFs-MRKCY;}JF+QpXSmzgrhkND(={jZme{4A`#`7g&W<%##N0%l40E>;L!uT3}S zYB;)!R`OOGP|m%GYsOK&Z$!K2>G|N9bc7G+OMP=mln*|)UBX9mVCZAQMR~?b`5cns zd;6(g{~hJCu9T0vb}#U`G{*;a>0)Qv9I?RpRN(XGD4)2U$pv?&>U?t;Fbiokj(NTc74cE9?_1LLbUuJy zHmYa~=}Xp5`E6guox~n~rvayxgumCINig8We(w8Yvb;5IPkFWLt>)Tu5(EXzCZDexNU$&1%RDz~-Om5!H{&0lk-@w_ek zuD{NEkV^b&ToC5oz{1?eDQw4mFWPfs263ZfK<3=KLfGbY!qx|NjQ17B$j5<>K^n?w z?L$2*%>9^!aW47b{bD$_ zy#B}Wvix|B!{YMSHh#`Qmk_H1akze{KM6ChTbS#B!gkybqCGce7k`7^+X42F{hQi_ zu${AZj1LvYDEGI`!{kA|bPU4x3Sjh!zdwxjydLHLwsA^-+c`+MY<~Z5IK~qE%{{o4 zU*-LX1CTho2@W;mG+I%-#4I--Or+J zI6jJuPni5r3i8|=jaD9gf)&a``^4ngP^6FQ!*x1Fzp+R!=`$Rk`6WhwQjuP6VTAO| zKQa3M$kE65=0K6X5$qJ&um7V(`hxzXr~PB}9}DPX{v7)8EZ^h_8S)?!c6DYFI#ppj(q%ZiB^kvt8S2=pMfe4Q_&R6dD zV)GSqU9RaP@+2oaeulna57KkJi_xDF(8v6%;{#RvtNawE@6g8lnLIGy7xORa8SgQ9 z?pLIb>cjaKqrZQVzTjVu&-ossA1~4u{7d?h_(~p-qmR#5<~y!|k@5RKU8FDQPkQcK zWAvX1=wtrW@qsG-RDKE@fA(J;@QeAA^xUJx>nmMfH)13Yhxvv-2@|&@F3Q#6SICx4gRO!?_=m=Ykx4 zTpz}3i9Y?;=IHa+5o%A;Ggm~$@BfCQS9`1j%tCvF*Ko>@{k*n`@Jl*q_qjAS+p7mY)!+44KCz(gP=T#yP`SV&KCeJr>^l|@CAI5u({yImmGMosQ zh5GQb%w_S$y=jDB!er}tmFPoxnCm0-{ol&bm+MpF-~MkqdX<6KJ}lISpZzV4F6< zo<}w@(Qa+bbKaMU$?-GC!^`3}FQS{>yhM2N{$PY>@-)ZO@#t!w5+40GgmR6c0Y!M) zO$gdTU3q^uB3J*<(eCve>mHM3JpM1wqpRO|-#H>jqWf#|lb7jjPF zvkL_se(QLO%=Us5?Kb{tSNb=`<9CjSw}a++%GD*$Q!eNk<@tNZQ{=J_T+trNMcHC< z-RyXHxir60t}gkNa&f;N<9UnYDRS8hM6`!;F$cwX{K4@^M!~kjcLQHa*9KqfVN{Ni*)`qYt@{7wFxw=sIocT2A?U*#COpCdi(9;4^AmZ8_z zdE~|i3hgEIm*HsTryUG^d+S_t22BO}NPJ1&xiv>W0eVvB={X+tN0Iw`cKqBvu!A?} zZAueYlAdGNKF@7 zpIe782j#cF&?V2CER5LX=Mbdd$eTPK?S3p+tM0yWNF0r4oXUKQ(f?HPkYDc zAD5%gwzyV5+KVzOJz*9L)29j>{rdlk_DnxKzli8Ze%#B&=>JxrFX~5n#zTz$?>YKx zyJYpF?99JPPuTJ!TqfT?(3k1Qclf6B(b}K%oIlFn+UK7I`l5cM=h_~lzb!|fZRMR`SL-jv2iGO#M>zDa z^XCBjye)BQ6w{A5VZ11P&KE*o)Q|LK{-mF>@$>s3%pA$5>{oxi)#0l@6;}HbCQp@* z0b}{c^kcu;Kcsi-wu>+3n+HJCpSK5no)_>H)!jwy4 ztFPM!dj2te$)9*JdbD|aQNEbIq$ggC-mSxK{POj~>POwIempPJk9pVIvSdDzzDz&2 zulDlA^mDRA^<&=7#*fcOposjilf(mIj%#7+udwl7@)WdN{xSanMb@Jj{gVsy#dsw> z{THDh;JU)`RfaK~voK!mttA_;w3p4Fgzfr7nAgaWcuBUP-H+A9pF02F0(@()O$GX* zz8qikMM%FnM_=wwoBu$P%ddoO{x$LqQ148?czz{)$$B|JyXNVO`jNgYztV0QeffUC zt{0S_*HzYEyiT$(Y|8z>0PSk|$KsXzG(QIRA7I|j(--w4J)cR%#%JEn=;P}L{jd7G z73YLi9)*=ZVUDNz5zq3E=|_HL>ks+n>5KZ2zHI%W+@x0?>u}D(`DkycQf6H*F|WNm zv^QZpe+biW5ql;tMY|uXd)+YpY|)?&vA?xJ?BYgAJP}~bMX_U=RPc>kLMf8ZuKL~qVfWp5jAykz z-;rBZ1%EPDDN}GyWneH*KUYT2+!dog%+Y%p(kYbsf){ZmzF>TCJye|tbA46V=$N$7 zp7G~IS1I!`LmspTd4zf%9`f*-7J1P3F&;n3;oRt&`~h&rzdY_amu)N%R=#trM|Za&DeysW0fyJq!=RcQLS) zb1m94Ipe;dp4^|sY&n#tkNSf2+|S17haJ6_!EH#z7u=9`$=%#rsGT?l_bLio9l0E2 z{JZ-ioCkKESe~4|hG(fSC>Lb%auL?JybR}tXEFsi<5}*Dvi!*~HuR;w;C>_K3!0wv zp2vCsDB=r#CZWU^j4$rV)b@nAKT_D}I6#&efAB2H$K;{@4CnueArG%}oId7x!-w-ID2$XNhiV)5hxHrbGQa(Y z<4GKe2Rgx@m(MB2JB!+ju$vQw^=k%w8GrCBu^;Knbia4Vqr`sX!LefYyHB1+sr|^q z<_E%I{y+@MdT`%7&(gU>nQZPR%zKY&KkBVK?}u|^uhO|h99@gUd2s)bM~S`2gVz`_ zdyP9DUKUO+6p51!<*`9s!u8XxuU6Lw05{`b9vh@7TdN)z@+i@jJj&Lqlk+^f#)j@+ zE&~l?qikKD&GYOU8SQNnZJl#pl1Tw;#obydu-r-nRA>_r}B_Ii-x;eE#hha$0I zUr3BL+I`UNwa8Ormi8m8@kf}rXp_11V4P?CyRRv__J;G|At8@0y24gbdp$JIqwAVN z`R$yh{1$e)CGC)Bi66;N_jsXh4-0ve=;mx1)$QRq9@$QSGzdT2f2j?U09op*jkVlEF$b%)u;|#PLIZAT`Ww+}u z;j;Dj%#df7I4IlmJTl}_q8sHX+j~7K&!cOuvil_3LvzGsIOiC!A!8#sFXUOG8*Q$8 zvA}Q1T*#wDH}X)qLLR?yJj8d4vz&UB1V{7nP?1~XoiQZ1kvMv^B$~_Z7sm5sFMblu<@~~UuJ__6(Ok|ijORu# zeiF^){K9x{_TneeT+T0y=S@X^joGa;g@{)Fz8v^&K1Abrb5DNx{2cNN<9SO@e);?y z@(bg+)stU7KZpFncs{2$zY(DL`4;jEvmc*v+jvG@l;0gD3j6ejZ>NM zE2H=thRJ!bf&X0ON+SoyU1ea7VPPw02Yp7)`EFy-zp6WF=pPG1ClA{(=Fx7(nA;5N z>I;a7H)!-=j<7sAhZMFv7tmhd3G<*?Vqo0xJhm2i%+IxEm|0V~H7+;~=VwfgZD_Z0%(tuNn|4J$%o{O2=jZv%Z%co2 zHm3_?hxeIc^iOs4BFmO$tndf5k= z#gDh(-1DDmHaZi$Ocxe-=R4r)+~&y?6uLo|@5qDs4#m$0<~IxL7=J{gj=^IxcW%A4 z32R@wRh?RFxNrQWa;+wZx!L-;^)0RTngEe~C{F&n-iGrtGkC8z;QWyehAeyT-)MDe zlTZR}ELQKg1CGkVH~7tSN1NhHYy|RT7@p@|Z-!dr$uWA2J@7Q(bQ!z4*qR#witxHK z#(#S6@n!X=E_cTG&*(kA66qhj{6$l3DD zficM#B76d>!aThK|Ja9n#e42Qdv1;UI~Ip8*Co#a%)Q0J7QskyxbLlWX4;)K zqLB2E7IIb2A@t92{zn;?|Lnuw@~%G-s8ab6j+Be-Sl5=(k2TaoP1}Fr95VO4&9&_X zyd98dtU5cN?t;Az%ihCH74TRb±kyc6|Zt_{~Ud)^)N_yqNq^DDy0zek@m&}rF+ z7Urj06I)viw>M=wyFP_~dj1`A&b8gO-;LJ_dVZ`qf2cXLND%7$lyPayk1unRMsV`T+%&NG z;4A3o+>=-xSIJTu81lCJ?LK?JaQw4aJZl_HEQR?0G8#E9zMq44G2mqbnBfgS!&5oekTQ%%!OypzxUrhH z0-f?^Ng7zADhk#Cx&x3{Rvr5((D zY}^BM(5nx)(g(hKaev0PyJc-Fm`|ub{H1t)J>VXW<3qL;lQ^;vMsDwVZSXDNkSBki zjpnRt!gTb}IRxD5d}A6YGDc>*A8uzGO4N2AM~B+Z>Q6^QhU9h}DKqDT*lYrZ%*-Zz z#x}{n96s7&wh`al44g9GfUaOawz>7R|K*N9?7eIU?&68Ut__D39e2KyHXnF4=)~qW zj+>d`X@ff+$3NZM$;~-;++U}$1P}_m8pU0=6o%-wXQGn^od!fz8GjqYj zT(`LH(|r)^=WV5XCiNBLT4e}*1)PFC*`{&Ld_}p+;+kt7`%zY0f`58m7VqFwwl_|` z2YQKhmtQtzVJ>`k(l6V=jwR}skD)_tV*PR-w9_wo+z6OD?W|u;a&*=&&|P?0zksi_ zVT=`zhpSmQUN65~?eN4ecrp>@oA2c=KDGBTbd)8 zBPgr*B)V@w0NM7jVYhQh1BHrnZ*2^`6&B--hN8jef#0cn=v1C49o?$mtXzRbz8TYT{RRL4rvXmbXfs55|L?1P$%bGZl9 zW~<0RW2Q0w3u(i@ah{OY?$37sR{LTPr7@oT6796_SKZO=Is35J6nyk0H{qQ84g%gy zypF3aY^z}1s-#y6vJ>6XsEbgT|11fnx8aOw#sz$nWNtU44DRW-^=;CgTvt71-e8Ywq1zx z8meBepl$gDjpnA>!j#-XQp3nEu z_V3CqPFI;1FXXs8k!$`3_@wWZYkmYA&b5qpe9o%fnQO*Tot+9cg>NWtg*DH&cb@s6 z)SOUL%>;m^?q`hMDtw7AAywga^87OJ#ie2M!H zIUL~adG}Nm>5t7t(8yfUxzoe811|#ae^-2GF*l4+Q@v?p$=4YyYtV-s+(Z5f=Ci~_ z#=B=cC;z)d-ajkw?j6(2YxHL?xw$&Q|EcV|xOO7HdLEt^^CB)6@C;~vZgy&_Imd-m zE`5QW%C?>-_PUDuO{JTk15MVOZzf^NBb!IROB*Ydv$YTN=pHLmLab7oJKEkYUdwX- zk!DU}4bgQuVZLMRuy9@AIqkkT=ExiC_|y}}uQaB)PO-3W9n7Wkm}?ho*2nzDBiAM# z4|GS&d4cJ1o!?_WXxHXy>3o-Ul1p)CB2_(GbiqQ#AH*Ej`%vFQ z8^+V$R_S+pz|xFsQ`*j>o-02d_hT&M*=(gfU9U`cHdm&an``y+E6uq%^!l+^GMlQ8FfUg=Kzg%( zl!5%tM&DzyW;p|0@~NPWAnnF>^0ftgq@8}Y1@cQf$6*D$a?&9v%tXMvq3}fk{BppI zFYSMmJ10!N-i;$la+Msib(>>xFF_kRT3I(g?O?|H-5FZ)Og|+i9>(%}*~1-f8>(NC ziczWDoZ|W;hrCeRaL#hGJF<@ovD`c;2Y;+wU#VQ3HbE9CdaR76Fy$FUm9_A8?kZSP z>n|G@0RPex;;v$j6`ij28BLpj~k-1Y`W(9hJq=9B z(aOBnKFT!=fYxwh>3dIAAKkYli~)lud5Jnff2lq<`8zNv&%@DwXXGaM!GB%_I{4gl zMk-Ij`3TvPhq>bu+;PGkFUFBN9|yGEottPA+6yJ2&GeiN?!59ZbAG!!r#%0z+;la@ z3JdMU_H=e+y^u@Vsh2I#McOGhD`3e@CQerFhTKF~GdJc?VkqD*e=-IJSc;Rq;t z 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 yU!}PVlDqy; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x00 0x00 0x80000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial1 = "/soc/uart@2800d000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - cluster0 { - - core0 { - cpu = <0x05>; - }; - }; - - // cluster1 { - - // core0 { - // cpu = <0x06>; - // }; - // }; - - // cluster2 { - - // core0 { - // cpu = <0x07>; - // }; - - // core1 { - // cpu = <0x08>; - // }; - // }; - }; - - // cpu@0 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x200>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x07>; - // }; - - // cpu@1 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x201>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x08>; - // }; - - cpu@100 { - device_type = "cpu"; - compatible = "phytium,ftc664\0arm,armv8"; - reg = <0x00 0x00>; - enable-method = "psci"; - clocks = <0x09 0x00>; - capacity-dmips-mhz = <0x161c>; - phandle = <0x05>; - }; - - // cpu@101 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x100>; - // enable-method = "psci"; - // clocks = <0x09 0x01>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x06>; - // }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 root=/dev/mmcblk0p1 rootfstype=ext4 rootwait rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory@00 { - device_type = "memory"; - reg = <0x20 0x20000000 0x00 0x20000000>; - }; -}; diff --git a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp1.toml b/os/axvisor/configs/vms/arceos-aarch64-e2000-smp1.toml deleted file mode 100644 index d8f730225..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp1.toml +++ /dev/null @@ -1,61 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 2 -# Guest vm name. -name = "arceos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x00] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_2008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x20_2008_0000 -## The file path of the kernel image. -kernel_path = "/guest/arceos/phytiumpi" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_2000_0000 -#dtb_path = "/path/to/axvisor/configs/vms/arceos-aarch64-e2000_smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_2000_0000, 0x2000_0000, 0x7, 1], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.dts b/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.dts deleted file mode 100644 index b80c5dad0..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.dts +++ /dev/null @@ -1,155 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000080000000 0x0000000000010000; -/ { - compatible = "phytium,pe2204"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x00 0x00 0x80000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial1 = "/soc/uart@2800d000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - // cluster0 { - - // core0 { - // cpu = <0x05>; - // }; - // }; - - cluster1 { - - core0 { - cpu = <0x06>; - }; - }; - - cluster2 { - - // core0 { - // cpu = <0x07>; - // }; - - core1 { - cpu = <0x08>; - }; - }; - }; - - // cpu@0 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x200>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x07>; - // }; - - cpu@1 { - device_type = "cpu"; - compatible = "phytium,ftc310\0arm,armv8"; - reg = <0x00 0x201>; - enable-method = "psci"; - clocks = <0x09 0x02>; - capacity-dmips-mhz = <0xb22>; - phandle = <0x08>; - }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x00>; - // enable-method = "psci"; - // clocks = <0x09 0x00>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x05>; - // }; - - cpu@101 { - device_type = "cpu"; - compatible = "phytium,ftc664\0arm,armv8"; - reg = <0x00 0x100>; - enable-method = "psci"; - clocks = <0x09 0x01>; - capacity-dmips-mhz = <0x161c>; - phandle = <0x06>; - }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 root=/dev/mmcblk0p1 rootfstype=ext4 rootwait rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory@00 { - device_type = "memory"; - reg = <0x20 0x20000000 0x00 0x20000000>; - }; -}; diff --git a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.toml b/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.toml deleted file mode 100644 index 4f04ffa7a..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-e2000-smp2.toml +++ /dev/null @@ -1,60 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 2 -# Guest vm name. -name = "arceos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 2 -# The physical CPU ids. -phys_cpu_ids = [0x201, 0x100] -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_2008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x20_2008_0000 -## The file path of the kernel image. -kernel_path = "/guest/arceos/phytiumpi" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_2000_0000 -#dtb_path = "/path/to/axvisor/configs/vms/arceos-aarch64-e2000_smp2.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_2000_0000, 0x2000_0000, 0x7, 1], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/arceos-aarch64-qemu-smp1.toml b/os/axvisor/configs/vms/arceos-aarch64-qemu-smp1.toml deleted file mode 100644 index fdd3beccd..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-qemu-smp1.toml +++ /dev/null @@ -1,75 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "arceos-qemu" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu ids. -phys_cpu_ids = [0] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8020_0000 -# The location of image: "memory" | "fs". -# load from memory. -image_location = "memory" -# The file path of the kernel image. -kernel_path = "path/arceos-aarch64-dyn-smp1.bin" -# The load address of the kernel image. -kernel_load_addr = 0x8020_0000 -# The file path of the device tree blob (DTB). -#dtb_path = "path/aarch64-qemu-gicv3.dtb" -# The load address of the device tree blob (DTB). -dtb_load_addr = 0x8000_0000 - -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "disk.img" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x4000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Pass-through devices. -passthrough_devices = [ - ["/",], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - ["/pcie@10000000"], -] - -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [ - # ["gppt-gicd", 0x0800_0000, 0x1_0000, 0, 0x21, []], - # ["gppt-gicr", 0x080a_0000, 0x2_0000, 0, 0x20, [1, 0x2_0000, 0]], # 1 vcpu, stride 0x20000, starts with pcpu 0 - # ["gppt-gits", 0x0808_0000, 0x2_0000, 0, 0x22, [0x0808_0000]], # host_gits_base -] - -interrupt_mode = "passthrough" - diff --git a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.dts b/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.dts deleted file mode 100644 index d1ad3a39f..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.dts +++ /dev/null @@ -1,87 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000008300000 0x000000000001c000; -/memreserve/ 0x000000000a200000 0x00000000008cf15d; -/ { - serial-number = "425ca8fc29ade692"; - compatible = "rockchip,rk3568-firefly-roc-pc\0rockchip,rk3568"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Firefly RK3568-ROC-PC"; - - memory { - reg = <0x00 0x70000000 0x00 0x10000000>; - device_type = "memory"; - }; - - chosen { - bootargs = "earlycon=uart8250,mmio32,0xfe660000"; - }; - - aliases { - serial2 = "/serial@fe660000"; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu@200 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x200>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - phandle = <0x0d>; - }; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; - arm,no-tick-in-suspend; - }; - - interrupt-controller@fd400000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0xfd400000 0x00 0x10000 0x00 0xfd460000 0x00 0xc0000>; - interrupts = <0x01 0x09 0x04>; - phandle = <0x01>; - - interrupt-controller@fd440000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <0x01>; - reg = <0x00 0xfd440000 0x00 0x20000>; - status = "okay"; - phandle = <0xbe>; - }; - }; - - serial@fe660000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe660000 0x00 0x100>; - interrupts = <0x00 0x76 0x04>; - clocks = <0x23 0x123 0x23 0x120>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x04 0x4e 0x05>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "okay"; - }; -}; \ No newline at end of file diff --git a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.toml b/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.toml deleted file mode 100644 index 432f53771..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp1.toml +++ /dev/null @@ -1,63 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "arceos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x200] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x7008_0000 -# The location of image: "memory" | "fs". -# Load from memory. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x7008_0000 -## The file path of the kernel image. -kernel_path = "/userdata/rootfs_overlay/guest/arceos/roc-rk3568-pc" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x7000_0000 -#dtb_path = "/path/arceos-rk3568.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x7000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# The interrupt mode. -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] diff --git a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.dts b/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.dts deleted file mode 100644 index 6da5bf8b4..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.dts +++ /dev/null @@ -1,101 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000008300000 0x000000000001c000; -/memreserve/ 0x000000000a200000 0x00000000008cf15d; -/ { - serial-number = "425ca8fc29ade692"; - compatible = "rockchip,rk3568-firefly-roc-pc\0rockchip,rk3568"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Firefly RK3568-ROC-PC"; - - memory { - reg = <0x00 0x70000000 0x00 0x10000000>; - device_type = "memory"; - }; - - chosen { - bootargs = "earlycon=uart8250,mmio32,0xfe660000"; - }; - - aliases { - serial2 = "/serial@fe660000"; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x00>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - #cooling-cells = <0x02>; - dynamic-power-coefficient = <0xbb>; - cpu-supply = <0x05>; - phandle = <0x0c>; - }; - - cpu@100 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x100>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - phandle = <0x0d>; - }; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; - arm,no-tick-in-suspend; - }; - - interrupt-controller@fd400000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0xfd400000 0x00 0x10000 0x00 0xfd460000 0x00 0xc0000>; - interrupts = <0x01 0x09 0x04>; - phandle = <0x01>; - - interrupt-controller@fd440000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <0x01>; - reg = <0x00 0xfd440000 0x00 0x20000>; - status = "okay"; - phandle = <0xbe>; - }; - }; - - serial@fe660000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe660000 0x00 0x100>; - interrupts = <0x00 0x76 0x04>; - clocks = <0x23 0x123 0x23 0x120>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x04 0x4e 0x05>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "okay"; - }; -}; \ No newline at end of file diff --git a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.toml b/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.toml deleted file mode 100644 index 5f2b90020..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-rk3568-smp2.toml +++ /dev/null @@ -1,63 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 2 -# Guest vm name. -name = "arceos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 2 -# The physical CPU ids. -phys_cpu_ids = [0x00, 0x100] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x7008_0000 -# The location of image: "memory" | "fs". -# Load from memory. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x7008_0000 -## The file path of the kernel image. -kernel_path = "/userdata/rootfs_overlay/guest/arceos/roc-rk3568-pc" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x7000_0000 -#dtb_path = "/path/arceos-aarch64-rk3568_smp2.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x7000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# The interrupt mode. -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] diff --git a/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.dts b/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.dts deleted file mode 100644 index 53d9a15f2..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.dts +++ /dev/null @@ -1,155 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000080000000 0x0000000000010000; -/ { - compatible = "phytium,pe2204"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x00 0x00 0x80000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial1 = "/soc/uart@2800d000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - // cluster0 { - - // core0 { - // cpu = <0x05>; - // }; - // }; - - // cluster1 { - - // core0 { - // cpu = <0x06>; - // }; - // }; - - cluster2 { - - core0 { - cpu = <0x07>; - }; - - // core1 { - // cpu = <0x08>; - // }; - }; - }; - - cpu@0 { - device_type = "cpu"; - compatible = "phytium,ftc310\0arm,armv8"; - reg = <0x00 0x200>; - enable-method = "psci"; - clocks = <0x09 0x02>; - capacity-dmips-mhz = <0xb22>; - phandle = <0x07>; - }; - - // cpu@1 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x201>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x08>; - // }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x00>; - // enable-method = "psci"; - // clocks = <0x09 0x00>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x05>; - // }; - - // cpu@101 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x100>; - // enable-method = "psci"; - // clocks = <0x09 0x01>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x06>; - // }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 root=/dev/sda2 rootfstype=ext4 rootwait rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory@00 { - device_type = "memory"; - reg = <0x20 0x20000000 0x00 0x20000000>; - }; -}; diff --git a/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.toml b/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.toml deleted file mode 100644 index 159ea607f..000000000 --- a/os/axvisor/configs/vms/arceos-aarch64-tac_e400-smp1.toml +++ /dev/null @@ -1,61 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 2 -# Guest vm name. -name = "arceos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x200] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_2008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "memory" -# The load address of the kernel image. -kernel_load_addr = 0x20_2008_0000 -## The file path of the kernel image. -kernel_path = "/path/to/arceos_aarch64-dyn_smp1.bin" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_2000_0000 -#dtb_path = "/path/to/arceos-aarch64-tac_e400-smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_2000_0000, 0x2000_0000, 0x7, 1], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/arceos-riscv64-qemu-smp1.toml b/os/axvisor/configs/vms/arceos-riscv64-qemu-smp1.toml deleted file mode 100644 index ff4a5ca1f..000000000 --- a/os/axvisor/configs/vms/arceos-riscv64-qemu-smp1.toml +++ /dev/null @@ -1,72 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "arceos-qemu" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu ids. -phys_cpu_ids = [0] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8020_0000 -# The location of image: "memory" | "fs". -# load from memory. -image_location = "memory" -# The file path of the kernel image. -kernel_path = "/path/tmp/images/helloworld_riscv64-qemu-virt.bin" -# The load address of the kernel image. -kernel_load_addr = 0x8020_0000 -# The file path of the device tree blob (DTB). -#dtb_path = "path/aarch64-qemu-gicv3.dtb" -# The load address of the device tree blob (DTB). -dtb_load_addr = 0x8220_0000 - -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "disk.img" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x800_0000, 0x7, 0], # System RAM 1G MAP_ALLOC -] - -# -# Device specifications -# -[devices] -# Pass-through devices. -passthrough_devices = [ -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ -] - -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [ - # ["gppt-gicd", 0x0800_0000, 0x1_0000, 0, 0x21, []], - # ["gppt-gicr", 0x080a_0000, 0x2_0000, 0, 0x20, [1, 0x2_0000, 0]], # 1 vcpu, stride 0x20000, starts with pcpu 0 - # ["gppt-gits", 0x0808_0000, 0x2_0000, 0, 0x22, [0x0808_0000]], # host_gits_base -] - -interrupt_mode = "passthrough" diff --git a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fada.dts b/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fada.dts deleted file mode 100644 index 49cb3d063..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fada.dts +++ /dev/null @@ -1,3385 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000018000000 0x0000000000100000; -/ { - compatible = "bst,a1000b"; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "BST A1000B FAD-A"; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - - cpu@0 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x00>; - cpu-idle-states = <0x04>; - phandle = <0x3a>; - }; - - cpu@1 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x100>; - cpu-idle-states = <0x04>; - phandle = <0x3b>; - }; - - cpu@2 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x200>; - cpu-idle-states = <0x04>; - phandle = <0x3c>; - }; - - cpu@3 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x300>; - cpu-idle-states = <0x04>; - phandle = <0x3d>; - }; - - cpu@4 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x400>; - cpu-idle-states = <0x04>; - phandle = <0x3e>; - }; - - cpu@5 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x500>; - cpu-idle-states = <0x04>; - phandle = <0x3f>; - }; - - cpu@6 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x600>; - cpu-idle-states = <0x04>; - phandle = <0x40>; - }; - - cpu@7 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x700>; - cpu-idle-states = <0x04>; - phandle = <0x41>; - }; - - l2-cache0 { - compatible = "cache"; - phandle = <0x03>; - }; - - }; - - psci { - compatible = "arm,psci"; - method = "hvc"; - cpu_on = <0xc4000003>; - cpu_off = <0x84000002>; - cpu_suspend = <0xc4000001>; - }; - - misc_clk { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x3d0900>; - phandle = <0x02>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupt-parent = <0x01>; - interrupts = <0x01 0x0d 0xff08 0x01 0x0e 0xff08 0x01 0x0b 0xff08 0x01 0x0a 0xff08>; - }; - - amba_apu@0 { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x00 0x00 0xffffffff>; - - a1000bclkc@33002000 { - compatible = "bst,a1000b-clkc"; - osc-clk-frequency = <0x17d7840>; - rgmii0-rxclk-frequency = <0x7735940>; - rgmii1-rxclk-frequency = <0x7735940>; - ptp_clk-frequency = <0x7735940>; - #clock-cells = <0x01>; - reg = <0x00 0x33002000 0x1000 0x00 0x70035000 0x1000 0x00 0x20020000 0x1000 0x00 0x20021000 0x1000>; - phandle = <0x05>; - }; - - a1000rstc@0x3300217c { - compatible = "bst,a1000b-rstc"; - #reset-cells = <0x01>; - reg = <0x00 0x3300217c 0x04 0x00 0x33002180 0x04 0x00 0x70035008 0x04 0x00 0x20020000 0x04 0x00 0x20021000 0x04>; - phandle = <0x06>; - }; - - interrupt-controller@32000000 { - compatible = "arm,gic-400"; - #interrupt-cells = <0x03>; - interrupt-controller; - reg = <0x00 0x32001000 0x1000 0x00 0x32002000 0x2000 0x00 0x32004000 0x2000 0x00 0x32006000 0x2000>; - interrupt-parent = <0x01>; - interrupts = <0x01 0x09 0xff04>; - phandle = <0x01>; - }; - - a1000noc { - compatible = "bst,a1000-noc"; - echo-args = <0x01>; - noc-arg-num = <0x2a>; - noc-args = <0x3342000c 0x00 0x33420008 0x505 0x3342008c 0x00 0x33420088 0x505 0x3342010c 0x00 0x33420108 0x505 0x3342018c 0x00 0x33420188 0x505 0x3342020c 0x00 0x33420208 0x505 0x3342028c 0x00 0x33420288 0x505 0x3342030c 0x00 0x33420308 0x505 0x3342038c 0x00 0x33420388 0x303 0x3342040c 0x00 0x33420408 0x303 0x3342048c 0x00 0x33420488 0x303 0x3342050c 0x00 0x33420508 0x303 0x3342058c 0x00 0x33420588 0x303 0x3342060c 0x00 0x33420608 0x303 0x3342068c 0x00 0x33420688 0x303 0x3342070c 0x00 0x33420708 0x00 0x3342078c 0x00 0x33420788 0x707 0x3342080c 0x00 0x33420808 0x707 0x3342088c 0x00 0x33420888 0x707 0x3342090c 0x00 0x33420908 0x707 0x3340010c 0x00 0x33400108 0x505 0x3340018c 0x00 0x33400188 0x505>; - }; - - serial@20008000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x20008000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd5 0x04>; - clocks = <0x05 0x54 0x05 0x66>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x1d>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x07>; - }; - - serial@2000a000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x2000a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd6 0x04>; - clocks = <0x05 0x55 0x05 0x67>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x17>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x08>; - }; - - serial@2000b000 { - status = "disable"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x2000b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x05 0x70 0x05 0x7d>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x29>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x09>; - }; - - serial@20009000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x20009000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x05 0x6f 0x05 0x7e>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x25>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x0a>; - }; - - dma-controller@33200000 { - status = "okay"; - compatible = "bst,dw-axi-gdma"; - reg = <0x00 0x33200000 0x1000>; - clocks = <0x05 0x2f 0x05 0x30>; - clock-names = "core-clk\0cfgr-clk"; - resets = <0x06 0x0c>; - reset-names = "gdma_reset"; - dma-channels = <0x08>; - snps,dma-masters = <0x01>; - snps,data-width = <0x04>; - snps,block-size = <0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000>; - snps,priority = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07>; - snps,axi-max-burst-len = <0x10>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x90 0xff04 0x00 0x91 0xff04 0x00 0x92 0xff04 0x00 0x93 0xff04 0x00 0x94 0xff04 0x00 0x95 0xff04 0x00 0x96 0xff04 0x00 0x97 0xff04 0x00 0x98 0xff04>; - support-slave; - }; - - pcie-phy@30E02000 { - reg = <0x00 0x30e02000 0x1000>; - reg-names = "phy-base"; - dmc-lane = <0x01>; - dmc-mode = <0x02>; - pcie-ctl0 = <0x01>; - pcie-ctl1 = <0x00>; - phandle = <0x0b>; - }; - - pcie@30600000 { - status = "disable"; - compatible = "bst,dw-pcie-rc"; - device_type = "pci"; - controller-id = <0x00>; - bus-range = <0x00 0xff>; - linux,pci-domain = <0x00>; - reg = <0x00 0x30600000 0x10000 0x00 0x30700000 0x10000 0x00 0x30900000 0x40000 0x00 0x30980000 0x40000 0x00 0x40000000 0x40000>; - reg-names = "dbi\0dbi2\0atu\0dma\0config"; - num-iatu = <0x04>; - num-viewport = <0x04>; - #address-cells = <0x03>; - #size-cells = <0x02>; - ranges = <0x81000000 0x00 0x41000000 0x00 0x41000000 0x00 0x1000000 0x83000000 0x00 0x42000000 0x00 0x42000000 0x00 0x4000000>; - dma-ranges = <0x3000000 0x00 0x80000000 0x00 0x80000000 0x02 0x00>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc5 0x04 0x00 0xbf 0x04 0x00 0xc0 0x04 0x00 0xc1 0x04 0x00 0xc6 0x04>; - interrupt-names = "sys\0dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - pcie-phy = <0x0b>; - max-link-speed = <0x03>; - num-lanes = <0x02>; - picp-ctl = "mem"; - ob-memaddr-def = <0x0c>; - }; - - pcie0_ep@30600000 { - status = "disable"; - compatible = "bst,dw-pcie-ep"; - device_type = "pci"; - controller-id = <0x00>; - bus-range = <0x00 0x04>; - reg = <0x00 0x30600000 0x40000 0x00 0x30700000 0x40000 0x00 0x30900000 0x40000 0x00 0x30980000 0x40000 0x00 0x40000000 0x100000>; - reg-names = "dbi\0dbi2\0atu\0dma\0addr_space"; - num-ib-windows = <0x06>; - num-ob-windows = <0x06>; - max-functions = [04]; - pcie-phy = <0x0b>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc5 0x04 0x00 0xbf 0x04 0x00 0xc0 0x04 0x00 0xc1 0x04 0x00 0xc6 0x04>; - interrupt-names = "sys\0dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - }; - - pcie@30a00000 { - status = "disable"; - compatible = "bst,dw-pcie-ep"; - device_type = "pci"; - controller-id = <0x01>; - bus-range = <0x00 0x04>; - reg = <0x00 0x30a00000 0x40000 0x00 0x30b00000 0x40000 0x00 0x30d00000 0x40000 0x00 0x30d80000 0x40000 0x00 0x48000000 0x1000000>; - reg-names = "dbi\0dbi2\0atu\0dma\0addr_space"; - num-ib-windows = <0x06>; - num-ob-windows = <0x06>; - max-functions = [04]; - pcie-phy = <0x0b>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc2 0x04 0x00 0xc3 0x04 0x00 0xc4 0x04 0x00 0xc7 0x04>; - interrupt-names = "dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - }; - - pcie_vnet@0 { - status = "disable"; - compatible = "bst,pcie-vnet"; - vnet-id = <0x00>; - dma-chan-num = <0x01>; - rx_queues = <0x01>; - tx_queues = <0x01>; - tx-fifo-depth = <0x100>; - rx-fifo-depth = <0x100>; - extend-op = <0x10>; - memory-region = <0x0c>; - }; - - pcie_vnet@1 { - status = "disable"; - compatible = "bst,pcie-vnet"; - vnet-id = <0x01>; - dma-chan-num = <0x01>; - rx_queues = <0x01>; - tx_queues = <0x01>; - tx-fifo-depth = <0x100>; - rx-fifo-depth = <0x100>; - }; - - - - - gpio@20010000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,dw-apb-gpio"; - reg = <0x00 0x20010000 0x1000>; - clocks = <0x05 0x62>; - clock-names = "bus"; - resets = <0x06 0x1c>; - resets-names = "gpio0_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x15 0x16 0x17 0x18 0x19 0x1a 0x1b>; - - gpio-controller@0 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x00>; - chipnum-base = <0x00>; - interrupt-controller; - #interrupt-cells = <0x03>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdf 0x04 0x00 0xe3 0x04 0x00 0xe4 0x04 0x00 0xe5 0x04 0x00 0xe6 0x04 0x00 0xe7 0x04 0x00 0xe8 0x04 0x00 0xe9 0x04 0x00 0xea 0x04>; - phandle = <0x1f>; - }; - - gpio-controller@1 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x01>; - chipnum-base = <0x20>; - phandle = <0x4a>; - }; - - gpio-controller@2 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x02>; - chipnum-base = <0x40>; - phandle = <0x47>; - }; - - gpio-controller@3 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x03>; - chipnum-base = <0x60>; - phandle = <0x54>; - }; - }; - - gpio@20011000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,dw-apb-gpio"; - reg = <0x00 0x20011000 0x1000>; - clocks = <0x05 0x82>; - clock-names = "bus"; - resets = <0x06 0x27>; - resets-names = "gpio1_reset"; - - gpio-controller@0 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x00>; - chipnum-base = <0x80>; - interrupt-controller; - #interrupt-cells = <0x03>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe0 0x04>; - }; - - gpio-controller@1 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x01>; - chipnum-base = <0xa0>; - }; - - gpio-controller@2 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x02>; - chipnum-base = <0xc0>; - }; - - gpio-controller@3 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x03>; - chipnum-base = <0xe0>; - phandle = <0x0d>; - }; - }; - - watchdog@2001a000 { - status = "disable"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5d 0x04>; - clocks = <0x05 0x5a 0x05 0x64>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x1f>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt0"; - }; - - watchdog@2001b000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5e 0x04>; - clocks = <0x05 0x5b 0x05 0x65>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x20>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt1"; - }; - - watchdog@2001c000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001c000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5f 0x04>; - clocks = <0x05 0x74 0x05 0x7b>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x2e>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt2"; - }; - - watchdog@2001d000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001d000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x60 0x04>; - clocks = <0x05 0x75 0x05 0x7c>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x2f>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt3"; - }; - - watchdog@32009000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x32009000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x43 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt0"; - }; - - watchdog@3200a000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x44 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt1"; - }; - - watchdog@3200b000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x45 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt2"; - }; - - watchdog@3200c000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200c000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x46 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt3"; - }; - - watchdog@3200d000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200d000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x47 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt4"; - }; - - watchdog@3200e000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200e000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x48 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt5"; - }; - - watchdog@3200f000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200f000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x49 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt6"; - }; - - watchdog@32010000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x32010000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4a 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt7"; - }; - - i2c@20000000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20000000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xcf 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x18>; - reset-names = "i2c0_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1c>; - }; - - i2c@20001000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20001000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd0 0x04>; - clock-frequency = <0xf4240>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x19>; - reset-names = "i2c1_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1d>; - - max96789@40 { - compatible = "bst,max96789"; - reg = <0x40>; - channel_ids = <0x2a 0x3a 0x2a 0x3b>; - }; - }; - - i2c@20002000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20002000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd1 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x1a>; - reset-names = "i2c2_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1e>; - - max96712@29 { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-ctl"; - reg = <0x29>; - lane-num = <0x02>; - i2c-port = <0x00>; - csi2-port = <0x00>; - lane-speed = <0x960>; - regs = <0x40>; - data-type = <0x2d>; - trigger-mode = <0x01>; - trigger-fps = <0x14>; - trigger-rx-gpio = <0x0a>; - trigger-tx-gpio = <0x08>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x06 0x06 0x06 0x06>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x1f 0x14 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - - endpoint { - remote-endpoint = <0x20>; - phandle = <0x71>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x21>; - phandle = <0x2d>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x22>; - phandle = <0x2e>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x23>; - phandle = <0x2f>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x24>; - phandle = <0x30>; - }; - }; - }; - }; - - max96712@2a { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-ctl"; - reg = <0x2a>; - i2c-port = <0x00>; - csi2-port = <0x00>; - lane-speed = <0x640>; - regs = <0x40>; - data-type = <0x2d>; - trigger-mode = <0x01>; - trigger-fps = <0x1e>; - trigger-rx-gpio = <0x01>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x03 0x03 0x03 0x03>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x1f 0x15 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - - endpoint { - remote-endpoint = <0x25>; - phandle = <0x76>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x26>; - phandle = <0x31>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x27>; - phandle = <0x32>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x28>; - phandle = <0x33>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x29>; - phandle = <0x34>; - }; - }; - }; - }; - - max96712@2e { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-ctl"; - reg = <0x2e>; - lane-num = <0x02>; - i2c-port = <0x00>; - csi2-port = <0x00>; - lane-speed = <0x960>; - regs = <0x40>; - data-type = <0x2d>; - trigger-mode = <0x01>; - trigger-fps = <0x14>; - trigger-rx-gpio = <0x0a>; - trigger-tx-gpio = <0x08>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x06 0x06 0x06 0x06>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x1f 0x17 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - - endpoint { - remote-endpoint = <0x2a>; - phandle = <0x7b>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x2b>; - phandle = <0x35>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x2c>; - phandle = <0x36>; - }; - }; - }; - }; - - camera@70 { - reg = <0x70>; - ser-alias-id = <0x60>; - sensor-alias-id = <0x70>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x2d>; - phandle = <0x21>; - }; - }; - }; - - camera71 { - status = "disabled"; - reg = <0x71>; - ser-alias-id = <0x61>; - sensor-alias-id = <0x71>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x2e>; - phandle = <0x22>; - }; - }; - }; - - camera@72 { - status = "disabled"; - reg = <0x72>; - ser-alias-id = <0x62>; - sensor-alias-id = <0x72>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x2f>; - phandle = <0x23>; - }; - }; - }; - - camera@73 { - status = "disabled"; - reg = <0x73>; - ser-alias-id = <0x63>; - sensor-alias-id = <0x73>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x30>; - phandle = <0x24>; - }; - }; - }; - - camera@54 { - reg = <0x54>; - ser-alias-id = <0x64>; - sensor-alias-id = <0x54>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x31>; - phandle = <0x26>; - }; - }; - }; - - camera@55 { - reg = <0x55>; - ser-alias-id = <0x65>; - sensor-alias-id = <0x55>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x32>; - phandle = <0x27>; - }; - }; - }; - - camera@56 { - reg = <0x56>; - ser-alias-id = <0x66>; - sensor-alias-id = <0x56>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x33>; - phandle = <0x28>; - }; - }; - }; - - camera@57 { - reg = <0x57>; - ser-alias-id = <0x67>; - sensor-alias-id = <0x57>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x34>; - phandle = <0x29>; - }; - }; - }; - - camera@58 { - reg = <0x58>; - ser-alias-id = <0x48>; - sensor-alias-id = <0x58>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x35>; - phandle = <0x2b>; - }; - }; - }; - - camera@59 { - status = "disabled"; - reg = <0x59>; - ser-alias-id = <0x49>; - sensor-alias-id = <0x59>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x36>; - phandle = <0x2c>; - }; - }; - }; - }; - - i2c@20003000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20003000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd2 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x2c>; - reset-names = "i2c3_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x37>; - }; - - i2c@20004000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20004000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd3 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x2d>; - reset-names = "i2c4_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x38>; - - lt9211@2d { - compatible = "bst,lt9211"; - reg = <0x2d>; - }; - }; - - i2c@20005000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20005000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd4 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x26>; - reset-names = "i2c5_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x39>; - }; - - ddr_ecc { - status = "okay"; - compatible = "bst,a1000_ddr_ecc"; - reg = <0x00 0x38000000 0x1400 0x00 0x3c000000 0x1400 0x00 0x33000000 0x140>; - reg-names = "ddr0\0ddr1\0a55_ctrl"; - interrupt-parent = <0x01>; - interrupts = <0x00 0x8b 0x04 0x00 0x8d 0x04>; - interrupt-names = "ddr0_ecc_irq\0ddr1_ecc_irq"; - mbox-names = "bstn-mbox"; - }; - - arm_pmu { - status = "okay"; - compatible = "arm,armv8-pmuv3"; - interrupt-parent = <0x01>; - interrupts = <0x00 0x38 0xff04 0x00 0x39 0xff04 0x00 0x3a 0xff04 0x00 0x3b 0xff04 0x00 0x3c 0xff04 0x00 0x3d 0xff04 0x00 0x3e 0xff04 0x00 0x3f 0xff04>; - interrupt-affinity = <0x3a 0x3b 0x3c 0x3d 0x3e 0x3f 0x40 0x41>; - }; - - noc_pmu@0x32702000 { - status = "okay"; - compatible = "bst,bst_noc_pmu"; - reg = <0x00 0x32702000 0x1000 0x00 0x32703000 0x1000 0x00 0x32704000 0x2000 0x00 0x32708000 0x1000 0x00 0x33402000 0x2400 0x00 0x33422000 0x4400 0x00 0x33401100 0x10 0x00 0x33421480 0x10>; - reg-names = "coresight_cpunoc_etf\0coresight_etr\0coresight_funnel\0coresight_corenoc_etf\0cpu_port_set\0core_port_set\0cpunoc_atb\0corenoc_atb"; - memory-region = <0x42>; - }; - - lsp0_pwm0@20012000 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x56 0x05 0x6b>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20012000 0x14 0x00 0x200120b0 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x43>; - }; - - lsp0_pwm1@20012014 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x57 0x05 0x6b>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20012014 0x14 0x00 0x200120b4 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x44>; - }; - - lsp1_pwm0@20013000 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x71 0x05 0x7a>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20013000 0x14 0x00 0x200130b0 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x45>; - }; - - lsp1_pwm1@20013014 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x72 0x05 0x7a>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20013014 0x14 0x00 0x200130b4 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x46>; - }; - - a55_timer0@32008000 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4b 0x04>; - reg = <0x00 0x32008000 0x14>; - }; - - a55_timer1@32008014 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4c 0x04>; - reg = <0x00 0x32008014 0x14>; - }; - - a55_timer2@32008028 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4d 0x04>; - reg = <0x00 0x32008028 0x14>; - }; - - a55_timer3@3200803c { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4e 0x04>; - reg = <0x00 0x3200803c 0x14>; - }; - - a55_timer4@32008050 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4f 0x04>; - reg = <0x00 0x32008050 0x14>; - }; - - a55_timer5@32008064 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x50 0x04>; - reg = <0x00 0x32008064 0x14>; - }; - - a55_timer6@32008078 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x51 0x04>; - reg = <0x00 0x32008078 0x14>; - }; - - a55_timer7@3200808c { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x52 0x04>; - reg = <0x00 0x3200808c 0x14>; - }; - - spi@2000c000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000c000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x00>; - cs-gpios = <0x47 0x02 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x48>; - }; - - spi@2000d000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000d000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdc 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x01>; - cs-gpios = <0x47 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x49>; - }; - - spi@2000c800 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000c800 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf4 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x02>; - cs-gpios = <0x4a 0x08 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4b 0x4c>; - }; - - spi@2000d800 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000d800 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf5 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x03>; - cs-gpios = <0x4a 0x10 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4d 0x4e>; - }; - - spi@20022000 { - status = "okay"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x20022000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe2 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x04>; - cs-gpios = <0x47 0x02 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4f>; - spi-slave; - - slave@0 { - compatible = "rohm,dh2228fv"; - reg = <0x00>; - spi-max-frequency = <0x989680>; - }; - }; - - spi@20023000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x20023000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe1 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x05>; - cs-gpios = <0x47 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x50>; - }; - - i2s-play@2000e000 { - status = "disable"; - compatible = "snps,designware-i2s"; - reg = <0x00 0x2000e000 0x1000>; - interrupt-names = "play_irq"; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdd 0x04>; - clocks = <0x05 0x61 0x05 0x59>; - clock-names = "i2spclk\0i2sclk"; - play; - channel = <0x02>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x51>; - }; - - i2s-rec@2000f000 { - status = "disable"; - compatible = "snps,designware-i2s"; - reg = <0x00 0x2000f000 0x1000>; - interrupt-names = "record_irq"; - interrupt-parent = <0x01>; - interrupts = <0x00 0xde 0x04>; - clocks = <0x02>; - record; - channel = <0x02>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x52>; - }; - - qspi@00000000 { - status = "okay"; - compatible = "snps,dwc-ssi-1.01a"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x00 0x4000000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd9 0x04>; - clocks = <0x05 0x4d 0x05 0x4e>; - clock-names = "hclk\0wclk"; - work-mode = <0x01>; - reg-io-width = <0x04>; - bst,use-gpio-cs; - spi-rx-bus-width = <0x04>; - spi-tx-bus-width = <0x04>; - cs-gpios = <0x47 0x15 0x00>; - num-cs = <0x01>; - bus-num = <0x06>; - pinctrl-names = "default"; - pinctrl-0 = <0x53>; - - qspi0-nor0@0 { - #address-cells = <0x01>; - #size-cells = <0x01>; - spi-rx-bus-width = <0x01>; - spi-tx-bus-width = <0x01>; - compatible = "jedec,spi-nor"; - status = "okay"; - spi-max-frequency = <0xf4240>; - reg = <0x00>; - mode = <0x00>; - powerctl-fada-gpios = <0x1f 0x1b 0x00>; - powerctl-fadb-gpios = <0x1f 0x1c 0x00>; - - partition@0 { - reg = <0x00 0x1e00000>; - label = "nor0_part0"; - }; - - partition@1e00000 { - reg = <0x1e00000 0x200000>; - label = "nor0_part1"; - }; - }; - }; - - qspi@14000000 { - status = "disable"; - compatible = "snps,dwc-ssi-1.01a"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x14000000 0x4000000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x05 0x4f 0x05 0x50>; - clock-names = "hclk\0wclk"; - work-mode = <0x01>; - reg-io-width = <0x04>; - bst,use-gpio-cs; - spi-rx-bus-width = <0x04>; - spi-tx-bus-width = <0x04>; - cs-gpios = <0x54 0x01 0x00>; - num-cs = <0x01>; - bus-num = <0x07>; - pinctrl-names = "default"; - pinctrl-0 = <0x55>; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x0f>; - snps,rd_osr_lmt = <0x0f>; - snps,axi_fb; - snps,blen = <0x04 0x08 0x10 0x00 0x00 0x00 0x00>; - phandle = <0x56>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x04>; - snps,rx-sched-sp; - phandle = <0x57>; - - queue0 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x00>; - snps,priority = <0x00>; - }; - - queue1 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x01>; - snps,priority = <0x01>; - }; - - queue2 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x02>; - snps,priority = <0x02>; - }; - - queue3 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x03>; - snps,priority = <0x03>; - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x04>; - snps,tx-sched-wrr; - phandle = <0x58>; - - queue0 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x00>; - }; - - queue1 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x01>; - }; - - queue2 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x02>; - }; - - queue3 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x03>; - }; - }; - - thermal@70039000 { - status = "okay"; - compatible = "bst,bst-thermal"; - reg = <0x00 0x70039000 0x1000>; - #thermal-sensor-cells = <0x00>; - phandle = <0x61>; - }; - - ethernet@30000000 { - status = "okay"; - compatible = "bst,dw-eqos-eth"; - reg = <0x00 0x30000000 0x100000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9f 0xff04 0x00 0xa0 0xff04 0x00 0xa1 0xff04 0x00 0xa2 0xff04 0x00 0xa3 0xff04 0x00 0xa4 0xff04 0x00 0xa5 0xff04 0x00 0xa6 0xff04 0x00 0xa7 0xff04 0x00 0xa8 0xff04 0x00 0xa9 0xff04 0x00 0xaa 0xff04>; - interrupt-names = "sbd_irq\0sfty_ce_irq\0sfty_ue_irq\0tx_chan0_irq\0tx_chan1_irq\0tx_chan2_irq\0tx_chan3_irq\0rx_chan0_irq\0rx_chan1_irq\0rx_chan2_irq\0rx_chan3_irq\0eth_lpi"; - ethernet-id = <0x00>; - mac-address = [00 00 00 00 00 00]; - max-frame-size = <0xed8>; - phy-mode = "rgmii"; - snps,multicast-filter-bins = <0x100>; - snps,perfect-filter-entries = <0x80>; - rx-fifo-depth = <0x4000>; - tx-fifo-depth = <0x4000>; - clocks = <0x05 0x0f 0x05 0x13 0x05 0x15 0x05 0x11>; - clock-names = "wclk\0axim_aclk\0pclk\0ptp_ref"; - bst,fix-safety = <0x00>; - bst,dma_int_mode = <0x01>; - snps,fixed-burst; - snps,force_sf_dma_mode; - snps,ps-speed = <0x3e8>; - snps,axi-config = <0x56>; - snps,mtl-rx-config = <0x57>; - snps,mtl-tx-config = <0x58>; - label = "gmac0"; - resets = <0x06 0x11>; - reset-names = "bstgmaceth"; - eth-name = "gmac0"; - eth-number = <0x00>; - mac-mode = "rgmii"; - extend-op = <0x02>; - pinctrl-names = "default"; - pinctrl-0 = <0x59>; - - fixed-link { - speed = <0x3e8>; - full-duplex; - }; - }; - - ethernet@30100000 { - status = "okay"; - compatible = "bst,dw-eqos-eth"; - reg = <0x00 0x30100000 0x100000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xab 0xff04 0x00 0xac 0xff04 0x00 0xad 0xff04 0x00 0xae 0xff04 0x00 0xaf 0xff04 0x00 0xb0 0xff04 0x00 0xb1 0xff04 0x00 0xb2 0xff04 0x00 0xb3 0xff04 0x00 0xb4 0xff04 0x00 0xb5 0xff04 0x00 0xb6 0xff04>; - interrupt-names = "sbd_irq\0sfty_ce_irq\0sfty_ue_irq\0tx_chan0_irq\0tx_chan1_irq\0tx_chan2_irq\0tx_chan3_irq\0rx_chan0_irq\0rx_chan1_irq\0rx_chan2_irq\0rx_chan3_irq\0eth_lpi"; - ethernet-id = <0x01>; - mac-address = [00 00 00 00 00 00]; - max-frame-size = <0xed8>; - phy-mode = "rgmii"; - snps,multicast-filter-bins = <0x100>; - snps,perfect-filter-entries = <0x80>; - rx-fifo-depth = <0x4000>; - tx-fifo-depth = <0x4000>; - clocks = <0x05 0x10 0x05 0x14 0x05 0x16 0x05 0x12>; - clock-names = "wclk\0axim_aclk\0pclk\0ptp_ref"; - bst,fix-safety = <0x00>; - bst,dma_int_mode = <0x01>; - snps,fixed-burst; - snps,force_sf_dma_mode; - snps,ps-speed = <0x3e8>; - snps,axi-config = <0x56>; - snps,mtl-rx-config = <0x57>; - snps,mtl-tx-config = <0x58>; - label = "gmac1"; - resets = <0x06 0x12>; - reset-names = "bstgmaceth"; - pinctrl-names = "default"; - pinctrl-0 = <0x5a>; - extend-op = <0x02>; - eth-name = "gmac1"; - eth-number = <0x01>; - mac-mode = "rgmii"; - phy-handle = <0x5b>; - - mdio1 { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - eth_phy1@1 { - compatible = "marvell,88Q2112\0ethernet-phy-id002B.0983\0ethernet-phy-ieee802.3-c45"; - device_type = "ethernet-phy"; - max-speed = <0x3e8>; - reg = <0x07>; - reset-gpios = <0x1f 0x09 0x01>; - reset-active-low; - reset-assert-us = <0x4e20>; - reset-deassert-us = <0x4e20>; - phandle = <0x5b>; - }; - }; - }; - - phy@30E01000 { - compatible = "bst,dwc-usb-phy"; - #phy-cells = <0x00>; - reg = <0x00 0x30e01000 0x1000>; - usb_mode = "usb20"; - phandle = <0x5e>; - }; - - phy@30E00000 { - compatible = "bst,dwc-usb-phy"; - reg = <0x00 0x30e00000 0x1000>; - #phy-cells = <0x00>; - usb_mode = "usb30"; - pll_type = "internal"; - phandle = <0x5c>; - }; - - usb3 { - compatible = "bst,dwc3usb"; - status = "okay"; - ranges; - #address-cells = <0x02>; - #size-cells = <0x01>; - clock-names = "suspend\0ref\0axi\0apb"; - clocks = <0x05 0x17 0x05 0x18 0x05 0x19 0x05 0x1a>; - resets = <0x06 0x04>; - reset-names = "usb3_reset"; - phys = <0x5c>; - phy-names = "usb-phy"; - pll_type = "internal"; - powerctl-gpios = <0x1f 0x0e 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x5d>; - - dwc3@30200000 { - compatible = "snps,dwc3"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - reg = <0x00 0x30200000 0x100000>; - interrupts = <0x00 0xc8 0x04>; - interrupt-parent = <0x01>; - dr_mode = "host"; - snps,dis_u3_susphy_quirk; - }; - }; - - usb2 { - compatible = "bst,dwc3usb"; - status = "okay"; - ranges; - #address-cells = <0x02>; - #size-cells = <0x01>; - clock-names = "ahb\0ref\0apb"; - clocks = <0x05 0x1b 0x05 0x1d 0x05 0x1c>; - reset-names = "usb2_reset"; - resets = <0x06 0x05>; - phys = <0x5e>; - phy-names = "usb-phy"; - pll_type = "internal"; - - dwc3@30300000 { - status = "okay"; - compatible = "snps,dwc3"; - reg = <0x00 0x30300000 0x100000>; - interrupts = <0x00 0xc9 0x04>; - interrupt-parent = <0x01>; - dr_mode = "peripheral"; - snps,incr-burst-type-adjustment = <0x01 0x04 0x08 0x10>; - snps,reqinfo-for-data-read = <0x08>; - snps,reqinfo-for-descriptor-read = <0x08>; - }; - }; - - dwmmc0@30400000 { - status = "okay"; - compatible = "bst,dwcmshc-sdhci"; - clocks = <0x05 0x1f 0x05 0x1e>; - clock-names = "core\0bus"; - reg = <0x00 0x30400000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xb9 0x04>; - interrupt-names = "IRQDWMMC0"; - #address-cells = <0x01>; - #size-cells = <0x00>; - data-addr = <0x200>; - fifo-watermark-aligned; - clock-frequency = <0x2faf080>; - max-frequency = <0xbebc200>; - min-frequency = <0x61a80>; - broken-64bit-dma; - clear-tarns-mode; - fifo-depth = <0x400>; - card-detect-delay = <0xc8>; - bus-width = <0x08>; - cap-mmc-highspeed; - non-removable; - no-sdio; - no-sd; - keep-power-in-suspend; - no-3-3-v; - sdhci,auto-cmd12; - pinctrl-names = "default"; - pinctrl-0 = <0x5f>; - }; - - dwmmc1@30500000 { - status = "okay"; - compatible = "bst,dwcmshc-sdhci"; - clocks = <0x05 0x21 0x05 0x20>; - clock-names = "core\0bus"; - reg = <0x00 0x30500000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xbd 0x04>; - interrupt-names = "IRQDWMMC1"; - #address-cells = <0x01>; - #size-cells = <0x00>; - data-addr = <0x200>; - fifo-watermark-aligned; - clock-frequency = <0x5f5e100>; - max-frequency = <0xbebc200>; - min-frequency = <0x61a80>; - broken-64bit-dma; - clear-tarns-mode; - fifo-depth = <0x400>; - card-detect-delay = <0xc8>; - bus-width = <0x04>; - cap-sd-highspeed; - sd-uhs-sdr12; - sd-uhs-sdr25; - sd-uhs-sdr50; - no-sdio; - no-mmc; - sdhci,auto-cmd12; - keep-power-in-suspend; - pinctrl-names = "default"; - pinctrl-0 = <0x60>; - }; - - gpu@33300000 { - status = "okay"; - compatible = "arm,mali-450\0arm,mali-utgard"; - reg = <0x00 0x33300000 0x30000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04>; - interrupt-names = "IRQPP0\0IRQPPMMU0\0IRQPP1\0IRQPPMMU1\0IRQGP\0IRQGPMMU\0IRQPMU\0IRQPP"; - clocks = <0x05 0x31 0x05 0x32>; - clock-names = "clk_mali\0clk_mali_apb"; - resets = <0x06 0x0b>; - reset-names = "gpu_reset"; - ppcores = <0x02>; - dedicated_mem_start = <0x00>; - dedicated_mem_size = <0x00>; - }; - - mali-v500@0x55000000 { - status = "okay"; - compatible = "arm,mali-v500"; - reg = <0x00 0x55000000 0xffff>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9a 0x04>; - interrupt-names = "IRQV500"; - resets = <0x06 0x00>; - reset-names = "codec_reset"; - clocks = <0x05 0x3d>; - clock-names = "clk_v500"; - }; - }; - - cooling_dev { - - pwm { - cpumask = <0x0f>; - capacitance = <0x5dc>; - #cooling-cells = <0x02>; - phandle = <0x63>; - }; - }; - - thermal-zones { - - cpu-thermal { - polling-delay-passive = <0x1f4>; - polling-delay = <0x3e8>; - thermal-sensors = <0x61>; - - trips { - - switch_trip { - temperature = <0x15f90>; - hysteresis = <0x7d0>; - type = "passive"; - phandle = <0x62>; - }; - - critical_trip { - temperature = <0x1e848>; - hysteresis = <0x00>; - type = "critical"; - }; - }; - - cooling-maps { - - map0 { - trip = <0x62>; - cooling-device = <0x63 0x00 0x01>; - }; - }; - }; - }; - - pinctrl@70038000 { - status = "okay"; - compatible = "bst,pinctrl-a1000b"; - #address-cells = <0x02>; - #size-cells = <0x02>; - reg = <0x00 0x70038000 0x00 0x1000 0x00 0x33001000 0x00 0x1000>; - reg-names = "aon\0top"; - #gpio-cells = <0x02>; - - spi0_pinctrl { - phandle = <0x48>; - - mux { - pins = "spi0_miso\0spi0_mosi\0spi0_sclk"; - function = "spi0"; - }; - }; - - spi1_pinctrl { - phandle = <0x49>; - - mux { - pins = "spi1_miso\0spi1_mosi\0spi1_sclk"; - function = "spi1"; - }; - }; - - spi2_pinctrl { - phandle = <0x4b>; - - mux { - pins = "uart0_cts\0uart1_cts\0uart1_rts"; - function = "spi2"; - }; - }; - - spi2_cs_pinctrl { - phandle = <0x4c>; - - mux { - pins = "uart0_rts"; - function = "gpio"; - }; - }; - - spi3_pinctrl { - phandle = <0x4d>; - - mux { - pins = "uart2_cts\0uart3_cts\0uart3_rts"; - function = "spi3"; - }; - }; - - spi3_cs_pinctrl { - phandle = <0x4e>; - - mux { - pins = "uart2_rts"; - function = "gpio"; - }; - }; - - spi4_pinctrl { - phandle = <0x4f>; - - mux { - pins = "spi0_miso\0spi0_mosi\0spi0_sclk\0spi0_cs"; - function = "spi0_s"; - }; - }; - - spi5_pinctrl { - phandle = <0x50>; - - mux { - pins = "spi1_miso\0spi1_mosi\0spi1_sclk"; - function = "spi1_s"; - }; - }; - - i2c0_pinctrl { - phandle = <0x1c>; - - mux { - pins = "i2c0_scl\0i2c0_sda"; - function = "i2c0"; - }; - }; - - i2c1_pinctrl { - phandle = <0x1d>; - - mux { - pins = "i2c1_scl\0i2c1_sda"; - function = "i2c1"; - }; - }; - - i2c2_pinctrl { - phandle = <0x1e>; - - mux { - pins = "i2c2_scl\0i2c2_sda"; - function = "i2c2"; - }; - }; - - i2c3_pinctrl { - phandle = <0x37>; - - mux { - pins = "i2c3_scl\0i2c3_sda"; - function = "i2c3"; - }; - }; - - i2c4_pinctrl { - phandle = <0x38>; - - mux { - pins = "i2c4_scl\0i2c4_sda"; - function = "i2c4"; - }; - }; - - i2c5_pinctrl { - phandle = <0x39>; - - mux { - pins = "i2c5_scl\0i2c5_sda"; - function = "i2c5"; - }; - }; - - uart0_pinctrl { - phandle = <0x07>; - - mux { - pins = "uart0_txd\0uart0_rxd"; - function = "uart0"; - }; - }; - - uart1_pinctrl { - phandle = <0x08>; - - mux { - pins = "uart1_txd\0uart1_rxd"; - function = "uart1"; - }; - }; - - uart2_pinctrl { - phandle = <0x09>; - - mux { - pins = "uart2_txd\0uart2_rxd"; - function = "uart2"; - }; - }; - - uart3_pinctrl { - phandle = <0x0a>; - - mux { - pins = "uart3_txd\0uart3_rxd"; - function = "uart3"; - }; - }; - - gpio0_pinctrl { - - mux { - pins = "gpio_29"; - function = "gpio"; - }; - }; - - gpio_special_func_pinctrl { - - mux { - pins = "gpio_24\0gpio_29\0debug4\0debug5\0uart0_cts\0uart0_rts"; - function = "gpio"; - }; - }; - - qspi0_pinctrl { - phandle = <0x53>; - - mux { - pins = "qspi0_cs0"; - function = "gpio"; - }; - }; - - usb3_pinctrl { - phandle = <0x5d>; - - mux { - pins = "gpio_14"; - function = "gpio"; - }; - }; - - qspi1_pinctrl { - phandle = <0x55>; - - mux { - pins = "qspi1_cs1"; - function = "gpio"; - }; - }; - - des_960_1_pinctrl { - - mux { - pins = "qspi1_io1"; - function = "gpio"; - }; - }; - - des_960_2_pinctrl { - - mux { - pins = "qspi1_io3"; - function = "gpio"; - }; - }; - - des_960_3_pinctrl { - - mux { - pins = "qspi1_io5"; - function = "gpio"; - }; - }; - - bist_pinctrl { - - mux { - pins = "spi1_mosi"; - function = "bist"; - }; - }; - - - - - - i2s0_pinctrl { - phandle = <0x51>; - - mux { - pins = "i2s0_ck\0i2s0_mck\0i2s0_sd_out\0i2s0_ws\0qspi0_io4"; - function = "i2s0"; - }; - }; - - i2s1_pinctrl { - phandle = <0x52>; - - mux { - pins = "i2s1_ck\0i2s1_sd_in\0i2s1_ws"; - function = "i2s1"; - }; - }; - - isp_pinctrl { - phandle = <0x66>; - - mux { - pins = "isp_fsync0\0isp_fsync1\0isp_fsync2\0isp_fsync3"; - function = "isp"; - }; - }; - - ptp_pinctrl { - - mux { - pins = "ptp_pps0\0ptp_pps1\0ptp_clk"; - function = "ptp"; - }; - }; - - ptp_pinconfig { - - config { - pins = "ptp_clk"; - drive-strength = <0x02>; - input-enable; - }; - }; - - err_rpt_l0_pinctrl { - - mux { - pins = "err_rpt_l0_n\0err_rpt_l0_p"; - function = "err_rpt_l0"; - }; - }; - - err_rpt_gpio_l0_pinctrl { - - mux { - pins = "err_rpt_l0_n\0err_rpt_l0_p"; - function = "gpio"; - }; - }; - - err_rpt_l1_pinctrl { - - mux { - pins = "err_rpt_l1_n\0err_rpt_l1_p"; - function = "err_rpt_l1"; - }; - }; - - pwm_lsp0_pwm0_pinctrl { - phandle = <0x43>; - - mux { - pins = "pwm0"; - function = "pwm"; - }; - }; - - pwm_lsp0_pwm1_pinctrl { - phandle = <0x44>; - - mux { - pins = "pwm1"; - function = "pwm"; - }; - }; - - pwm_lsp1_pwm0_pinctrl { - phandle = <0x45>; - - mux { - pins = "pwm2"; - function = "pwm"; - }; - }; - - pwm_lsp1_pwm1_pinctrl { - phandle = <0x46>; - - mux { - pins = "pwm3"; - function = "pwm"; - }; - }; - - ts_pinctrl { - - mux { - pins = "ts_trig_in00\0ts_trig_in01\0ts_trig_in10\0ts_trig_in11"; - function = "ts"; - }; - }; - - sdemmc0_pinctrl { - phandle = <0x15>; - - mux { - pins = "sdemmc0_clk\0sdemmc0_cmd\0sdemmc0_dat0\0sdemmc0_dat1\0sdemmc0_dat2\0sdemmc0_dat3\0sdemmc0_dat4\0sdemmc0_dat5\0sdemmc0_dat6\0sdemmc0_dat7\0sdemmc0_rstb\0sdemmc0_cdn\0sdemmc0_wp"; - function = "sdemmc0"; - }; - }; - - sdemmc0_pinconfig { - phandle = <0x5f>; - - config { - pins = "sdemmc0_clk\0sdemmc0_cmd\0sdemmc0_dat0\0sdemmc0_dat1\0sdemmc0_dat2\0sdemmc0_dat3\0sdemmc0_dat4\0sdemmc0_dat5\0sdemmc0_dat6\0sdemmc0_dat7\0sdemmc0_rstb\0sdemmc0_cdn\0sdemmc0_wp"; - drive-strength = <0x02>; - input-enable; - }; - }; - - sdemmc1_pinctrl { - phandle = <0x16>; - - mux { - pins = "sdemmc1_clk\0sdemmc1_cmd\0sdemmc1_dat0\0sdemmc1_dat1\0sdemmc1_dat2\0sdemmc1_dat3\0sdemmc1_dat4\0sdemmc1_dat5\0sdemmc1_dat6\0sdemmc1_dat7\0sdemmc1_rstb\0sdemmc1_cdn\0sdemmc1_wp"; - function = "sdemmc1"; - }; - }; - - sdemmc1_pinconfig { - phandle = <0x60>; - - config { - pins = "sdemmc1_clk\0sdemmc1_cmd\0sdemmc1_dat0\0sdemmc1_dat1\0sdemmc1_dat2\0sdemmc1_dat3\0sdemmc1_dat4\0sdemmc1_dat5\0sdemmc1_dat6\0sdemmc1_dat7\0sdemmc1_rstb\0sdemmc1_cdn\0sdemmc1_wp"; - drive-strength = <0x0f>; - input-enable; - }; - }; - - debug_pinctrl { - phandle = <0x17>; - - mux { - pins = "debug0\0debug1\0debug2\0debug3\0debug4\0debug5\0debug6\0debug7"; - function = "debug"; - }; - }; - - strap_pinctrl { - - mux { - pins = "gpio_24\0gpio_25\0gpio_26\0gpio_27\0gpio_28\0gpio_29\0spi1_sclk\0i2s0_mck\0i2s0_ck\0gpio_107\0gpio_108"; - function = "strap"; - }; - }; - - vout_pinctrl { - phandle = <0x18>; - - mux { - pins = "vout_r0\0vout_r1\0vout_r2\0vout_r3\0vout_r4\0vout_r5\0vout_r6\0vout_r7\0vout_g0\0vout_g1\0vout_g2\0vout_g3\0vout_g4\0vout_g5\0vout_g6\0vout_g7\0vout_b0\0vout_b1\0vout_b2\0vout_b3\0vout_b4\0vout_b5\0vout_b6\0vout_b7\0vout_hs\0vout_vs\0vout_de\0vout_pclk\0vout_pdb"; - function = "vout"; - }; - }; - - vout_pinconfig { - - config { - pins = "vout_r0\0vout_r1\0vout_r2\0vout_r3\0vout_r4\0vout_r5\0vout_r6\0vout_r7\0vout_g0\0vout_g1\0vout_g2\0vout_g3\0vout_g4\0vout_g5\0vout_g6\0vout_g7\0vout_b0\0vout_b1\0vout_b2\0vout_b3\0vout_b4\0vout_b5\0vout_b6\0vout_b7\0vout_hs\0vout_vs\0vout_de\0vout_pclk\0vout_pdb"; - drive-strength = <0x02>; - input-enable; - }; - }; - - vin_pinctrl { - phandle = <0x19>; - - mux { - pins = "vin_b0\0vin_b1\0vin_b2\0vin_b3\0vin_b4\0vin_de\0vin_g0\0vin_g1\0vin_g2\0vin_g3\0vin_g4\0vin_g5\0vin_hs\0vin_llc\0vin_r0\0vin_r1\0vin_r2\0vin_r3\0vin_r4\0vin_vs"; - function = "vin"; - }; - }; - - vin_pinconfig { - - config { - pins = "vin_b0\0vin_b1\0vin_b2\0vin_b3\0vin_b4\0vin_de\0vin_g0\0vin_g1\0vin_g2\0vin_g3\0vin_g4\0vin_g5\0vin_hs\0vin_llc\0vin_r0\0vin_r1\0vin_r2\0vin_r3\0vin_r4\0vin_vs"; - drive-strength = <0x02>; - input-enable; - }; - }; - - rgmii0_pinctrl { - phandle = <0x59>; - - mux { - pins = "rgmii0_txd0\0rgmii0_txd1\0rgmii0_txd2\0rgmii0_txd3\0gmii0_txd4\0gmii0_txd5\0gmii0_txd6\0gmii0_txd7\0gmii0_txer\0rgmii0_txctrl\0mii0_txclk\0rgmii0_gtxclk\0rgmii0_rxd0\0rgmii0_rxd1\0rgmii0_rxd2\0rgmii0_rxd3\0gmii0_rxd4\0gmii0_rxd5\0gmii0_rxd6\0gmii0_rxd7\0gmii0_rxer\0rgmii0_rxctrl\0rgmii0_rxclk\0rgmii0_mdio\0rgmii0_mdc\0rgmii0_intr"; - function = "rgmii0"; - }; - }; - - rgmii0_pinconfig { - - config { - pins = "rgmii0_gtxclk\0rgmii0_mdc\0rgmii0_mdio\0rgmii0_rxclk\0rgmii0_rxctrl\0rgmii0_rxd0\0rgmii0_rxd1\0rgmii0_rxd2\0rgmii0_rxd3\0rgmii0_txctrl\0rgmii0_txd0\0rgmii0_txd1\0rgmii0_txd2\0rgmii0_txd3"; - drive-strength = <0x02>; - input-enable; - }; - }; - - rgmii1_pinctrl { - phandle = <0x5a>; - - mux { - pins = "rgmii1_txd0\0rgmii1_txd1\0rgmii1_txd2\0rgmii1_txd3\0mii1_rxer\0mii1_txclk\0rgmii1_txctrl\0rgmii1_gtxclk\0rgmii1_rxd0\0rgmii1_rxd1\0rgmii1_rxd2\0rgmii1_rxd3\0rgmii1_rxctrl\0rgmii1_rxclk\0rgmii1_mdc\0rgmii1_mdio\0rgmii1_intr"; - function = "rgmii1"; - }; - }; - - rgmii1_pinconfig { - - config { - pins = "rgmii1_gtxclk\0rgmii1_mdc\0rgmii1_mdio\0rgmii1_rxclk\0rgmii1_rxctrl\0rgmii1_rxd0\0rgmii1_rxd1\0rgmii1_rxd2\0rgmii1_rxd3\0rgmii1_txctrl\0rgmii1_txd0\0rgmii1_txd1\0rgmii1_txd2\0rgmii1_txd3"; - drive-strength = <0x02>; - input-enable; - }; - }; - - gmii0_pinconfig { - - config { - pins = "gmii0_rxd4\0gmii0_rxd5\0gmii0_rxd6\0gmii0_rxd7\0gmii0_rxer\0gmii0_txd4\0gmii0_txd5\0gmii0_txd6\0gmii0_txd7\0gmii0_txer"; - drive-strength = <0x02>; - input-enable; - }; - }; - - ssd_pinctrl { - - mux { - pins = "sdemmc0_led_ctl\0sdemmc1_led_ctl\0gpio_26\0gpio_27"; - function = "gpio"; - }; - }; - - gnss_pinctrl { - - mux { - pins = "gpio_31\0gpio_12\0pwm1"; - function = "gpio"; - }; - }; - - fan_pinctrl { - phandle = <0x1a>; - - mux { - pins = "gpio_27\0gpio_28"; - function = "gpio"; - }; - }; - - - - - board_special_func_pinctrl { - phandle = <0x1b>; - - mux { - pins = "vout_pdb\0gpio_31"; - function = "gpio"; - }; - }; - }; - - isp { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,a1000b-isp"; - memory-region = <0x64 0x65>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x06>; - isp-fw-fbuf-addr = <0xa2000000>; - isp-fw-fbuf-size = <0x10000000>; - dma-coherent; - pinctrl-names = "default"; - pinctrl-0 = <0x66>; - - core@0 { - id = <0x00>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x67>; - phandle = <0x72>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x68>; - phandle = <0x73>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x69>; - phandle = <0x74>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x6a>; - phandle = <0x75>; - }; - }; - }; - }; - - core@1 { - id = <0x01>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@4 { - remote-endpoint = <0x6b>; - phandle = <0x77>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@5 { - remote-endpoint = <0x6c>; - phandle = <0x78>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@6 { - remote-endpoint = <0x6d>; - phandle = <0x79>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@7 { - remote-endpoint = <0x6e>; - phandle = <0x7a>; - }; - }; - }; - }; - - core@2 { - id = <0x02>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@8 { - remote-endpoint = <0x6f>; - phandle = <0x7c>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@9 { - remote-endpoint = <0x70>; - phandle = <0x7d>; - }; - }; - }; - }; - }; - - csi@0 { - compatible = "bst,a1000b_csi2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - lane-speed = <0x960>; - id = <0x00>; - resets = <0x06 0x0d>; - reset-names = "csi0_reset"; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x71>; - phandle = <0x20>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x72>; - phandle = <0x67>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x73>; - phandle = <0x68>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x74>; - phandle = <0x69>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x75>; - phandle = <0x6a>; - }; - }; - }; - }; - - csi@1 { - compatible = "bst,a1000b_csi2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - lane-speed = <0x640>; - id = <0x01>; - resets = <0x06 0x0e>; - reset-names = "csi1_reset"; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x76>; - phandle = <0x25>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x77>; - phandle = <0x6b>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x78>; - phandle = <0x6c>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x79>; - phandle = <0x6d>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x7a>; - phandle = <0x6e>; - }; - }; - }; - }; - - csi@3 { - compatible = "bst,a1000b-csi2-2x2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - lane-speed = <0x960>; - resets = <0x06 0x10>; - reset-names = "csi2_reset"; - id = <0x03>; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x7b>; - phandle = <0x2a>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x7c>; - phandle = <0x6f>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x7d>; - phandle = <0x70>; - }; - }; - }; - }; - - chosen { - bootargs = "earlycon=uart8250,mmio32,0x20008000 console=ttyS0,115200n8 memreserve=64M@0xf8000000 rdinit=/sbin/init root=/dev/mmcblk0p7 rw rodata=n"; - stdout-path = "uart0"; - }; - - aliases { - uart0 = "/amba_apu/serial@20008000"; - }; - - memory@90000000 { - device_type = "memory"; - reg = <0x00 0x90000000 0x00 0x60000000>; - }; - - // memory@1b0000000 { - // device_type = "memory"; - // reg = <0x01 0xb0000000 0x00 0x40000000>; - // }; - - reserved-memory { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - pcie_ctrl@8fd00000 { - compatible = "bst,pcie-ctrl"; - reg = <0x00 0x8fd00000 0x00 0x100000>; - no-map; - phandle = <0x0c>; - }; - - bst_atf@8b000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8b000000 0x00 0x2000000>; - no-map; - }; - - bst_tee@8fec0000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8fec0000 0x00 0x40000>; - no-map; - phandle = <0x81>; - }; - - bstn_cma@8ff00000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8ff00000 0x00 0x100000>; - no-map; - phandle = <0x7e>; - }; - - bstn@90000000 { - compatible = "bst,bstn"; - reg = <0x00 0x90000000 0x00 0x2000000>; - no-map; - }; - - bst_lwnn@92000000 { - compatible = "bst,bst_lwnn"; - reg = <0x00 0x92000000 0x00 0x2000000>; - no-map; - }; - - bst_lwnn@94000000 { - compatible = "bst,bst_lwnn"; - reg = <0x00 0x94000000 0x00 0x2000000>; - no-map; - }; - - bst_cv@96000000 { - compatible = "bst,bst_cv"; - reg = <0x00 0x96000000 0x00 0x4000000>; - no-map; - }; - - bst_cv_cma@9a000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x9a000000 0x00 0x2000000>; - align-shift = <0x08>; - no-map; - phandle = <0x7f>; - }; - - vsp@0x9c000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x9c000000 0x00 0x1000000>; - no-map; - phandle = <0x80>; - }; - - vsp_fw@0x9d000000 { - reg = <0x00 0x9d000000 0x00 0x4000000>; - no-map; - }; - - bst_isp@0xa1000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0xa1000000 0x00 0x1000000>; - no-map; - phandle = <0x64>; - }; - - bst_isp_fw@0xa2000000 { - reg = <0x00 0xa2000000 0x00 0x10000000>; - no-map; - }; - - coreip_pub_cma@0xb2000000 { - compatible = "shared-dma-pool"; - align-shift = <0x08>; - reg = <0x00 0xb2000000 0x00 0x36000000>; - reusable; - phandle = <0x65>; - }; - - noc_pmu@0xe8000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0xe8000000 0x00 0x800000>; - reusable; - phandle = <0x42>; - }; - - - ddr0@0xf0000000 { - reg = <0x00 0xf0000000 0x00 0x10000000>; - no-map; - }; - - ddr1@0x1f0000000 { - reg = <0x01 0xf0000000 0x00 0x10000000>; - no-map; - }; - }; - - mbox-poll-clients { - compatible = "bst,ipc-mbox-client"; - reg = <0xfec00020 0x08 0x52030090 0x08 0x53090008 0x08 0xfec00028 0x08>; - #mbox-cells = <0x01>; - phandle = <0x0e>; - }; - - bstn-mbox { - compatible = "bstn,bstn-mbox"; - reg = <0x00 0x33102000 0x00 0x2000 0x00 0x33100000 0x00 0x2000 0x00 0x80000000 0x00 0x04 0x00 0x80002000 0x00 0x04>; - fpga-reset = <0x01>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x71 0xff04 0x00 0x72 0xff04 0x00 0x73 0xff04 0x00 0x74 0xff04 0x00 0x75 0xff04 0x00 0x76 0xff04 0x00 0x77 0xff04 0x00 0x78 0xff04>; - #mbox-cells = <0x01>; - phandle = <0xea>; - }; - - bstn@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bstn-a1000b,cma"; - reg = <0x00 0x50020000 0x00 0x100 0x00 0x90000000 0x00 0x2000000>; - memory-region = <0x65>; - rmem-base = <0x00 0xb2000000>; - rmem-size = <0x00 0x36000000>; - id = <0x00>; - assigned-mem-size = <0x1000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x08>; - firmware = "bstn_dsp_rtos.rbf"; - }; - - bst_cv@0x51030000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst_cv,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x96000000 0x00 0x2000000 0x00 0x98000000 0x00 0x2000000 0x00 0x92000000 0x00 0x2000000 0x00 0x94000000 0x00 0x2000000>; - memory-region = <0x7f>; - assigned-mem-size = <0x4000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x09>; - dsp-num = <0x04>; - ipc-register-addr = <0x8ff00000>; - - dsp@0x96000000 { - index = <0x00>; - firmware = "bst_cv_dsp_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x967ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x98000000 { - index = <0x01>; - firmware = "bst_cv_dsp1_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x987ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x92000000 { - index = <0x02>; - firmware = "bst_cv_dsp2_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x927ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x94000000 { - index = <0x03>; - firmware = "bst_cv_dsp3_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x947ff000>; - ipc-src-core = <0x03>; - }; - }; - - bst_gwarp_scaler@0x51060000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst_gwarp_scaler,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x51050000 0x00 0x100 0x00 0x51060000 0x00 0x100>; - memory-region = <0x65>; - id = <0x00>; - bus-offset = <0x00 0x00>; - assigned-mem-size = <0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9b 0x04>; - interrupt-names = "bst_cv_irq"; - }; - - bare_cv@51030000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bare_cv,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x96000000 0x00 0x1000000 0x00 0x98000000 0x00 0x1000000 0x00 0x97000000 0x00 0x1000000 0x00 0x99000000 0x00 0x1000000>; - memory-region = <0x7f>; - id = <0x01>; - assigned-mem-size = <0x2000000 0x2000000 0x2000000 0x2000000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x09>; - firmware = "bstcv0.rbf\0bstcv1.rbf\0bstcv2.rbf\0bstcv3.rbf"; - }; - - bst_lwnn@0x51030000 { - compatible = "bst,bst_lwnn-a1000b,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x92000000 0x00 0x2000000 0x00 0x94000000 0x00 0x2000000>; - memory-region = <0x65>; - bus-offset = <0x00 0x00>; - mbox-names = "bst-lwnn-mbox"; - dsp-num = <0x02>; - ipc-register-addr = <0x8ff00000>; - - dsp@0x92000000 { - index = <0x02>; - firmware = "bst_lwnn_dsp2_rt.rbf"; - assigned-mem-size = <0x2000>; - rt-init-addr = <0x927ff000>; - ipc-src-core = <0x06>; - }; - - dsp@0x94000000 { - index = <0x03>; - firmware = "bst_lwnn_dsp3_rt.rbf"; - assigned-mem-size = <0x2000>; - rt-init-addr = <0x947ff000>; - ipc-src-core = <0x00>; - }; - }; - - ipc_vsp@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-vsp-ipc"; - reg = <0x00 0x9c000000 0x00 0x100000 0x00 0x9c100000 0x00 0x80000 0x00 0x9c180000 0x00 0x80000 0x00 0x53090004 0x00 0x04 0x00 0x53090010 0x00 0x0c 0x00 0x33102fbc 0x00 0x04 0x00 0x9d000000 0x00 0x4000000>; - memory-region = <0x80>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x07>; - }; - - vsp@1 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-vsp"; - memory-region = <0x65>; - assigned-mem-size = <0x1000>; - clocks = <0x05 0x43>; - clock-names = "vout_display_clk"; - output-format = "HDMI_RGB"; - output-hsize = <0x780>; - output-vsize = <0x438>; - output-fresh = <0x3c>; - }; - - gmwarp@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-gmwarp"; - memory-region = <0x65>; - }; - - encoder@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-encoder"; - memory-region = <0x65>; - }; - - codec_dma_buf@0 { - compatible = "bst,dma_buf_te"; - memory-region = <0x65>; - }; - - firmware { - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - chip-number = <0x02>; - }; - }; - - tee { - #address-cells = <0x02>; - #size-cells = <0x02>; - reg = <0x00 0x18060000 0x00 0x20000>; - memory-region = <0x81>; - mbox-names = "bstn-mbox"; - chip-number = <0x01>; - }; - - ipc_arm0@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm0"; - reg = <0x04 0xfec00000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x00>; - }; - - ipc_arm3@3 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm3"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x03>; - }; - - ipc_arm2@2 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm2"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x02>; - }; - - ipc_arm1@1 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm1"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x01>; - }; - - ipc@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,ipc"; - reg = <0x04 0xfec00000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7e>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x05>; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fadb.dts b/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fadb.dts deleted file mode 100644 index 287b9526d..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8-fadb.dts +++ /dev/null @@ -1,3378 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000018000000 0x0000000000100000; -/ { - compatible = "bst,a1000b"; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "BST A1000B FAD-B"; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - - cpu@0 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x00>; - cpu-idle-states = <0x04>; - phandle = <0x3b>; - }; - - cpu@1 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x100>; - cpu-idle-states = <0x04>; - phandle = <0x3c>; - }; - - cpu@2 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x200>; - cpu-idle-states = <0x04>; - phandle = <0x3d>; - }; - - cpu@3 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x300>; - cpu-idle-states = <0x04>; - phandle = <0x3e>; - }; - - cpu@4 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x400>; - cpu-idle-states = <0x04>; - phandle = <0x3f>; - }; - - cpu@5 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x500>; - cpu-idle-states = <0x04>; - phandle = <0x40>; - }; - - cpu@6 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x600>; - cpu-idle-states = <0x04>; - phandle = <0x41>; - }; - - cpu@7 { - compatible = "arm,cortex-a55\0arm,armv8"; - device_type = "cpu"; - enable-method = "psci"; - next-level-cache = <0x03>; - reg = <0x700>; - cpu-idle-states = <0x04>; - phandle = <0x42>; - }; - - l2-cache0 { - compatible = "cache"; - phandle = <0x03>; - }; - }; - - psci { - compatible = "arm,psci"; - method = "hvc"; - cpu_on = <0xc4000003>; - cpu_off = <0x84000002>; - cpu_suspend = <0xc4000001>; - }; - - misc_clk { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x3d0900>; - phandle = <0x02>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupt-parent = <0x01>; - interrupts = <0x01 0x0d 0xff08 0x01 0x0e 0xff08 0x01 0x0b 0xff08 0x01 0x0a 0xff08>; - }; - - amba_apu@0 { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x00 0x00 0xffffffff>; - - a1000bclkc@33002000 { - compatible = "bst,a1000b-clkc"; - osc-clk-frequency = <0x17d7840>; - rgmii0-rxclk-frequency = <0x7735940>; - rgmii1-rxclk-frequency = <0x7735940>; - ptp_clk-frequency = <0x7735940>; - #clock-cells = <0x01>; - reg = <0x00 0x33002000 0x1000 0x00 0x70035000 0x1000 0x00 0x20020000 0x1000 0x00 0x20021000 0x1000>; - phandle = <0x05>; - }; - - a1000rstc@0x3300217c { - compatible = "bst,a1000b-rstc"; - #reset-cells = <0x01>; - reg = <0x00 0x3300217c 0x04 0x00 0x33002180 0x04 0x00 0x70035008 0x04 0x00 0x20020000 0x04 0x00 0x20021000 0x04>; - phandle = <0x06>; - }; - - interrupt-controller@32000000 { - compatible = "arm,gic-400"; - #interrupt-cells = <0x03>; - interrupt-controller; - reg = <0x00 0x32001000 0x1000 0x00 0x32002000 0x2000 0x00 0x32004000 0x2000 0x00 0x32006000 0x2000>; - interrupt-parent = <0x01>; - interrupts = <0x01 0x09 0xff04>; - phandle = <0x01>; - }; - - a1000noc { - compatible = "bst,a1000-noc"; - echo-args = <0x01>; - noc-arg-num = <0x2a>; - noc-args = <0x3342000c 0x00 0x33420008 0x505 0x3342008c 0x00 0x33420088 0x505 0x3342010c 0x00 0x33420108 0x505 0x3342018c 0x00 0x33420188 0x505 0x3342020c 0x00 0x33420208 0x505 0x3342028c 0x00 0x33420288 0x505 0x3342030c 0x00 0x33420308 0x505 0x3342038c 0x00 0x33420388 0x303 0x3342040c 0x00 0x33420408 0x303 0x3342048c 0x00 0x33420488 0x303 0x3342050c 0x00 0x33420508 0x303 0x3342058c 0x00 0x33420588 0x303 0x3342060c 0x00 0x33420608 0x303 0x3342068c 0x00 0x33420688 0x303 0x3342070c 0x00 0x33420708 0x00 0x3342078c 0x00 0x33420788 0x707 0x3342080c 0x00 0x33420808 0x707 0x3342088c 0x00 0x33420888 0x707 0x3342090c 0x00 0x33420908 0x707 0x3340010c 0x00 0x33400108 0x505 0x3340018c 0x00 0x33400188 0x505>; - }; - - serial@20008000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x20008000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd5 0x04>; - clocks = <0x05 0x54 0x05 0x66>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x1d>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x07>; - }; - - serial@2000a000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x2000a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd6 0x04>; - clocks = <0x05 0x55 0x05 0x67>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x17>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x08>; - }; - - serial@2000b000 { - status = "disable"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x2000b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x05 0x70 0x05 0x7d>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x29>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x09>; - }; - - serial@20009000 { - status = "okay"; - compatible = "snps,dw-apb-uart"; - reg = <0x00 0x20009000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x05 0x6f 0x05 0x7e>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x06 0x25>; - resets-names = "uart_reset"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x0a>; - }; - - dma-controller@33200000 { - status = "okay"; - compatible = "bst,dw-axi-gdma"; - reg = <0x00 0x33200000 0x1000>; - clocks = <0x05 0x2f 0x05 0x30>; - clock-names = "core-clk\0cfgr-clk"; - resets = <0x06 0x0c>; - reset-names = "gdma_reset"; - dma-channels = <0x08>; - snps,dma-masters = <0x01>; - snps,data-width = <0x04>; - snps,block-size = <0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000>; - snps,priority = <0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07>; - snps,axi-max-burst-len = <0x10>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x90 0xff04 0x00 0x91 0xff04 0x00 0x92 0xff04 0x00 0x93 0xff04 0x00 0x94 0xff04 0x00 0x95 0xff04 0x00 0x96 0xff04 0x00 0x97 0xff04 0x00 0x98 0xff04>; - support-slave; - }; - - pcie-phy@30E02000 { - reg = <0x00 0x30e02000 0x1000>; - reg-names = "phy-base"; - dmc-lane = <0x01>; - dmc-mode = <0x02>; - pcie-ctl0 = <0x01>; - pcie-ctl1 = <0x01>; - phandle = <0x0b>; - }; - - pcie@30600000 { - status = "disable"; - compatible = "bst,dw-pcie-rc"; - device_type = "pci"; - controller-id = <0x00>; - bus-range = <0x00 0xff>; - linux,pci-domain = <0x00>; - reg = <0x00 0x30600000 0x10000 0x00 0x30700000 0x10000 0x00 0x30900000 0x40000 0x00 0x30980000 0x40000 0x00 0x40000000 0x40000>; - reg-names = "dbi\0dbi2\0atu\0dma\0config"; - num-iatu = <0x04>; - num-viewport = <0x04>; - #address-cells = <0x03>; - #size-cells = <0x02>; - ranges = <0x81000000 0x00 0x41000000 0x00 0x41000000 0x00 0x1000000 0x83000000 0x00 0x42000000 0x00 0x42000000 0x00 0x4000000>; - dma-ranges = <0x3000000 0x00 0x80000000 0x00 0x80000000 0x02 0x00>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc5 0x04 0x00 0xbf 0x04 0x00 0xc0 0x04 0x00 0xc1 0x04 0x00 0xc6 0x04>; - interrupt-names = "sys\0dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - pcie-phy = <0x0b>; - max-link-speed = <0x03>; - num-lanes = <0x02>; - }; - - pcie0_ep@30600000 { - status = "disable"; - compatible = "bst,dw-pcie-ep"; - device_type = "pci"; - controller-id = <0x00>; - bus-range = <0x00 0x04>; - reg = <0x00 0x30600000 0x40000 0x00 0x30700000 0x40000 0x00 0x30900000 0x40000 0x00 0x30980000 0x40000 0x00 0x40000000 0x100000>; - reg-names = "dbi\0dbi2\0atu\0dma\0addr_space"; - num-ib-windows = <0x06>; - num-ob-windows = <0x06>; - max-functions = [04]; - pcie-phy = <0x0b>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc5 0x04 0x00 0xbf 0x04 0x00 0xc0 0x04 0x00 0xc1 0x04 0x00 0xc6 0x04>; - interrupt-names = "sys\0dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - }; - - pcie@30a00000 { - status = "disable"; - compatible = "bst,dw-pcie-ep"; - device_type = "pci"; - controller-id = <0x01>; - bus-range = <0x00 0x04>; - reg = <0x00 0x30a00000 0x40000 0x00 0x30b00000 0x40000 0x00 0x30d00000 0x40000 0x00 0x30d80000 0x40000 0x00 0x48000000 0x1000000>; - reg-names = "dbi\0dbi2\0atu\0dma\0addr_space"; - num-ib-windows = <0x06>; - num-ob-windows = <0x06>; - max-functions = [04]; - pcie-phy = <0x0b>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xc2 0x04 0x00 0xc3 0x04 0x00 0xc4 0x04 0x00 0xc7 0x04>; - interrupt-names = "dma\0correctable\0uncorrectable\0other"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x00>; - interrupt-map = <0x00 0x00 0x00 0x00 0x01 0x00 0xc5 0x04>; - max-link-speed = <0x03>; - num-lanes = <0x02>; - picp-ctl = "dma"; - ob-memaddr-def = <0x0c>; - }; - - pcie_vnet@0 { - status = "disable"; - compatible = "bst,pcie-vnet"; - vnet-id = <0x00>; - dma-chan-num = <0x01>; - rx_queues = <0x01>; - tx_queues = <0x01>; - tx-fifo-depth = <0x100>; - rx-fifo-depth = <0x100>; - extend-op = <0x11>; - memory-region = <0x0c>; - }; - - pcie_vnet@1 { - status = "disable"; - compatible = "bst,pcie-vnet"; - vnet-id = <0x01>; - dma-chan-num = <0x01>; - rx_queues = <0x01>; - tx_queues = <0x01>; - tx-fifo-depth = <0x100>; - rx-fifo-depth = <0x100>; - }; - - - gpio@20010000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,dw-apb-gpio"; - reg = <0x00 0x20010000 0x1000>; - clocks = <0x05 0x62>; - clock-names = "bus"; - resets = <0x06 0x1c>; - resets-names = "gpio0_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c>; - - gpio-controller@0 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x00>; - chipnum-base = <0x00>; - interrupt-controller; - #interrupt-cells = <0x03>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdf 0x04 0x00 0xe3 0x04 0x00 0xe4 0x04 0x00 0xe5 0x04 0x00 0xe6 0x04 0x00 0xe7 0x04 0x00 0xe8 0x04 0x00 0xe9 0x04 0x00 0xea 0x04>; - phandle = <0x20>; - }; - - gpio-controller@1 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x01>; - chipnum-base = <0x20>; - phandle = <0x4b>; - }; - - gpio-controller@2 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x02>; - chipnum-base = <0x40>; - phandle = <0x48>; - }; - - gpio-controller@3 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x03>; - chipnum-base = <0x60>; - phandle = <0x55>; - }; - }; - - gpio@20011000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,dw-apb-gpio"; - reg = <0x00 0x20011000 0x1000>; - clocks = <0x05 0x82>; - clock-names = "bus"; - resets = <0x06 0x27>; - resets-names = "gpio1_reset"; - - gpio-controller@0 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x00>; - chipnum-base = <0x80>; - interrupt-controller; - #interrupt-cells = <0x03>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe0 0x04>; - }; - - gpio-controller@1 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x01>; - chipnum-base = <0xa0>; - }; - - gpio-controller@2 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x02>; - chipnum-base = <0xc0>; - }; - - gpio-controller@3 { - compatible = "snps,dw-apb-gpio-port"; - gpio-controller; - #gpio-cells = <0x02>; - snps,nr-gpios = <0x20>; - reg = <0x03>; - chipnum-base = <0xe0>; - phandle = <0x0d>; - }; - }; - - watchdog@2001a000 { - status = "disable"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5d 0x04>; - clocks = <0x05 0x5a 0x05 0x64>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x1f>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt0"; - }; - - watchdog@2001b000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5e 0x04>; - clocks = <0x05 0x5b 0x05 0x65>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x20>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt1"; - }; - - watchdog@2001c000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001c000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x5f 0x04>; - clocks = <0x05 0x74 0x05 0x7b>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x2e>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt2"; - }; - - watchdog@2001d000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x2001d000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x60 0x04>; - clocks = <0x05 0x75 0x05 0x7c>; - clock-names = "wclk\0pclk"; - resets = <0x06 0x2f>; - resets-names = "wdt_reset"; - response-mode = <0x00>; - bst_wdt_name = "lsp_wdt3"; - }; - - watchdog@32009000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x32009000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x43 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt0"; - }; - - watchdog@3200a000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200a000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x44 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt1"; - }; - - watchdog@3200b000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200b000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x45 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt2"; - }; - - watchdog@3200c000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200c000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x46 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt3"; - }; - - watchdog@3200d000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200d000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x47 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt4"; - }; - - watchdog@3200e000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200e000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x48 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt5"; - }; - - watchdog@3200f000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x3200f000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x49 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt6"; - }; - - watchdog@32010000 { - status = "okay"; - compatible = "snps,dw-wdt"; - reg = <0x00 0x32010000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4a 0x04>; - clocks = <0x05 0x01>; - clock-names = "wclk"; - response-mode = <0x01>; - bst_wdt_name = "a55_wdt7"; - }; - - i2c@20000000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20000000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xcf 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x18>; - reset-names = "i2c0_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1d>; - }; - - i2c@20001000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20001000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd0 0x04>; - clock-frequency = <0xf4240>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x19>; - reset-names = "i2c1_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1e>; - }; - - i2c@20002000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20002000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd1 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4b 0x05 0x49>; - clock-names = "LSP0_PCLK\0LSP0_WCLK"; - resets = <0x06 0x1a>; - reset-names = "i2c2_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x1f>; - - max96712@2a { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-lis"; - reg = <0x2a>; - i2c-port = <0x01>; - csi2-port = <0x01>; - lane-speed = <0x960>; - regs = <0x40>; - data-type = <0x2d>; - trigger-mode = <0x01>; - trigger-fps = <0x14>; - trigger-rx-gpio = <0x02>; - trigger-tx-gpio = <0x08>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x06 0x06 0x06 0x06>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x20 0x14 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - - endpoint { - remote-endpoint = <0x21>; - phandle = <0x72>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x22>; - phandle = <0x2e>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x23>; - phandle = <0x2f>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x24>; - phandle = <0x30>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x25>; - phandle = <0x31>; - }; - }; - }; - }; - - max96712@6b { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-ctl"; - reg = <0x6b>; - i2c-port = <0x00>; - csi2-port = <0x00>; - lane-speed = <0x640>; - regs = <0x44>; - data-type = <0x1e>; - trigger-mode = <0x01>; - trigger-fps = <0x1e>; - trigger-rx-gpio = <0x01>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x03 0x03 0x03 0x03>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x20 0x15 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - - endpoint { - remote-endpoint = <0x26>; - phandle = <0x77>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x27>; - phandle = <0x32>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x28>; - phandle = <0x33>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x29>; - phandle = <0x34>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x2a>; - phandle = <0x35>; - }; - }; - }; - }; - - max96712@29 { - compatible = "bst,maxim-deser-hub"; - type = "max96712"; - ctl-mode = "fad-lis"; - reg = <0x29>; - i2c-port = <0x01>; - csi2-port = <0x01>; - lane-speed = <0x960>; - regs = <0x40>; - data-type = <0x2d>; - trigger-mode = <0x01>; - trigger-fps = <0x14>; - trigger-rx-gpio = <0x02>; - trigger-tx-gpio = <0x08>; - maxim,hsync-invert = <0x00>; - maxim,vsync-invert = <0x00>; - maxim,linkrx-rate = <0x06 0x06 0x06 0x06>; - maxim,link-mode = "GMSL2"; - pdb-gpio = <0x20 0x17 0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - csi-link { - - ports { - - port@0 { - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - - endpoint { - remote-endpoint = <0x2b>; - phandle = <0x7c>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x2c>; - phandle = <0x36>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x2d>; - phandle = <0x37>; - }; - }; - }; - }; - - camera@70 { - reg = <0x70>; - ser-alias-id = <0x60>; - sensor-alias-id = <0x70>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x2e>; - phandle = <0x22>; - }; - }; - }; - - camera71 { - status = "disabled"; - reg = <0x71>; - ser-alias-id = <0x61>; - sensor-alias-id = <0x71>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x2f>; - phandle = <0x23>; - }; - }; - }; - - camera@72 { - status = "disabled"; - reg = <0x72>; - ser-alias-id = <0x62>; - sensor-alias-id = <0x72>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x30>; - phandle = <0x24>; - }; - }; - }; - - camera@73 { - status = "disabled"; - reg = <0x73>; - ser-alias-id = <0x63>; - sensor-alias-id = <0x73>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x31>; - phandle = <0x25>; - }; - }; - }; - - camera@54 { - reg = <0x54>; - ser-alias-id = <0x64>; - sensor-alias-id = <0x54>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x32>; - phandle = <0x27>; - }; - }; - }; - - camera@55 { - reg = <0x55>; - ser-alias-id = <0x65>; - sensor-alias-id = <0x55>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x33>; - phandle = <0x28>; - }; - }; - }; - - camera@56 { - reg = <0x56>; - ser-alias-id = <0x66>; - sensor-alias-id = <0x56>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x34>; - phandle = <0x29>; - }; - }; - }; - - camera@57 { - reg = <0x57>; - ser-alias-id = <0x67>; - sensor-alias-id = <0x57>; - compatible = "bst,ofilm_ox3c"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity-low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max96717f"; - algo-bin = "ox3c/0X03C10_raw14_OF_h100_AlgoParam.bin"; - iq-bin = "ox3c/0X03C10_raw14_OF_h100_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0x780 0x506>; - dvp-dummy = <0xabcd>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0x780 0x438>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0x780 0x64 0x49c>; - sensor-fps = <0x1e>; - trigger-gpio = <0x00>; - trigger-tx-gpio = <0x00>; - maxim,rx_rate = <0x03>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x35>; - phandle = <0x2a>; - }; - }; - }; - - camera@58 { - reg = <0x58>; - ser-alias-id = <0x48>; - sensor-alias-id = <0x58>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x36>; - phandle = <0x2c>; - }; - }; - }; - - camera@59 { - status = "disabled"; - reg = <0x59>; - ser-alias-id = <0x49>; - sensor-alias-id = <0x59>; - compatible = "bst,jk_ox08b"; - sensor-id = <0x36>; - data-type = <0x2d>; - fv-polarity_low = <0x00>; - fpd3-mode = "csi-2"; - serializer = "max9295"; - algo-bin = "ox08b/Ox08B40_raw14_hk_h120_AlgoParam.bin"; - iq-bin = "ox08b/Ox08B40_raw14_hk_h120_IqParam.bin"; - hdr-stagger-en = <0x01>; - exp-num = <0x01>; - pwl-format = <0x0f>; - dvp-data-type = <0x2d>; - vin-data-type = <0x2d>; - size = <0xf00 0x876>; - dvp-dummy = <0xaaa0>; - view0-fmt = <0x01>; - view0-size = <0x500 0x2d0>; - view1-fmt = <0x01>; - view1-size = <0xf00 0x870>; - pdns-mode = <0x00>; - pdns-input-view = <0x00>; - hblank = <0x00>; - input-crop = <0x00 0xf00 0x02 0x872>; - clock-frequency = <0x18>; - sensor-fps = <0x1e>; - maxim,rx_rate = <0x06>; - trigger-gpio = <0x01>; - trigger-tx-gpio = <0x08>; - serializer-id = <0x42>; - maxim,link-mode = "GMSL2"; - - port { - - endpoint@0 { - remote-endpoint = <0x37>; - phandle = <0x2d>; - }; - }; - }; - }; - - i2c@20003000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20003000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd2 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x2c>; - reset-names = "i2c3_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x38>; - }; - - i2c@20004000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20004000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd3 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x2d>; - reset-names = "i2c4_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x39>; - - eeprom@56 { - compatible = "atmel,24c02"; - reg = <0x56>; - pagesize = <0x10>; - }; - - lt9211@2d { - compatible = "bst,lt9211"; - reg = <0x2d>; - }; - }; - - i2c@20005000 { - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "snps,designware-i2c"; - reg = <0x00 0x20005000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd4 0x04>; - clock-frequency = <0x186a0>; - i2c-sda-hold-time-ns = <0x12c>; - i2c-sda-falling-time-ns = <0x12c>; - i2c-scl-falling-time-ns = <0x12c>; - clocks = <0x05 0x4c 0x05 0x4a>; - clock-names = "LSP1_PCLK\0LSP1_WCLK"; - resets = <0x06 0x26>; - reset-names = "i2c5_reset"; - pinctrl-names = "default"; - pinctrl-0 = <0x3a>; - }; - - ddr_ecc { - status = "okay"; - compatible = "bst,a1000_ddr_ecc"; - reg = <0x00 0x38000000 0x1400 0x00 0x3c000000 0x1400 0x00 0x33000000 0x140>; - reg-names = "ddr0\0ddr1\0a55_ctrl"; - interrupt-parent = <0x01>; - interrupts = <0x00 0x8b 0x04 0x00 0x8d 0x04>; - interrupt-names = "ddr0_ecc_irq\0ddr1_ecc_irq"; - mbox-names = "bstn-mbox"; - }; - - arm_pmu { - status = "okay"; - compatible = "arm,armv8-pmuv3"; - interrupt-parent = <0x01>; - interrupts = <0x00 0x38 0xff04 0x00 0x39 0xff04 0x00 0x3a 0xff04 0x00 0x3b 0xff04 0x00 0x3c 0xff04 0x00 0x3d 0xff04 0x00 0x3e 0xff04 0x00 0x3f 0xff04>; - interrupt-affinity = <0x3b 0x3c 0x3d 0x3e 0x3f 0x40 0x41 0x42>; - }; - - noc_pmu@0x32702000 { - status = "okay"; - compatible = "bst,bst_noc_pmu"; - reg = <0x00 0x32702000 0x1000 0x00 0x32703000 0x1000 0x00 0x32704000 0x2000 0x00 0x32708000 0x1000 0x00 0x33402000 0x2400 0x00 0x33422000 0x4400 0x00 0x33401100 0x10 0x00 0x33421480 0x10>; - reg-names = "coresight_cpunoc_etf\0coresight_etr\0coresight_funnel\0coresight_corenoc_etf\0cpu_port_set\0core_port_set\0cpunoc_atb\0corenoc_atb"; - memory-region = <0x43>; - }; - - lsp0_pwm0@20012000 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x56 0x05 0x6b>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20012000 0x14 0x00 0x200120b0 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x44>; - }; - - lsp0_pwm1@20012014 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x57 0x05 0x6b>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20012014 0x14 0x00 0x200120b4 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x45>; - }; - - lsp1_pwm0@20013000 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x71 0x05 0x7a>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20013000 0x14 0x00 0x200130b0 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x46>; - }; - - lsp1_pwm1@20013014 { - status = "disable"; - compatible = "snps,bst-pwm"; - clocks = <0x05 0x72 0x05 0x7a>; - clock-names = "wclk\0pclk"; - clock-frequency = <0x17d7840>; - reg = <0x00 0x20013014 0x14 0x00 0x200130b4 0x04>; - reg-names = "base\0top"; - pwm-ch = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x47>; - }; - - a55_timer0@32008000 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4b 0x04>; - reg = <0x00 0x32008000 0x14>; - }; - - a55_timer1@32008014 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4c 0x04>; - reg = <0x00 0x32008014 0x14>; - }; - - a55_timer2@32008028 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4d 0x04>; - reg = <0x00 0x32008028 0x14>; - }; - - a55_timer3@3200803c { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4e 0x04>; - reg = <0x00 0x3200803c 0x14>; - }; - - a55_timer4@32008050 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x4f 0x04>; - reg = <0x00 0x32008050 0x14>; - }; - - a55_timer5@32008064 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x50 0x04>; - reg = <0x00 0x32008064 0x14>; - }; - - a55_timer6@32008078 { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x51 0x04>; - reg = <0x00 0x32008078 0x14>; - }; - - a55_timer7@3200808c { - status = "disable"; - compatible = "snps,dw-apb-timer"; - clock-frequency = <0x14dc9380>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x52 0x04>; - reg = <0x00 0x3200808c 0x14>; - }; - - spi@2000c000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000c000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x00>; - cs-gpios = <0x48 0x02 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x49>; - }; - - spi@2000d000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000d000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdc 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x01>; - cs-gpios = <0x48 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4a>; - }; - - spi@2000c800 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000c800 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf4 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x02>; - cs-gpios = <0x4b 0x08 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4c 0x4d>; - }; - - spi@2000d800 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x2000d800 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf5 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x03>; - cs-gpios = <0x4b 0x10 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4e 0x4f>; - }; - - spi@20022000 { - status = "okay"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x20022000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe2 0x04>; - clocks = <0x05 0x58 0x05 0x68>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x04>; - cs-gpios = <0x48 0x02 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x50>; - spi-slave; - - slave@0 { - compatible = "rohm,dh2228fv"; - reg = <0x00>; - spi-max-frequency = <0x989680>; - }; - }; - - spi@20023000 { - status = "disable"; - compatible = "snps,dw-apb-ssi"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x20023000 0x800>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xe1 0x04>; - clocks = <0x05 0x73 0x05 0x80>; - clock-names = "spi_wclk\0spi_pclk"; - num-cs = <0x01>; - bus-num = <0x05>; - cs-gpios = <0x48 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x51>; - }; - - i2s-play@2000e000 { - status = "disable"; - compatible = "snps,designware-i2s"; - reg = <0x00 0x2000e000 0x1000>; - interrupt-names = "play_irq"; - interrupt-parent = <0x01>; - interrupts = <0x00 0xdd 0x04>; - clocks = <0x05 0x61 0x05 0x59>; - clock-names = "i2spclk\0i2sclk"; - play; - channel = <0x02>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x52>; - }; - - i2s-rec@2000f000 { - status = "disable"; - compatible = "snps,designware-i2s"; - reg = <0x00 0x2000f000 0x1000>; - interrupt-names = "record_irq"; - interrupt-parent = <0x01>; - interrupts = <0x00 0xde 0x04>; - clocks = <0x02>; - record; - channel = <0x02>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x53>; - }; - - qspi@00000000 { - status = "okay"; - compatible = "snps,dwc-ssi-1.01a"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x00 0x4000000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xd9 0x04>; - clocks = <0x05 0x4d 0x05 0x4e>; - clock-names = "hclk\0wclk"; - work-mode = <0x01>; - reg-io-width = <0x04>; - bst,use-gpio-cs; - spi-rx-bus-width = <0x04>; - spi-tx-bus-width = <0x04>; - cs-gpios = <0x48 0x15 0x00>; - num-cs = <0x01>; - bus-num = <0x06>; - pinctrl-names = "default"; - pinctrl-0 = <0x54>; - - qspi0-nor0@0 { - #address-cells = <0x01>; - #size-cells = <0x01>; - spi-rx-bus-width = <0x01>; - spi-tx-bus-width = <0x01>; - compatible = "jedec,spi-nor"; - status = "okay"; - spi-max-frequency = <0xf4240>; - reg = <0x00>; - mode = <0x00>; - powerctl-fad-gpios = <0x20 0x1c 0x00>; - - partition@0 { - reg = <0x00 0x1e00000>; - label = "nor0_part0"; - }; - - partition@1e00000 { - reg = <0x1e00000 0x200000>; - label = "nor0_part1"; - }; - }; - }; - - qspi@14000000 { - status = "disable"; - compatible = "snps,dwc-ssi-1.01a"; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00 0x14000000 0x4000000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x05 0x4f 0x05 0x50>; - clock-names = "hclk\0wclk"; - work-mode = <0x01>; - reg-io-width = <0x04>; - bst,use-gpio-cs; - spi-rx-bus-width = <0x04>; - spi-tx-bus-width = <0x04>; - cs-gpios = <0x55 0x01 0x00>; - num-cs = <0x01>; - bus-num = <0x07>; - pinctrl-names = "default"; - pinctrl-0 = <0x56>; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x0f>; - snps,rd_osr_lmt = <0x0f>; - snps,axi_fb; - snps,blen = <0x04 0x08 0x10 0x00 0x00 0x00 0x00>; - phandle = <0x57>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x04>; - snps,rx-sched-sp; - phandle = <0x58>; - - queue0 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x00>; - snps,priority = <0x00>; - }; - - queue1 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x01>; - snps,priority = <0x01>; - }; - - queue2 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x02>; - snps,priority = <0x02>; - }; - - queue3 { - snps,dcb-algorithm; - snps,map-to-dma-channel = <0x03>; - snps,priority = <0x03>; - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x04>; - snps,tx-sched-wrr; - phandle = <0x59>; - - queue0 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x00>; - }; - - queue1 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x01>; - }; - - queue2 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x02>; - }; - - queue3 { - snps,weight = <0x10>; - snps,dcb-algorithm; - snps,priority = <0x03>; - }; - }; - - thermal@70039000 { - status = "okay"; - compatible = "bst,bst-thermal"; - reg = <0x00 0x70039000 0x1000>; - #thermal-sensor-cells = <0x00>; - phandle = <0x62>; - }; - - ethernet@30000000 { - status = "okay"; - compatible = "bst,dw-eqos-eth"; - reg = <0x00 0x30000000 0x100000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9f 0xff04 0x00 0xa0 0xff04 0x00 0xa1 0xff04 0x00 0xa2 0xff04 0x00 0xa3 0xff04 0x00 0xa4 0xff04 0x00 0xa5 0xff04 0x00 0xa6 0xff04 0x00 0xa7 0xff04 0x00 0xa8 0xff04 0x00 0xa9 0xff04 0x00 0xaa 0xff04>; - interrupt-names = "sbd_irq\0sfty_ce_irq\0sfty_ue_irq\0tx_chan0_irq\0tx_chan1_irq\0tx_chan2_irq\0tx_chan3_irq\0rx_chan0_irq\0rx_chan1_irq\0rx_chan2_irq\0rx_chan3_irq\0eth_lpi"; - ethernet-id = <0x00>; - mac-address = [00 00 00 00 00 00]; - max-frame-size = <0xed8>; - phy-mode = "rgmii"; - snps,multicast-filter-bins = <0x100>; - snps,perfect-filter-entries = <0x80>; - rx-fifo-depth = <0x4000>; - tx-fifo-depth = <0x4000>; - clocks = <0x05 0x0f 0x05 0x13 0x05 0x15 0x05 0x11>; - clock-names = "wclk\0axim_aclk\0pclk\0ptp_ref"; - bst,fix-safety = <0x00>; - bst,dma_int_mode = <0x01>; - snps,fixed-burst; - snps,force_sf_dma_mode; - snps,ps-speed = <0x3e8>; - snps,axi-config = <0x57>; - snps,mtl-rx-config = <0x58>; - snps,mtl-tx-config = <0x59>; - label = "gmac0"; - resets = <0x06 0x11>; - reset-names = "bstgmaceth"; - eth-name = "gmac0"; - eth-number = <0x00>; - mac-mode = "rgmii"; - extend-op = <0x03>; - pinctrl-names = "default"; - pinctrl-0 = <0x5a>; - - fixed-link { - speed = <0x3e8>; - full-duplex; - }; - }; - - ethernet@30100000 { - status = "disable"; - compatible = "bst,dw-eqos-eth"; - reg = <0x00 0x30100000 0x100000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xab 0xff04 0x00 0xac 0xff04 0x00 0xad 0xff04 0x00 0xae 0xff04 0x00 0xaf 0xff04 0x00 0xb0 0xff04 0x00 0xb1 0xff04 0x00 0xb2 0xff04 0x00 0xb3 0xff04 0x00 0xb4 0xff04 0x00 0xb5 0xff04 0x00 0xb6 0xff04>; - interrupt-names = "sbd_irq\0sfty_ce_irq\0sfty_ue_irq\0tx_chan0_irq\0tx_chan1_irq\0tx_chan2_irq\0tx_chan3_irq\0rx_chan0_irq\0rx_chan1_irq\0rx_chan2_irq\0rx_chan3_irq\0eth_lpi"; - ethernet-id = <0x01>; - mac-address = [00 00 00 00 00 00]; - max-frame-size = <0xed8>; - phy-mode = "rgmii"; - snps,multicast-filter-bins = <0x100>; - snps,perfect-filter-entries = <0x80>; - rx-fifo-depth = <0x4000>; - tx-fifo-depth = <0x4000>; - clocks = <0x05 0x10 0x05 0x14 0x05 0x16 0x05 0x12>; - clock-names = "wclk\0axim_aclk\0pclk\0ptp_ref"; - bst,fix-safety = <0x00>; - bst,dma_int_mode = <0x01>; - snps,fixed-burst; - snps,force_sf_dma_mode; - snps,ps-speed = <0x3e8>; - snps,axi-config = <0x57>; - snps,mtl-rx-config = <0x58>; - snps,mtl-tx-config = <0x59>; - label = "gmac1"; - resets = <0x06 0x12>; - reset-names = "bstgmaceth"; - pinctrl-names = "default"; - pinctrl-0 = <0x5b>; - extend-op = <0x03>; - eth-name = "gmac1"; - eth-number = <0x01>; - mac-mode = "rgmii"; - phy-handle = <0x5c>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - eth_phy1@1 { - compatible = "marvell,88Q2122\0ethernet-phy-id002B.0983\0ethernet-phy-ieee802.3-c45"; - device_type = "ethernet-phy"; - max-speed = <0x3e8>; - reg = <0x01>; - reset-gpios = <0x20 0x09 0x01>; - reset-active-low; - reset-assert-us = <0x4e20>; - reset-deassert-us = <0x4e20>; - phandle = <0x5c>; - }; - }; - }; - - phy@30E01000 { - compatible = "bst,dwc-usb-phy"; - #phy-cells = <0x00>; - reg = <0x00 0x30e01000 0x1000>; - usb_mode = "usb20"; - phandle = <0x5f>; - }; - - phy@30E00000 { - compatible = "bst,dwc-usb-phy"; - reg = <0x00 0x30e00000 0x1000>; - #phy-cells = <0x00>; - usb_mode = "usb30"; - pll_type = "internal"; - phandle = <0x5d>; - }; - - usb3 { - compatible = "bst,dwc3usb"; - status = "okay"; - ranges; - #address-cells = <0x02>; - #size-cells = <0x01>; - clock-names = "suspend\0ref\0axi\0apb"; - clocks = <0x05 0x17 0x05 0x18 0x05 0x19 0x05 0x1a>; - resets = <0x06 0x04>; - reset-names = "usb3_reset"; - phys = <0x5d>; - phy-names = "usb-phy"; - pll_type = "internal"; - powerctl-gpios = <0x20 0x0e 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x5e>; - - dwc3@30200000 { - compatible = "snps,dwc3"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - reg = <0x00 0x30200000 0x100000>; - interrupts = <0x00 0xc8 0x04>; - interrupt-parent = <0x01>; - dr_mode = "host"; - snps,dis_u3_susphy_quirk; - }; - }; - - usb2 { - compatible = "bst,dwc3usb"; - status = "okay"; - ranges; - #address-cells = <0x02>; - #size-cells = <0x01>; - clock-names = "ahb\0ref\0apb"; - clocks = <0x05 0x1b 0x05 0x1d 0x05 0x1c>; - reset-names = "usb2_reset"; - resets = <0x06 0x05>; - phys = <0x5f>; - phy-names = "usb-phy"; - pll_type = "internal"; - - dwc3@30300000 { - status = "okay"; - compatible = "snps,dwc3"; - reg = <0x00 0x30300000 0x100000>; - interrupts = <0x00 0xc9 0x04>; - interrupt-parent = <0x01>; - dr_mode = "peripheral"; - snps,incr-burst-type-adjustment = <0x01 0x04 0x08 0x10>; - snps,reqinfo-for-data-read = <0x08>; - snps,reqinfo-for-descriptor-read = <0x08>; - }; - }; - - dwmmc0@30400000 { - status = "okay"; - compatible = "bst,dwcmshc-sdhci"; - clocks = <0x05 0x1f 0x05 0x1e>; - clock-names = "core\0bus"; - reg = <0x00 0x30400000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xb9 0x04>; - interrupt-names = "IRQDWMMC0"; - #address-cells = <0x01>; - #size-cells = <0x00>; - data-addr = <0x200>; - fifo-watermark-aligned; - clock-frequency = <0x2faf080>; - max-frequency = <0xbebc200>; - min-frequency = <0x61a80>; - broken-64bit-dma; - clear-tarns-mode; - fifo-depth = <0x400>; - card-detect-delay = <0xc8>; - bus-width = <0x08>; - cap-mmc-highspeed; - non-removable; - no-sdio; - no-sd; - keep-power-in-suspend; - no-3-3-v; - sdhci,auto-cmd12; - pinctrl-names = "default"; - pinctrl-0 = <0x60>; - }; - - dwmmc1@30500000 { - status = "okay"; - compatible = "bst,dwcmshc-sdhci"; - clocks = <0x05 0x21 0x05 0x20>; - clock-names = "core\0bus"; - reg = <0x00 0x30500000 0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xbd 0x04>; - interrupt-names = "IRQDWMMC1"; - #address-cells = <0x01>; - #size-cells = <0x00>; - data-addr = <0x200>; - fifo-watermark-aligned; - clock-frequency = <0x5f5e100>; - max-frequency = <0xbebc200>; - min-frequency = <0x61a80>; - broken-64bit-dma; - clear-tarns-mode; - fifo-depth = <0x400>; - card-detect-delay = <0xc8>; - bus-width = <0x04>; - cap-sd-highspeed; - sd-uhs-sdr12; - sd-uhs-sdr25; - sd-uhs-sdr50; - no-sdio; - no-mmc; - sdhci,auto-cmd12; - keep-power-in-suspend; - pinctrl-names = "default"; - pinctrl-0 = <0x61>; - }; - - gpu@33300000 { - status = "okay"; - compatible = "arm,mali-450\0arm,mali-utgard"; - reg = <0x00 0x33300000 0x30000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04>; - interrupt-names = "IRQPP0\0IRQPPMMU0\0IRQPP1\0IRQPPMMU1\0IRQGP\0IRQGPMMU\0IRQPMU\0IRQPP"; - clocks = <0x05 0x31 0x05 0x32>; - clock-names = "clk_mali\0clk_mali_apb"; - resets = <0x06 0x0b>; - reset-names = "gpu_reset"; - ppcores = <0x02>; - dedicated_mem_start = <0x00>; - dedicated_mem_size = <0x00>; - }; - - mali-v500@0x55000000 { - status = "okay"; - compatible = "arm,mali-v500"; - reg = <0x00 0x55000000 0xffff>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9a 0x04>; - interrupt-names = "IRQV500"; - resets = <0x06 0x00>; - reset-names = "codec_reset"; - clocks = <0x05 0x3d>; - clock-names = "clk_v500"; - }; - }; - - cooling_dev { - - pwm { - cpumask = <0x0f>; - capacitance = <0x5dc>; - #cooling-cells = <0x02>; - phandle = <0x64>; - }; - }; - - thermal-zones { - - cpu-thermal { - polling-delay-passive = <0x1f4>; - polling-delay = <0x3e8>; - thermal-sensors = <0x62>; - - trips { - - switch_trip { - temperature = <0x15f90>; - hysteresis = <0x7d0>; - type = "passive"; - phandle = <0x63>; - }; - - critical_trip { - temperature = <0x1e848>; - hysteresis = <0x00>; - type = "critical"; - }; - }; - - cooling-maps { - - map0 { - trip = <0x63>; - cooling-device = <0x64 0x00 0x01>; - }; - }; - }; - }; - - pinctrl@70038000 { - status = "okay"; - compatible = "bst,pinctrl-a1000b"; - #address-cells = <0x02>; - #size-cells = <0x02>; - reg = <0x00 0x70038000 0x00 0x1000 0x00 0x33001000 0x00 0x1000>; - reg-names = "aon\0top"; - #gpio-cells = <0x02>; - - spi0_pinctrl { - phandle = <0x49>; - - mux { - pins = "spi0_miso\0spi0_mosi\0spi0_sclk"; - function = "spi0"; - }; - }; - - spi1_pinctrl { - phandle = <0x4a>; - - mux { - pins = "spi1_miso\0spi1_mosi\0spi1_sclk"; - function = "spi1"; - }; - }; - - spi2_pinctrl { - phandle = <0x4c>; - - mux { - pins = "uart0_cts\0uart1_cts\0uart1_rts"; - function = "spi2"; - }; - }; - - spi2_cs_pinctrl { - phandle = <0x4d>; - - mux { - pins = "uart0_rts"; - function = "gpio"; - }; - }; - - spi3_pinctrl { - phandle = <0x4e>; - - mux { - pins = "uart2_cts\0uart3_cts\0uart3_rts"; - function = "spi3"; - }; - }; - - spi3_cs_pinctrl { - phandle = <0x4f>; - - mux { - pins = "uart2_rts"; - function = "gpio"; - }; - }; - - spi4_pinctrl { - phandle = <0x50>; - - mux { - pins = "spi0_miso\0spi0_mosi\0spi0_sclk\0spi0_cs"; - function = "spi0_s"; - }; - }; - - spi5_pinctrl { - phandle = <0x51>; - - mux { - pins = "spi1_miso\0spi1_mosi\0spi1_sclk"; - function = "spi1_s"; - }; - }; - - i2c0_pinctrl { - phandle = <0x1d>; - - mux { - pins = "i2c0_scl\0i2c0_sda"; - function = "i2c0"; - }; - }; - - i2c1_pinctrl { - phandle = <0x1e>; - - mux { - pins = "i2c1_scl\0i2c1_sda"; - function = "i2c1"; - }; - }; - - i2c2_pinctrl { - phandle = <0x1f>; - - mux { - pins = "i2c2_scl\0i2c2_sda"; - function = "i2c2"; - }; - }; - - i2c3_pinctrl { - phandle = <0x38>; - - mux { - pins = "i2c3_scl\0i2c3_sda"; - function = "i2c3"; - }; - }; - - i2c4_pinctrl { - phandle = <0x39>; - - mux { - pins = "i2c4_scl\0i2c4_sda"; - function = "i2c4"; - }; - }; - - i2c5_pinctrl { - phandle = <0x3a>; - - mux { - pins = "i2c5_scl\0i2c5_sda"; - function = "i2c5"; - }; - }; - - uart0_pinctrl { - phandle = <0x07>; - - mux { - pins = "uart0_txd\0uart0_rxd"; - function = "uart0"; - }; - }; - - uart1_pinctrl { - phandle = <0x08>; - - mux { - pins = "uart1_txd\0uart1_rxd"; - function = "uart1"; - }; - }; - - uart2_pinctrl { - phandle = <0x09>; - - mux { - pins = "uart2_txd\0uart2_rxd"; - function = "uart2"; - }; - }; - - uart3_pinctrl { - phandle = <0x0a>; - - mux { - pins = "uart3_txd\0uart3_rxd"; - function = "uart3"; - }; - }; - - gpio0_pinctrl { - - mux { - pins = "gpio_29"; - function = "gpio"; - }; - }; - - gpio_special_func_pinctrl { - - mux { - pins = "gpio_24\0gpio_29\0debug4\0debug5\0uart0_cts\0uart0_rts"; - function = "gpio"; - }; - }; - - qspi0_pinctrl { - phandle = <0x54>; - - mux { - pins = "qspi0_cs0"; - function = "gpio"; - }; - }; - - usb3_pinctrl { - phandle = <0x5e>; - - mux { - pins = "gpio_14"; - function = "gpio"; - }; - }; - - qspi1_pinctrl { - phandle = <0x56>; - - mux { - pins = "qspi1_cs1"; - function = "gpio"; - }; - }; - - des_960_1_pinctrl { - - mux { - pins = "qspi1_io1"; - function = "gpio"; - }; - }; - - des_960_2_pinctrl { - - mux { - pins = "qspi1_io3"; - function = "gpio"; - }; - }; - - des_960_3_pinctrl { - - mux { - pins = "qspi1_io5"; - function = "gpio"; - }; - }; - - bist_pinctrl { - - mux { - pins = "spi1_mosi"; - function = "bist"; - }; - }; - - - - i2s0_pinctrl { - phandle = <0x52>; - - mux { - pins = "i2s0_ck\0i2s0_mck\0i2s0_sd_out\0i2s0_ws\0qspi0_io4"; - function = "i2s0"; - }; - }; - - i2s1_pinctrl { - phandle = <0x53>; - - mux { - pins = "i2s1_ck\0i2s1_sd_in\0i2s1_ws"; - function = "i2s1"; - }; - }; - - isp_pinctrl { - phandle = <0x67>; - - mux { - pins = "isp_fsync0\0isp_fsync1\0isp_fsync2\0isp_fsync3"; - function = "isp"; - }; - }; - - ptp_pinctrl { - - mux { - pins = "ptp_pps0\0ptp_pps1\0ptp_clk"; - function = "ptp"; - }; - }; - - ptp_pinconfig { - - config { - pins = "ptp_clk"; - drive-strength = <0x02>; - input-enable; - }; - }; - - err_rpt_l0_pinctrl { - - mux { - pins = "err_rpt_l0_n\0err_rpt_l0_p"; - function = "err_rpt_l0"; - }; - }; - - err_rpt_gpio_l0_pinctrl { - - mux { - pins = "err_rpt_l0_n\0err_rpt_l0_p"; - function = "gpio"; - }; - }; - - err_rpt_l1_pinctrl { - - mux { - pins = "err_rpt_l1_n\0err_rpt_l1_p"; - function = "err_rpt_l1"; - }; - }; - - pwm_lsp0_pwm0_pinctrl { - phandle = <0x44>; - - mux { - pins = "pwm0"; - function = "pwm"; - }; - }; - - pwm_lsp0_pwm1_pinctrl { - phandle = <0x45>; - - mux { - pins = "pwm1"; - function = "pwm"; - }; - }; - - pwm_lsp1_pwm0_pinctrl { - phandle = <0x46>; - - mux { - pins = "pwm2"; - function = "pwm"; - }; - }; - - pwm_lsp1_pwm1_pinctrl { - phandle = <0x47>; - - mux { - pins = "pwm3"; - function = "pwm"; - }; - }; - - ts_pinctrl { - - mux { - pins = "ts_trig_in00\0ts_trig_in01\0ts_trig_in10\0ts_trig_in11"; - function = "ts"; - }; - }; - - sdemmc0_pinctrl { - phandle = <0x15>; - - mux { - pins = "sdemmc0_clk\0sdemmc0_cmd\0sdemmc0_dat0\0sdemmc0_dat1\0sdemmc0_dat2\0sdemmc0_dat3\0sdemmc0_dat4\0sdemmc0_dat5\0sdemmc0_dat6\0sdemmc0_dat7\0sdemmc0_rstb\0sdemmc0_cdn\0sdemmc0_wp"; - function = "sdemmc0"; - }; - }; - - sdemmc0_pinconfig { - phandle = <0x60>; - - config { - pins = "sdemmc0_clk\0sdemmc0_cmd\0sdemmc0_dat0\0sdemmc0_dat1\0sdemmc0_dat2\0sdemmc0_dat3\0sdemmc0_dat4\0sdemmc0_dat5\0sdemmc0_dat6\0sdemmc0_dat7\0sdemmc0_rstb\0sdemmc0_cdn\0sdemmc0_wp"; - drive-strength = <0x02>; - input-enable; - }; - }; - - sdemmc1_pinctrl { - phandle = <0x16>; - - mux { - pins = "sdemmc1_clk\0sdemmc1_cmd\0sdemmc1_dat0\0sdemmc1_dat1\0sdemmc1_dat2\0sdemmc1_dat3\0sdemmc1_dat4\0sdemmc1_dat5\0sdemmc1_dat6\0sdemmc1_dat7\0sdemmc1_rstb\0sdemmc1_cdn\0sdemmc1_wp"; - function = "sdemmc1"; - }; - }; - - sdemmc1_pinconfig { - phandle = <0x61>; - - config { - pins = "sdemmc1_clk\0sdemmc1_cmd\0sdemmc1_dat0\0sdemmc1_dat1\0sdemmc1_dat2\0sdemmc1_dat3\0sdemmc1_dat4\0sdemmc1_dat5\0sdemmc1_dat6\0sdemmc1_dat7\0sdemmc1_rstb\0sdemmc1_cdn\0sdemmc1_wp"; - drive-strength = <0x0f>; - input-enable; - }; - }; - - debug_pinctrl { - phandle = <0x17>; - - mux { - pins = "debug0\0debug1\0debug2\0debug3\0debug4\0debug5\0debug6\0debug7"; - function = "debug"; - }; - }; - - strap_pinctrl { - - mux { - pins = "gpio_24\0gpio_25\0gpio_26\0gpio_27\0gpio_28\0gpio_29\0spi1_sclk\0i2s0_mck\0i2s0_ck\0gpio_107\0gpio_108"; - function = "strap"; - }; - }; - - vout_pinctrl { - phandle = <0x18>; - - mux { - pins = "vout_r0\0vout_r1\0vout_r2\0vout_r3\0vout_r4\0vout_r5\0vout_r6\0vout_r7\0vout_g0\0vout_g1\0vout_g2\0vout_g3\0vout_g4\0vout_g5\0vout_g6\0vout_g7\0vout_b0\0vout_b1\0vout_b2\0vout_b3\0vout_b4\0vout_b5\0vout_b6\0vout_b7\0vout_hs\0vout_vs\0vout_de\0vout_pclk\0vout_pdb"; - function = "vout"; - }; - }; - - vout_pinconfig { - - config { - pins = "vout_r0\0vout_r1\0vout_r2\0vout_r3\0vout_r4\0vout_r5\0vout_r6\0vout_r7\0vout_g0\0vout_g1\0vout_g2\0vout_g3\0vout_g4\0vout_g5\0vout_g6\0vout_g7\0vout_b0\0vout_b1\0vout_b2\0vout_b3\0vout_b4\0vout_b5\0vout_b6\0vout_b7\0vout_hs\0vout_vs\0vout_de\0vout_pclk\0vout_pdb"; - drive-strength = <0x02>; - input-enable; - }; - }; - - vin_pinctrl { - phandle = <0x19>; - - mux { - pins = "vin_b0\0vin_b1\0vin_b2\0vin_b3\0vin_b4\0vin_de\0vin_g0\0vin_g1\0vin_g2\0vin_g3\0vin_g4\0vin_g5\0vin_hs\0vin_llc\0vin_r0\0vin_r1\0vin_r2\0vin_r3\0vin_r4\0vin_vs"; - function = "vin"; - }; - }; - - vin_pinconfig { - - config { - pins = "vin_b0\0vin_b1\0vin_b2\0vin_b3\0vin_b4\0vin_de\0vin_g0\0vin_g1\0vin_g2\0vin_g3\0vin_g4\0vin_g5\0vin_hs\0vin_llc\0vin_r0\0vin_r1\0vin_r2\0vin_r3\0vin_r4\0vin_vs"; - drive-strength = <0x02>; - input-enable; - }; - }; - - rgmii0_pinctrl { - phandle = <0x5a>; - - mux { - pins = "rgmii0_txd0\0rgmii0_txd1\0rgmii0_txd2\0rgmii0_txd3\0gmii0_txd4\0gmii0_txd5\0gmii0_txd6\0gmii0_txd7\0gmii0_txer\0rgmii0_txctrl\0mii0_txclk\0rgmii0_gtxclk\0rgmii0_rxd0\0rgmii0_rxd1\0rgmii0_rxd2\0rgmii0_rxd3\0gmii0_rxd4\0gmii0_rxd5\0gmii0_rxd6\0gmii0_rxd7\0gmii0_rxer\0rgmii0_rxctrl\0rgmii0_rxclk\0rgmii0_mdio\0rgmii0_mdc\0rgmii0_intr"; - function = "rgmii0"; - }; - }; - - rgmii0_pinconfig { - - config { - pins = "rgmii0_gtxclk\0rgmii0_mdc\0rgmii0_mdio\0rgmii0_rxclk\0rgmii0_rxctrl\0rgmii0_rxd0\0rgmii0_rxd1\0rgmii0_rxd2\0rgmii0_rxd3\0rgmii0_txctrl\0rgmii0_txd0\0rgmii0_txd1\0rgmii0_txd2\0rgmii0_txd3"; - drive-strength = <0x02>; - input-enable; - }; - }; - - rgmii1_pinctrl { - phandle = <0x5b>; - - mux { - pins = "rgmii1_txd0\0rgmii1_txd1\0rgmii1_txd2\0rgmii1_txd3\0mii1_rxer\0mii1_txclk\0rgmii1_txctrl\0rgmii1_gtxclk\0rgmii1_rxd0\0rgmii1_rxd1\0rgmii1_rxd2\0rgmii1_rxd3\0rgmii1_rxctrl\0rgmii1_rxclk\0rgmii1_mdc\0rgmii1_mdio\0rgmii1_intr"; - function = "rgmii1"; - }; - }; - - rgmii1_pinconfig { - - config { - pins = "rgmii1_gtxclk\0rgmii1_mdc\0rgmii1_mdio\0rgmii1_rxclk\0rgmii1_rxctrl\0rgmii1_rxd0\0rgmii1_rxd1\0rgmii1_rxd2\0rgmii1_rxd3\0rgmii1_txctrl\0rgmii1_txd0\0rgmii1_txd1\0rgmii1_txd2\0rgmii1_txd3"; - drive-strength = <0x02>; - input-enable; - }; - }; - - gmii0_pinconfig { - - config { - pins = "gmii0_rxd4\0gmii0_rxd5\0gmii0_rxd6\0gmii0_rxd7\0gmii0_rxer\0gmii0_txd4\0gmii0_txd5\0gmii0_txd6\0gmii0_txd7\0gmii0_txer"; - drive-strength = <0x02>; - input-enable; - }; - }; - - ssd_pinctrl { - phandle = <0x1a>; - - mux { - pins = "sdemmc0_led_ctl\0sdemmc1_led_ctl\0gpio_26\0gpio_27"; - function = "gpio"; - }; - }; - - gnss_pinctrl { - phandle = <0x1b>; - - mux { - pins = "gpio_31\0gpio_12\0pwm1"; - function = "gpio"; - }; - }; - - fan_pinctrl { - phandle = <0x1c>; - - mux { - pins = "gpio_27\0gpio_28"; - function = "gpio"; - }; - }; - - - }; - - isp { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,a1000b-isp"; - memory-region = <0x65 0x66>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x06>; - isp-fw-fbuf-addr = <0xa2000000>; - isp-fw-fbuf-size = <0x10000000>; - dma-coherent; - pinctrl-names = "default"; - pinctrl-0 = <0x67>; - - core@0 { - id = <0x00>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x68>; - phandle = <0x73>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x69>; - phandle = <0x74>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x6a>; - phandle = <0x75>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x6b>; - phandle = <0x76>; - }; - }; - }; - }; - - core@1 { - id = <0x01>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@4 { - remote-endpoint = <0x6c>; - phandle = <0x78>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@5 { - remote-endpoint = <0x6d>; - phandle = <0x79>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@6 { - remote-endpoint = <0x6e>; - phandle = <0x7a>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@7 { - remote-endpoint = <0x6f>; - phandle = <0x7b>; - }; - }; - }; - }; - - core@2 { - id = <0x02>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@8 { - remote-endpoint = <0x70>; - phandle = <0x7d>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@9 { - remote-endpoint = <0x71>; - phandle = <0x7e>; - }; - }; - }; - }; - }; - - csi@0 { - compatible = "bst,a1000b_csi2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - lane-speed = <0x960>; - id = <0x00>; - resets = <0x06 0x0d>; - reset-names = "csi0_reset"; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x72>; - phandle = <0x21>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x73>; - phandle = <0x68>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x74>; - phandle = <0x69>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x75>; - phandle = <0x6a>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x76>; - phandle = <0x6b>; - }; - }; - }; - }; - - csi@1 { - compatible = "bst,a1000b_csi2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02 0x03 0x04>; - lane-speed = <0x640>; - id = <0x01>; - resets = <0x06 0x0e>; - reset-names = "csi1_reset"; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x77>; - phandle = <0x26>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x78>; - phandle = <0x6c>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x79>; - phandle = <0x6d>; - }; - }; - - port@2 { - reg = <0x02>; - - endpoint@2 { - remote-endpoint = <0x7a>; - phandle = <0x6e>; - }; - }; - - port@3 { - reg = <0x03>; - - endpoint@3 { - remote-endpoint = <0x7b>; - phandle = <0x6f>; - }; - }; - }; - }; - - csi@3 { - compatible = "bst,a1000b-csi2-2x2"; - #address-cells = <0x01>; - #size-cells = <0x00>; - clock-lanes = <0x00>; - data-lanes = <0x01 0x02>; - lane-speed = <0x960>; - resets = <0x06 0x10>; - reset-names = "csi2_reset"; - id = <0x03>; - - csi-link { - - ports { - - port@0 { - - endpoint@0 { - remote-endpoint = <0x7c>; - phandle = <0x2b>; - }; - }; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x7d>; - phandle = <0x70>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint@1 { - remote-endpoint = <0x7e>; - phandle = <0x71>; - }; - }; - }; - }; - - chosen { - bootargs = "earlycon=uart8250,mmio32,0x20008000 console=ttyS0,115200n8 memreserve=64M@0xf8000000 rdinit=/sbin/init root=/dev/mmcblk0p7 rw"; - stdout-path = "uart0"; - }; - - aliases { - // uart0 = "/amba_apu/serial@20008000"; - }; - - memory@80000000 { - device_type = "memory"; - reg = <0x00 0x80000000 0x00 0x70000000>; - }; - - // memory@1b0000000 { - // device_type = "memory"; - // reg = <0x01 0xb0000000 0x00 0x40000000>; - // }; - - reserved-memory { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - // linux,cma@88000000 { - // compatible = "shared-dma-pool"; - // reusable; - // reg = <0x0 0x84000000 0x0 0x07000000>; - // linux,cma-default; - // }; - - pcie_ctrl@8fd00000 { - compatible = "bst,pcie-ctrl"; - reg = <0x00 0x8fd00000 0x00 0x100000>; - no-map; - phandle = <0x0c>; - }; - - bst_atf@8b000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8b000000 0x00 0x2000000>; - no-map; - }; - - bst_tee@8fec0000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8fec0000 0x00 0x40000>; - no-map; - phandle = <0x82>; - }; - - bstn_cma@8ff00000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x8ff00000 0x00 0x100000>; - no-map; - phandle = <0x7f>; - }; - - bstn@90000000 { - compatible = "bst,bstn"; - reg = <0x00 0x90000000 0x00 0x2000000>; - no-map; - }; - - bst_lwnn@92000000 { - compatible = "bst,bst_lwnn"; - reg = <0x00 0x92000000 0x00 0x2000000>; - no-map; - }; - - bst_lwnn@94000000 { - compatible = "bst,bst_lwnn"; - reg = <0x00 0x94000000 0x00 0x2000000>; - no-map; - }; - - bst_cv@96000000 { - compatible = "bst,bst_cv"; - reg = <0x00 0x96000000 0x00 0x4000000>; - no-map; - }; - - bst_cv_cma@9a000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x9a000000 0x00 0x2000000>; - align-shift = <0x08>; - no-map; - phandle = <0x80>; - }; - - vsp@0x9c000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0x9c000000 0x00 0x1000000>; - no-map; - phandle = <0x81>; - }; - - vsp_fw@0x9d000000 { - reg = <0x00 0x9d000000 0x00 0x4000000>; - no-map; - }; - - bst_isp@0xa1000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0xa1000000 0x00 0x1000000>; - no-map; - phandle = <0x65>; - }; - - bst_isp_fw@0xa2000000 { - reg = <0x00 0xa2000000 0x00 0x10000000>; - no-map; - }; - - coreip_pub_cma@0xb2000000 { - compatible = "shared-dma-pool"; - align-shift = <0x08>; - reg = <0x00 0xb2000000 0x00 0x36000000>; - reusable; - phandle = <0x66>; - }; - - noc_pmu@0xe8000000 { - compatible = "shared-dma-pool"; - reg = <0x00 0xe8000000 0x00 0x800000>; - reusable; - phandle = <0x43>; - }; - - - ddr0@0xf0000000 { - reg = <0x00 0xf0000000 0x00 0x10000000>; - no-map; - }; - - ddr1@0x1f0000000 { - reg = <0x01 0xf0000000 0x00 0x10000000>; - no-map; - }; - }; - - mbox-poll-clients { - compatible = "bst,ipc-mbox-client"; - reg = <0xfec00020 0x08 0x52030090 0x08 0x53090008 0x08 0xfec00028 0x08>; - #mbox-cells = <0x01>; - phandle = <0x0e>; - }; - - bstn-mbox { - compatible = "bstn,bstn-mbox"; - reg = <0x00 0x33102000 0x00 0x2000 0x00 0x33100000 0x00 0x2000 0x00 0x80000000 0x00 0x04 0x00 0x80002000 0x00 0x04>; - fpga-reset = <0x01>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x71 0xff04 0x00 0x72 0xff04 0x00 0x73 0xff04 0x00 0x74 0xff04 0x00 0x75 0xff04 0x00 0x76 0xff04 0x00 0x77 0xff04 0x00 0x78 0xff04>; - #mbox-cells = <0x01>; - phandle = <0xea>; - }; - - bstn@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bstn-a1000b,cma"; - reg = <0x00 0x50020000 0x00 0x100 0x00 0x90000000 0x00 0x2000000>; - memory-region = <0x66>; - rmem-base = <0x00 0xb2000000>; - rmem-size = <0x00 0x36000000>; - id = <0x00>; - assigned-mem-size = <0x1000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x08>; - firmware = "bstn_dsp_rtos.rbf"; - }; - - bst_cv@0x51030000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst_cv,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x96000000 0x00 0x2000000 0x00 0x98000000 0x00 0x2000000 0x00 0x92000000 0x00 0x2000000 0x00 0x94000000 0x00 0x2000000>; - memory-region = <0x80>; - assigned-mem-size = <0x4000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x09>; - dsp-num = <0x04>; - ipc-register-addr = <0x8ff00000>; - - dsp@0x96000000 { - index = <0x00>; - firmware = "bst_cv_dsp_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x967ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x98000000 { - index = <0x01>; - firmware = "bst_cv_dsp1_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x987ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x92000000 { - index = <0x02>; - firmware = "bst_cv_dsp2_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x927ff000>; - ipc-src-core = <0x03>; - }; - - dsp@0x94000000 { - index = <0x03>; - firmware = "bst_cv_dsp3_rt.rbf"; - assigned-mem-size = <0x1000>; - rt-init-addr = <0x947ff000>; - ipc-src-core = <0x03>; - }; - }; - - bst_gwarp_scaler@0x51060000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst_gwarp_scaler,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x51050000 0x00 0x100 0x00 0x51060000 0x00 0x100>; - memory-region = <0x66>; - id = <0x00>; - bus-offset = <0x00 0x00>; - assigned-mem-size = <0x1000>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9b 0x04>; - interrupt-names = "bst_cv_irq"; - }; - - bare_cv@51030000 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bare_cv,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x96000000 0x00 0x1000000 0x00 0x98000000 0x00 0x1000000 0x00 0x97000000 0x00 0x1000000 0x00 0x99000000 0x00 0x1000000>; - memory-region = <0x80>; - id = <0x01>; - assigned-mem-size = <0x2000000 0x2000000 0x2000000 0x2000000>; - bus-offset = <0x00 0x00>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x09>; - firmware = "bstcv0.rbf\0bstcv1.rbf\0bstcv2.rbf\0bstcv3.rbf"; - }; - - bst_lwnn@0x51030000 { - compatible = "bst,bst_lwnn-a1000b,cma"; - reg = <0x00 0x51030000 0x00 0x100 0x00 0x92000000 0x00 0x2000000 0x00 0x94000000 0x00 0x2000000>; - memory-region = <0x66>; - bus-offset = <0x00 0x00>; - mbox-names = "bst-lwnn-mbox"; - dsp-num = <0x02>; - ipc-register-addr = <0x8ff00000>; - - dsp@0x92000000 { - index = <0x02>; - firmware = "bst_lwnn_dsp2_rt.rbf"; - assigned-mem-size = <0x2000>; - rt-init-addr = <0x927ff000>; - ipc-src-core = <0x06>; - }; - - dsp@0x94000000 { - index = <0x03>; - firmware = "bst_lwnn_dsp3_rt.rbf"; - assigned-mem-size = <0x2000>; - rt-init-addr = <0x947ff000>; - ipc-src-core = <0x00>; - }; - }; - - ipc_vsp@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-vsp-ipc"; - reg = <0x00 0x9c000000 0x00 0x100000 0x00 0x9c100000 0x00 0x80000 0x00 0x9c180000 0x00 0x80000 0x00 0x53090004 0x00 0x04 0x00 0x53090010 0x00 0x0c 0x00 0x33102fbc 0x00 0x04 0x00 0x9d000000 0x00 0x4000000>; - memory-region = <0x81>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x07>; - }; - - vsp@1 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-vsp"; - memory-region = <0x66>; - assigned-mem-size = <0x1000>; - clocks = <0x05 0x43>; - clock-names = "vout_display_clk"; - output-format = "HDMI_RGB"; - output-hsize = <0x780>; - output-vsize = <0x438>; - output-fresh = <0x3c>; - }; - - gmwarp@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-gmwarp"; - memory-region = <0x66>; - }; - - encoder@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,bst-encoder"; - memory-region = <0x66>; - }; - - codec_dma_buf@0 { - compatible = "bst,dma_buf_te"; - memory-region = <0x66>; - }; - - firmware { - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - chip-number = <0x02>; - }; - }; - - tee { - #address-cells = <0x02>; - #size-cells = <0x02>; - reg = <0x00 0x18060000 0x00 0x20000>; - memory-region = <0x82>; - mbox-names = "bstn-mbox"; - chip-number = <0x01>; - }; - - ipc_arm0@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm0"; - reg = <0x04 0xfec00000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x00>; - }; - - ipc_arm3@3 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm3"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x03>; - }; - - ipc_arm2@2 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm2"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x02>; - }; - - ipc_arm1@1 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,arm1"; - reg = <0x04 0xfec10000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x01>; - }; - - ipc@0 { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "bst,ipc"; - reg = <0x04 0xfec00000 0x00 0x20 0x04 0x80000000 0x00 0x20000>; - memory-region = <0x7f>; - assigned-mem-size = <0x1000>; - mbox-names = "bstn-mbox"; - mboxes = <0xea 0x05>; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8.toml b/os/axvisor/configs/vms/linux-aarch64-a1000-smp8.toml deleted file mode 100644 index d3231ee6d..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-a1000-smp8.toml +++ /dev/null @@ -1,64 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 8 -# The physical CPU ids. -phys_cpu_ids = [0x00, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700] -# Guest vm physical cpu sets. -phys_cpu_sets = [1, 2, 4, 8, 16, 32, 64, 128] - - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x1_ce80_0000 -# The location of image: "memory" | "fs". -# Load from memory. -image_location = "memory" -# The load address of the kernel image. -kernel_load_addr = 0x1_ce80_0000 -## The file path of the kernel image. -kernel_path = "/path/to/kernel" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x1_cef0_0000 -dtb_path = "/path/to/dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x1_ce80_0000, 0x2000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL - [0x8000_0000, 0x7000_0000, 0xf, 2], -] -# -# Device specifications -# -[devices] -# The interrupt mode. -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ -["most-devices", 0x0, 0x0, 0x8000_0000, 0x1], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.dts b/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.dts deleted file mode 100644 index 18d68f0f7..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.dts +++ /dev/null @@ -1,1302 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000080000000 0x0000000000010000; -/ { - compatible = "phytium,pe2204"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x40000000 0x00 0x40000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial0 = "/soc/uart@2800c000"; - serial1 = "/soc/uart@2800d000"; - serial2 = "/soc/uart@2800e000"; - serial3 = "/soc/uart@2800f000"; - ethernet0 = "/soc/ethernet@3200c000"; - ethernet1 = "/soc/ethernet@3200e000"; - ethernet2 = "/soc/ethernet@32010000"; - ethernet3 = "/soc/ethernet@32012000"; - serial4 = "/soc/uart@28014000"; - serial5 = "/soc/uart@2802A000"; - serial6 = "/soc/uart@28032000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - firmware { - - scmi { - compatible = "arm,scmi"; - mboxes = <0x02 0x00>; - mbox-names = "tx"; - shmem = <0x03>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@13 { - reg = <0x13>; - #clock-cells = <0x01>; - phandle = <0x09>; - }; - - protocol@15 { - reg = <0x15>; - #thermal-sensor-cells = <0x01>; - phandle = <0x04>; - }; - }; - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - }; - }; - - thermal-zones { - - sensor0 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x00>; - }; - - sensor1 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x01>; - }; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - // cluster0 { - - // core0 { - // cpu = <0x05>; - // }; - // }; - - cluster1 { - - core0 { - cpu = <0x06>; - }; - }; - - // cluster2 { - - // core0 { - // cpu = <0x07>; - // }; - - // core1 { - // cpu = <0x08>; - // }; - // }; - }; - - // cpu@0 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x200>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x07>; - // }; - - // cpu@1 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x201>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x08>; - // }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x00>; - // enable-method = "psci"; - // clocks = <0x09 0x00>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x05>; - // }; - - cpu@101 { - device_type = "cpu"; - compatible = "phytium,ftc664\0arm,armv8"; - reg = <0x00 0x100>; - enable-method = "psci"; - clocks = <0x09 0x01>; - capacity-dmips-mhz = <0x161c>; - phandle = <0x06>; - }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0x01 0x07 0x08>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - clocks { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - clk48mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2dc6c00>; - phandle = <0x13>; - }; - - clk50mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2faf080>; - phandle = <0x0d>; - }; - - clk100mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x5f5e100>; - phandle = <0x0c>; - }; - - clk200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbebc200>; - phandle = <0x11>; - }; - - clk250mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xee6b280>; - phandle = <0x12>; - }; - - clk300mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x11e1a300>; - phandle = <0x0b>; - }; - - clk600mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x23c34600>; - phandle = <0x0e>; - }; - - clk1200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x47868c00>; - phandle = <0x0a>; - }; - }; - - iommu@30000000 { - compatible = "arm,smmu-v3"; - reg = <0x00 0x30000000 0x00 0x800000>; - interrupts = <0x00 0xf0 0x01 0x00 0xef 0x01 0x00 0xec 0x01 0x00 0xf2 0x01>; - interrupt-names = "eventq\0priq\0cmdq-sync\0gerror"; - dma-coherent; - #iommu-cells = <0x01>; - phandle = <0x10>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - mmc@28000000 { - compatible = "phytium,mci"; - reg = <0x00 0x28000000 0x00 0x1000>; - interrupts = <0x00 0x48 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "okay"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - }; - - mmc@28001000 { - compatible = "phytium,mci"; - reg = <0x00 0x28001000 0x00 0x1000>; - interrupts = <0x00 0x49 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "okay"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - no-sd; - non-removable; - }; - - nand@28002000 { - compatible = "phytium,nfc"; - reg = <0x00 0x28002000 0x00 0x1000>; - interrupts = <0x00 0x4a 0x04>; - status = "disabled"; - }; - - ddma@28003000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28003000 0x00 0x1000>; - interrupts = <0x00 0x4b 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - ddma@28004000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28004000 0x00 0x1000>; - interrupts = <0x00 0x4c 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - spi@28008000 { - compatible = "phytium,qspi-nor"; - reg = <0x00 0x28008000 0x00 0x1000 0x00 0x00 0x00 0xfffffff>; - reg-names = "qspi\0qspi_mm"; - clocks = <0x0b>; - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x00>; - spi-rx-bus-width = <0x01>; - spi-max-frequency = <0x1312d00>; - status = "okay"; - }; - }; - - uart@2800c000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800c000 0x00 0x1000>; - interrupts = <0x00 0x53 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800e000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800e000 0x00 0x1000>; - interrupts = <0x00 0x55 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800f000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800f000 0x00 0x1000>; - interrupts = <0x00 0x56 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - lpc@28010000 { - compatible = "simple-mfd\0syscon"; - reg = <0x00 0x28010000 0x00 0x1000>; - reg-io-width = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x28010000 0x1000>; - - kcs@24 { - compatible = "phytium,kcs-bmc"; - reg = <0x24 0x01 0x30 0x01 0x3c 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@28 { - compatible = "phytium,kcs-bmc"; - reg = <0x28 0x01 0x34 0x01 0x40 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@2c { - compatible = "phytium,kcs-bmc"; - reg = <0x2c 0x01 0x38 0x01 0x44 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@8c { - compatible = "phytium,kcs-bmc"; - reg = <0x8c 0x01 0x90 0x01 0x94 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - bt@48 { - compatible = "phytium,bt-bmc"; - reg = <0x48 0x20>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - }; - - gpio@28034000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28034000 0x00 0x1000>; - interrupts = <0x00 0x6c 0x04 0x00 0x6d 0x04 0x00 0x6e 0x04 0x00 0x6f 0x04 0x00 0x70 0x04 0x00 0x71 0x04 0x00 0x72 0x04 0x00 0x73 0x04 0x00 0x74 0x04 0x00 0x75 0x04 0x00 0x76 0x04 0x00 0x77 0x04 0x00 0x78 0x04 0x00 0x79 0x04 0x00 0x7a 0x04 0x00 0x7b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28035000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28035000 0x00 0x1000>; - interrupts = <0x00 0x7c 0x04 0x00 0x7d 0x04 0x00 0x7e 0x04 0x00 0x7f 0x04 0x00 0x80 0x04 0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04 0x00 0x89 0x04 0x00 0x8a 0x04 0x00 0x8b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x14>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28036000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28036000 0x00 0x1000>; - interrupts = <0x00 0x8c 0x04 0x00 0x8d 0x04 0x00 0x8e 0x04 0x00 0x8f 0x04 0x00 0x90 0x04 0x00 0x91 0x04 0x00 0x92 0x04 0x00 0x93 0x04 0x00 0x94 0x04 0x00 0x95 0x04 0x00 0x96 0x04 0x00 0x97 0x04 0x00 0x98 0x04 0x00 0x99 0x04 0x00 0x9a 0x04 0x00 0x9b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x15>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28037000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28037000 0x00 0x1000>; - interrupts = <0x00 0x9c 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28038000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28038000 0x00 0x1000>; - interrupts = <0x00 0x9d 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28039000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28039000 0x00 0x1000>; - interrupts = <0x00 0x9e 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - spi@2803a000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803a000 0x00 0x1000>; - interrupts = <0x00 0x9f 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - global-cs = <0x01>; - - spidev@0 { - compatible = "spidev"; - reg = <0x00>; - spi-max-frequency = <0x2faf080>; - status = "disabled"; - }; - }; - - spi@2803b000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803b000 0x00 0x1000>; - interrupts = <0x00 0xa0 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803c000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803c000 0x00 0x1000>; - interrupts = <0x00 0xa1 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803d000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803d000 0x00 0x1000>; - interrupts = <0x00 0xa2 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - watchdog@28040000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28041000 0x00 0x1000 0x00 0x28040000 0x00 0x1000>; - interrupts = <0x00 0xa4 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - watchdog@28042000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28043000 0x00 0x1000 0x00 0x28042000 0x00 0x1000>; - interrupts = <0x00 0xa5 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - pwm@2804a000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804a000 0x00 0x1000>; - interrupts = <0x00 0xad 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - pwm@2804b000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804b000 0x00 0x1000>; - interrupts = <0x00 0xae 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - tacho@28054000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28054000 0x00 0x1000>; - interrupts = <0x00 0xc2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28055000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28055000 0x00 0x1000>; - interrupts = <0x00 0xc3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28056000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28056000 0x00 0x1000>; - interrupts = <0x00 0xc4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28057000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28057000 0x00 0x1000>; - interrupts = <0x00 0xc5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28058000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28058000 0x00 0x1000>; - interrupts = <0x00 0xc6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28059000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28059000 0x00 0x1000>; - interrupts = <0x00 0xc7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805a000 0x00 0x1000>; - interrupts = <0x00 0xc8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805b000 0x00 0x1000>; - interrupts = <0x00 0xc9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805c000 0x00 0x1000>; - interrupts = <0x00 0xca 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805d000 0x00 0x1000>; - interrupts = <0x00 0xcb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805e000 0x00 0x1000>; - interrupts = <0x00 0xcc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805f000 0x00 0x1000>; - interrupts = <0x00 0xcd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28060000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28060000 0x00 0x1000>; - interrupts = <0x00 0xce 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28061000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28061000 0x00 0x1000>; - interrupts = <0x00 0xcf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28062000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28062000 0x00 0x1000>; - interrupts = <0x00 0xd0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28063000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28063000 0x00 0x1000>; - interrupts = <0x00 0xd1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28064000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28064000 0x00 0x1000>; - interrupts = <0x00 0xd2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28065000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28065000 0x00 0x1000>; - interrupts = <0x00 0xd3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28066000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28066000 0x00 0x1000>; - interrupts = <0x00 0xd4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28067000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28067000 0x00 0x1000>; - interrupts = <0x00 0xd5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28068000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28068000 0x00 0x1000>; - interrupts = <0x00 0xd6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28069000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28069000 0x00 0x1000>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806a000 0x00 0x1000>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806b000 0x00 0x1000>; - interrupts = <0x00 0xd9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806c000 0x00 0x1000>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806d000 0x00 0x1000>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806e000 0x00 0x1000>; - interrupts = <0x00 0xdc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806f000 0x00 0x1000>; - interrupts = <0x00 0xdd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28070000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28070000 0x00 0x1000>; - interrupts = <0x00 0xde 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28071000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28071000 0x00 0x1000>; - interrupts = <0x00 0xdf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28072000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28072000 0x00 0x1000>; - interrupts = <0x00 0xe0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28073000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28073000 0x00 0x1000>; - interrupts = <0x00 0xe1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28074000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28074000 0x00 0x1000>; - interrupts = <0x00 0xe2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28075000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28075000 0x00 0x1000>; - interrupts = <0x00 0xe3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28076000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28076000 0x00 0x1000>; - interrupts = <0x00 0xe4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28077000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28077000 0x00 0x1000>; - interrupts = <0x00 0xe5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28078000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28078000 0x00 0x1000>; - interrupts = <0x00 0xe6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28079000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28079000 0x00 0x1000>; - interrupts = <0x00 0xe7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb2@31800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31800000 0x00 0x80000 0x00 0x31990000 0x00 0x10000>; - interrupts = <0x00 0x20 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@31880000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31880000 0x00 0x80000 0x00 0x319a0000 0x00 0x10000>; - interrupts = <0x00 0x21 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@31900000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31900000 0x00 0x80000 0x00 0x319b0000 0x00 0x10000>; - interrupts = <0x00 0x22 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@32800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32800000 0x00 0x40000 0x00 0x32880000 0x00 0x40000>; - interrupts = <0x00 0x0e 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@32840000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32840000 0x00 0x40000 0x00 0x328c0000 0x00 0x40000>; - interrupts = <0x00 0x0f 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - dc@32000000 { - compatible = "phytium,dc"; - reg = <0x00 0x32000000 0x00 0x8000>; - interrupts = <0x00 0x2c 0x04>; - status = "okay"; - pipe_mask = [01]; - edp_mask = [00]; - }; - - i2s_dp0@32009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x32009000 0x00 0x1000 0x00 0x32008000 0x00 0x1000>; - interrupts = <0x00 0x2f 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp0"; - status = "okay"; - }; - - i2s_dp1@3200B000 { - compatible = "phytium,i2s"; - reg = <0x00 0x3200b000 0x00 0x1000 0x00 0x3200a000 0x00 0x1000>; - interrupts = <0x00 0x30 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp1"; - status = "disabled"; - }; - - pmdk_dp { - compatible = "phytium,pmdk-dp"; - status = "okay"; - num-dp = <0x01>; - dp-mask = [01]; - }; - - mailbox@32a00000 { - compatible = "phytium,mbox"; - reg = <0x00 0x32a00000 0x00 0x1000>; - interrupts = <0x00 0x16 0x04>; - #mbox-cells = <0x01>; - phandle = <0x02>; - }; - - rng@32a36000 { - compatible = "phytium,rng"; - reg = <0x00 0x32a36000 0x00 0x1000>; - status = "okay"; - }; - - sram@32a10000 { - compatible = "phytium,pe220x-sram-ns\0mmio-sram"; - reg = <0x00 0x32a10000 0x00 0x2000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x32a10000 0x2000>; - - scp-shmem@0 { - compatible = "arm,scmi-shmem"; - reg = <0x1000 0x400>; - }; - - scp-shmem@1 { - compatible = "arm,scmi-shmem"; - reg = <0x1400 0x400>; - phandle = <0x03>; - }; - }; - - gdma@32b34000 { - compatible = "phytium,gdma"; - dma-channels = <0x10>; - max-outstanding = <0x10>; - reg = <0x00 0x32b34000 0x00 0x1000>; - interrupts = <0x00 0xea 0x04>; - #dma-cells = <0x01>; - }; - - spinlock@32b36000 { - compatible = "phytium,hwspinlock"; - reg = <0x00 0x32b36000 0x00 0x1000>; - #hwlock-cells = <0x01>; - nr-locks = <0x20>; - status = "disabled"; - }; - - pcie@40000000 { - compatible = "pci-host-ecam-generic"; - device_type = "pci"; - #address-cells = <0x03>; - #size-cells = <0x02>; - #interrupt-cells = <0x01>; - reg = <0x00 0x40000000 0x00 0x10000000>; - msi-parent = <0x0f>; - bus-range = <0x00 0xff>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x07 0x04>; - ranges = <0x1000000 0x00 0x00 0x00 0x50000000 0x00 0xf00000 0x2000000 0x00 0x58000000 0x00 0x58000000 0x00 0x28000000 0x3000000 0x10 0x00 0x10 0x00 0x10 0x00>; - iommu-map = <0x00 0x10 0x00 0x10000>; - status = "okay"; - }; - - edac@32b28000 { - compatible = "phytium,pe220x-edac"; - reg = <0x00 0x32b28000 0x00 0x1000 0x00 0x31400000 0x00 0x1000 0x00 0x31401000 0x00 0x1000>; - interrupts = <0x00 0x00 0x04 0x00 0x01 0x04>; - status = "disabled"; - }; - - hda@28006000 { - compatible = "phytium,hda"; - reg = <0x00 0x28006000 0x00 0x1000>; - interrupts = <0x00 0x4e 0x04>; - status = "disabled"; - }; - - i2s@28009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x28009000 0x00 0x1000 0x00 0x28005000 0x00 0x1000>; - interrupts = <0x00 0x4d 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - status = "okay"; - #sound-dai-cells = <0x00>; - dai-name = "phytium-i2s-lsd"; - phandle = <0x16>; - }; - - can@2800a000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800a000 0x00 0x1000>; - interrupts = <0x00 0x51 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - can@2800b000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800b000 0x00 0x1000>; - interrupts = <0x00 0x52 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - keypad@2807a000 { - compatible = "phytium,keypad"; - reg = <0x00 0x2807a000 0x00 0x1000>; - interrupts = <0x00 0xbd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb3@31a08000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a08000 0x00 0x18000>; - interrupts = <0x00 0x10 0x04>; - status = "okay"; - }; - - usb3@31a28000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a28000 0x00 0x18000>; - interrupts = <0x00 0x11 0x04>; - status = "okay"; - }; - - sata@31a40000 { - compatible = "generic-ahci"; - reg = <0x00 0x31a40000 0x00 0x1000>; - interrupts = <0x00 0x2a 0x04>; - status = "disabled"; - }; - - sata@32014000 { - compatible = "generic-ahci"; - reg = <0x00 0x32014000 0x00 0x1000>; - interrupts = <0x00 0x2b 0x04>; - status = "disabled"; - }; - - ethernet@3200c000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200c000 0x00 0x2000>; - interrupts = <0x00 0x37 0x04 0x00 0x38 0x04 0x00 0x39 0x04 0x00 0x3a 0x04 0x00 0x1c 0x04 0x00 0x1d 0x04 0x00 0x1e 0x04 0x00 0x1f 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - support-tsn; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@3200e000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200e000 0x00 0x2000>; - interrupts = <0x00 0x3b 0x04 0x00 0x3c 0x04 0x00 0x3d 0x04 0x00 0x3e 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@32010000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32010000 0x00 0x2000>; - interrupts = <0x00 0x40 0x04 0x00 0x41 0x04 0x00 0x42 0x04 0x00 0x43 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - ethernet@32012000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32012000 0x00 0x2000>; - interrupts = <0x00 0x44 0x04 0x00 0x45 0x04 0x00 0x46 0x04 0x00 0x47 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - vpu@32b00000 { - compatible = "phytium,vpu"; - reg = <0x00 0x32b00000 0x00 0x20000>; - interrupts = <0x00 0x0c 0x04>; - status = "okay"; - }; - - i2c@28026000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28026000 0x00 0x1000>; - interrupts = <0x00 0x65 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28030000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28030000 0x00 0x1000>; - interrupts = <0x00 0x6a 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - es8336@10 { - #sound-dai-cells = <0x00>; - compatible = "everest,es8336"; - reg = <0x10>; - phandle = <0x17>; - }; - }; - - uart@28014000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28014000 0x00 0x1000>; - interrupts = <0x00 0x5c 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - i2c@28016000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28016000 0x00 0x1000>; - interrupts = <0x00 0x5d 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28024000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28024000 0x00 0x1000>; - interrupts = <0x00 0x64 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - uart@2802A000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2802a000 0x00 0x1000>; - interrupts = <0x00 0x67 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@28032000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28032000 0x00 0x1000>; - interrupts = <0x00 0x6b 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 root=/dev/mmcblk0p1 rootfstype=ext4 rootwait rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory { - device_type = "memory"; - reg = <0x20 0x40000000 0x00 0x40000000>; - }; - - leds { - compatible = "gpio-leds"; - - sysled { - label = "sysled"; - gpios = <0x14 0x05 0x00>; - linux,default-trigger = "none"; - }; - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "phytium,pe220x-i2s-audio"; - simple-audio-card,pin-switches = "mic-in"; - simple-audio-card,widgets = "Microphone\0mic-in\0Headphone\0Headphones"; - simple-audio-card,routing = "MIC2\0mic-in"; - simple-audio-card,hp-det-gpio = <0x15 0x0b 0x01>; - - simple-audio-card,cpu { - sound-dai = <0x16>; - }; - - simple-audio-card,codec { - sound-dai = <0x17>; - }; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.toml b/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.toml deleted file mode 100644 index ba5a6d324..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-e2000-smp1.toml +++ /dev/null @@ -1,62 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x100] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_4008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x20_4008_0000 -## The file path of the kernel image. -kernel_path = "/guest/linux/phytiumpi" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_4000_0000 -#dtb_path = "/path/to/axvisor/configs/vms/linux-aarch64-e2000_smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_4000_0000, 0x4000_0000, 0x7, 1], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -interrupt_mode = "passthrough" -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.dts b/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.dts deleted file mode 100644 index 0d3b86b4c..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.dts +++ /dev/null @@ -1,1302 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000080000000 0x0000000000010000; -/ { - compatible = "phytium,pe2204"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x00000000 0x00 0x40000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial0 = "/soc/uart@2800c000"; - serial1 = "/soc/uart@2800d000"; - serial2 = "/soc/uart@2800e000"; - serial3 = "/soc/uart@2800f000"; - ethernet0 = "/soc/ethernet@3200c000"; - ethernet1 = "/soc/ethernet@3200e000"; - ethernet2 = "/soc/ethernet@32010000"; - ethernet3 = "/soc/ethernet@32012000"; - serial4 = "/soc/uart@28014000"; - serial5 = "/soc/uart@2802A000"; - serial6 = "/soc/uart@28032000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - firmware { - - scmi { - compatible = "arm,scmi"; - mboxes = <0x02 0x00>; - mbox-names = "tx"; - shmem = <0x03>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@13 { - reg = <0x13>; - #clock-cells = <0x01>; - phandle = <0x09>; - }; - - protocol@15 { - reg = <0x15>; - #thermal-sensor-cells = <0x01>; - phandle = <0x04>; - }; - }; - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - }; - }; - - thermal-zones { - - sensor0 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x00>; - }; - - sensor1 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x01>; - }; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - cluster0 { - - core0 { - cpu = <0x05>; - }; - }; - - // cluster1 { - - // core0 { - // cpu = <0x06>; - // }; - // }; - - cluster2 { - - core0 { - cpu = <0x07>; - }; - - // core1 { - // cpu = <0x08>; - // }; - }; - }; - - cpu@0 { - device_type = "cpu"; - compatible = "phytium,ftc310\0arm,armv8"; - reg = <0x00 0x200>; - enable-method = "psci"; - clocks = <0x09 0x02>; - capacity-dmips-mhz = <0xb22>; - phandle = <0x07>; - }; - - // cpu@1 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x201>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x08>; - // }; - - cpu@100 { - device_type = "cpu"; - compatible = "phytium,ftc664\0arm,armv8"; - reg = <0x00 0x00>; - enable-method = "psci"; - clocks = <0x09 0x00>; - capacity-dmips-mhz = <0x161c>; - phandle = <0x05>; - }; - - // cpu@101 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x100>; - // enable-method = "psci"; - // clocks = <0x09 0x01>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x06>; - // }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0x01 0x07 0x08>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - clocks { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - clk48mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2dc6c00>; - phandle = <0x13>; - }; - - clk50mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2faf080>; - phandle = <0x0d>; - }; - - clk100mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x5f5e100>; - phandle = <0x0c>; - }; - - clk200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbebc200>; - phandle = <0x11>; - }; - - clk250mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xee6b280>; - phandle = <0x12>; - }; - - clk300mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x11e1a300>; - phandle = <0x0b>; - }; - - clk600mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x23c34600>; - phandle = <0x0e>; - }; - - clk1200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x47868c00>; - phandle = <0x0a>; - }; - }; - - iommu@30000000 { - compatible = "arm,smmu-v3"; - reg = <0x00 0x30000000 0x00 0x800000>; - interrupts = <0x00 0xf0 0x01 0x00 0xef 0x01 0x00 0xec 0x01 0x00 0xf2 0x01>; - interrupt-names = "eventq\0priq\0cmdq-sync\0gerror"; - dma-coherent; - #iommu-cells = <0x01>; - phandle = <0x10>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - mmc@28000000 { - compatible = "phytium,mci"; - reg = <0x00 0x28000000 0x00 0x1000>; - interrupts = <0x00 0x48 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "okay"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - }; - - mmc@28001000 { - compatible = "phytium,mci"; - reg = <0x00 0x28001000 0x00 0x1000>; - interrupts = <0x00 0x49 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "okay"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - no-sd; - non-removable; - }; - - nand@28002000 { - compatible = "phytium,nfc"; - reg = <0x00 0x28002000 0x00 0x1000>; - interrupts = <0x00 0x4a 0x04>; - status = "disabled"; - }; - - ddma@28003000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28003000 0x00 0x1000>; - interrupts = <0x00 0x4b 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - ddma@28004000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28004000 0x00 0x1000>; - interrupts = <0x00 0x4c 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - spi@28008000 { - compatible = "phytium,qspi-nor"; - reg = <0x00 0x28008000 0x00 0x1000 0x00 0x00 0x00 0xfffffff>; - reg-names = "qspi\0qspi_mm"; - clocks = <0x0b>; - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x00>; - spi-rx-bus-width = <0x01>; - spi-max-frequency = <0x1312d00>; - status = "okay"; - }; - }; - - uart@2800c000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800c000 0x00 0x1000>; - interrupts = <0x00 0x53 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800e000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800e000 0x00 0x1000>; - interrupts = <0x00 0x55 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800f000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800f000 0x00 0x1000>; - interrupts = <0x00 0x56 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - lpc@28010000 { - compatible = "simple-mfd\0syscon"; - reg = <0x00 0x28010000 0x00 0x1000>; - reg-io-width = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x28010000 0x1000>; - - kcs@24 { - compatible = "phytium,kcs-bmc"; - reg = <0x24 0x01 0x30 0x01 0x3c 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@28 { - compatible = "phytium,kcs-bmc"; - reg = <0x28 0x01 0x34 0x01 0x40 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@2c { - compatible = "phytium,kcs-bmc"; - reg = <0x2c 0x01 0x38 0x01 0x44 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@8c { - compatible = "phytium,kcs-bmc"; - reg = <0x8c 0x01 0x90 0x01 0x94 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - bt@48 { - compatible = "phytium,bt-bmc"; - reg = <0x48 0x20>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - }; - - gpio@28034000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28034000 0x00 0x1000>; - interrupts = <0x00 0x6c 0x04 0x00 0x6d 0x04 0x00 0x6e 0x04 0x00 0x6f 0x04 0x00 0x70 0x04 0x00 0x71 0x04 0x00 0x72 0x04 0x00 0x73 0x04 0x00 0x74 0x04 0x00 0x75 0x04 0x00 0x76 0x04 0x00 0x77 0x04 0x00 0x78 0x04 0x00 0x79 0x04 0x00 0x7a 0x04 0x00 0x7b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28035000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28035000 0x00 0x1000>; - interrupts = <0x00 0x7c 0x04 0x00 0x7d 0x04 0x00 0x7e 0x04 0x00 0x7f 0x04 0x00 0x80 0x04 0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04 0x00 0x89 0x04 0x00 0x8a 0x04 0x00 0x8b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x14>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28036000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28036000 0x00 0x1000>; - interrupts = <0x00 0x8c 0x04 0x00 0x8d 0x04 0x00 0x8e 0x04 0x00 0x8f 0x04 0x00 0x90 0x04 0x00 0x91 0x04 0x00 0x92 0x04 0x00 0x93 0x04 0x00 0x94 0x04 0x00 0x95 0x04 0x00 0x96 0x04 0x00 0x97 0x04 0x00 0x98 0x04 0x00 0x99 0x04 0x00 0x9a 0x04 0x00 0x9b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x15>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28037000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28037000 0x00 0x1000>; - interrupts = <0x00 0x9c 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28038000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28038000 0x00 0x1000>; - interrupts = <0x00 0x9d 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28039000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28039000 0x00 0x1000>; - interrupts = <0x00 0x9e 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - spi@2803a000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803a000 0x00 0x1000>; - interrupts = <0x00 0x9f 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - global-cs = <0x01>; - - spidev@0 { - compatible = "spidev"; - reg = <0x00>; - spi-max-frequency = <0x2faf080>; - status = "disabled"; - }; - }; - - spi@2803b000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803b000 0x00 0x1000>; - interrupts = <0x00 0xa0 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803c000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803c000 0x00 0x1000>; - interrupts = <0x00 0xa1 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803d000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803d000 0x00 0x1000>; - interrupts = <0x00 0xa2 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - watchdog@28040000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28041000 0x00 0x1000 0x00 0x28040000 0x00 0x1000>; - interrupts = <0x00 0xa4 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - watchdog@28042000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28043000 0x00 0x1000 0x00 0x28042000 0x00 0x1000>; - interrupts = <0x00 0xa5 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - pwm@2804a000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804a000 0x00 0x1000>; - interrupts = <0x00 0xad 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - pwm@2804b000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804b000 0x00 0x1000>; - interrupts = <0x00 0xae 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - tacho@28054000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28054000 0x00 0x1000>; - interrupts = <0x00 0xc2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28055000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28055000 0x00 0x1000>; - interrupts = <0x00 0xc3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28056000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28056000 0x00 0x1000>; - interrupts = <0x00 0xc4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28057000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28057000 0x00 0x1000>; - interrupts = <0x00 0xc5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28058000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28058000 0x00 0x1000>; - interrupts = <0x00 0xc6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28059000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28059000 0x00 0x1000>; - interrupts = <0x00 0xc7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805a000 0x00 0x1000>; - interrupts = <0x00 0xc8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805b000 0x00 0x1000>; - interrupts = <0x00 0xc9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805c000 0x00 0x1000>; - interrupts = <0x00 0xca 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805d000 0x00 0x1000>; - interrupts = <0x00 0xcb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805e000 0x00 0x1000>; - interrupts = <0x00 0xcc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805f000 0x00 0x1000>; - interrupts = <0x00 0xcd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28060000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28060000 0x00 0x1000>; - interrupts = <0x00 0xce 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28061000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28061000 0x00 0x1000>; - interrupts = <0x00 0xcf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28062000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28062000 0x00 0x1000>; - interrupts = <0x00 0xd0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28063000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28063000 0x00 0x1000>; - interrupts = <0x00 0xd1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28064000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28064000 0x00 0x1000>; - interrupts = <0x00 0xd2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28065000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28065000 0x00 0x1000>; - interrupts = <0x00 0xd3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28066000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28066000 0x00 0x1000>; - interrupts = <0x00 0xd4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28067000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28067000 0x00 0x1000>; - interrupts = <0x00 0xd5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28068000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28068000 0x00 0x1000>; - interrupts = <0x00 0xd6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28069000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28069000 0x00 0x1000>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806a000 0x00 0x1000>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806b000 0x00 0x1000>; - interrupts = <0x00 0xd9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806c000 0x00 0x1000>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806d000 0x00 0x1000>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806e000 0x00 0x1000>; - interrupts = <0x00 0xdc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806f000 0x00 0x1000>; - interrupts = <0x00 0xdd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28070000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28070000 0x00 0x1000>; - interrupts = <0x00 0xde 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28071000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28071000 0x00 0x1000>; - interrupts = <0x00 0xdf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28072000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28072000 0x00 0x1000>; - interrupts = <0x00 0xe0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28073000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28073000 0x00 0x1000>; - interrupts = <0x00 0xe1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28074000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28074000 0x00 0x1000>; - interrupts = <0x00 0xe2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28075000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28075000 0x00 0x1000>; - interrupts = <0x00 0xe3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28076000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28076000 0x00 0x1000>; - interrupts = <0x00 0xe4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28077000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28077000 0x00 0x1000>; - interrupts = <0x00 0xe5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28078000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28078000 0x00 0x1000>; - interrupts = <0x00 0xe6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28079000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28079000 0x00 0x1000>; - interrupts = <0x00 0xe7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb2@31800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31800000 0x00 0x80000 0x00 0x31990000 0x00 0x10000>; - interrupts = <0x00 0x20 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@31880000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31880000 0x00 0x80000 0x00 0x319a0000 0x00 0x10000>; - interrupts = <0x00 0x21 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@31900000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31900000 0x00 0x80000 0x00 0x319b0000 0x00 0x10000>; - interrupts = <0x00 0x22 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@32800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32800000 0x00 0x40000 0x00 0x32880000 0x00 0x40000>; - interrupts = <0x00 0x0e 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@32840000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32840000 0x00 0x40000 0x00 0x328c0000 0x00 0x40000>; - interrupts = <0x00 0x0f 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - dc@32000000 { - compatible = "phytium,dc"; - reg = <0x00 0x32000000 0x00 0x8000>; - interrupts = <0x00 0x2c 0x04>; - status = "okay"; - pipe_mask = [01]; - edp_mask = [00]; - }; - - i2s_dp0@32009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x32009000 0x00 0x1000 0x00 0x32008000 0x00 0x1000>; - interrupts = <0x00 0x2f 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp0"; - status = "okay"; - }; - - i2s_dp1@3200B000 { - compatible = "phytium,i2s"; - reg = <0x00 0x3200b000 0x00 0x1000 0x00 0x3200a000 0x00 0x1000>; - interrupts = <0x00 0x30 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp1"; - status = "disabled"; - }; - - pmdk_dp { - compatible = "phytium,pmdk-dp"; - status = "okay"; - num-dp = <0x01>; - dp-mask = [01]; - }; - - mailbox@32a00000 { - compatible = "phytium,mbox"; - reg = <0x00 0x32a00000 0x00 0x1000>; - interrupts = <0x00 0x16 0x04>; - #mbox-cells = <0x01>; - phandle = <0x02>; - }; - - rng@32a36000 { - compatible = "phytium,rng"; - reg = <0x00 0x32a36000 0x00 0x1000>; - status = "okay"; - }; - - sram@32a10000 { - compatible = "phytium,pe220x-sram-ns\0mmio-sram"; - reg = <0x00 0x32a10000 0x00 0x2000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x32a10000 0x2000>; - - scp-shmem@0 { - compatible = "arm,scmi-shmem"; - reg = <0x1000 0x400>; - }; - - scp-shmem@1 { - compatible = "arm,scmi-shmem"; - reg = <0x1400 0x400>; - phandle = <0x03>; - }; - }; - - gdma@32b34000 { - compatible = "phytium,gdma"; - dma-channels = <0x10>; - max-outstanding = <0x10>; - reg = <0x00 0x32b34000 0x00 0x1000>; - interrupts = <0x00 0xea 0x04>; - #dma-cells = <0x01>; - }; - - spinlock@32b36000 { - compatible = "phytium,hwspinlock"; - reg = <0x00 0x32b36000 0x00 0x1000>; - #hwlock-cells = <0x01>; - nr-locks = <0x20>; - status = "disabled"; - }; - - pcie@40000000 { - compatible = "pci-host-ecam-generic"; - device_type = "pci"; - #address-cells = <0x03>; - #size-cells = <0x02>; - #interrupt-cells = <0x01>; - reg = <0x00 0x40000000 0x00 0x10000000>; - msi-parent = <0x0f>; - bus-range = <0x00 0xff>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x07 0x04>; - ranges = <0x1000000 0x00 0x00 0x00 0x50000000 0x00 0xf00000 0x2000000 0x00 0x58000000 0x00 0x58000000 0x00 0x28000000 0x3000000 0x10 0x00 0x10 0x00 0x10 0x00>; - iommu-map = <0x00 0x10 0x00 0x10000>; - status = "okay"; - }; - - edac@32b28000 { - compatible = "phytium,pe220x-edac"; - reg = <0x00 0x32b28000 0x00 0x1000 0x00 0x31400000 0x00 0x1000 0x00 0x31401000 0x00 0x1000>; - interrupts = <0x00 0x00 0x04 0x00 0x01 0x04>; - status = "disabled"; - }; - - hda@28006000 { - compatible = "phytium,hda"; - reg = <0x00 0x28006000 0x00 0x1000>; - interrupts = <0x00 0x4e 0x04>; - status = "disabled"; - }; - - i2s@28009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x28009000 0x00 0x1000 0x00 0x28005000 0x00 0x1000>; - interrupts = <0x00 0x4d 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - status = "okay"; - #sound-dai-cells = <0x00>; - dai-name = "phytium-i2s-lsd"; - phandle = <0x16>; - }; - - can@2800a000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800a000 0x00 0x1000>; - interrupts = <0x00 0x51 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - can@2800b000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800b000 0x00 0x1000>; - interrupts = <0x00 0x52 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - keypad@2807a000 { - compatible = "phytium,keypad"; - reg = <0x00 0x2807a000 0x00 0x1000>; - interrupts = <0x00 0xbd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb3@31a08000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a08000 0x00 0x18000>; - interrupts = <0x00 0x10 0x04>; - status = "okay"; - }; - - usb3@31a28000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a28000 0x00 0x18000>; - interrupts = <0x00 0x11 0x04>; - status = "okay"; - }; - - sata@31a40000 { - compatible = "generic-ahci"; - reg = <0x00 0x31a40000 0x00 0x1000>; - interrupts = <0x00 0x2a 0x04>; - status = "disabled"; - }; - - sata@32014000 { - compatible = "generic-ahci"; - reg = <0x00 0x32014000 0x00 0x1000>; - interrupts = <0x00 0x2b 0x04>; - status = "disabled"; - }; - - ethernet@3200c000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200c000 0x00 0x2000>; - interrupts = <0x00 0x37 0x04 0x00 0x38 0x04 0x00 0x39 0x04 0x00 0x3a 0x04 0x00 0x1c 0x04 0x00 0x1d 0x04 0x00 0x1e 0x04 0x00 0x1f 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - support-tsn; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@3200e000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200e000 0x00 0x2000>; - interrupts = <0x00 0x3b 0x04 0x00 0x3c 0x04 0x00 0x3d 0x04 0x00 0x3e 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@32010000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32010000 0x00 0x2000>; - interrupts = <0x00 0x40 0x04 0x00 0x41 0x04 0x00 0x42 0x04 0x00 0x43 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - ethernet@32012000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32012000 0x00 0x2000>; - interrupts = <0x00 0x44 0x04 0x00 0x45 0x04 0x00 0x46 0x04 0x00 0x47 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - vpu@32b00000 { - compatible = "phytium,vpu"; - reg = <0x00 0x32b00000 0x00 0x20000>; - interrupts = <0x00 0x0c 0x04>; - status = "okay"; - }; - - i2c@28026000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28026000 0x00 0x1000>; - interrupts = <0x00 0x65 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28030000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28030000 0x00 0x1000>; - interrupts = <0x00 0x6a 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - es8336@10 { - #sound-dai-cells = <0x00>; - compatible = "everest,es8336"; - reg = <0x10>; - phandle = <0x17>; - }; - }; - - uart@28014000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28014000 0x00 0x1000>; - interrupts = <0x00 0x5c 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - i2c@28016000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28016000 0x00 0x1000>; - interrupts = <0x00 0x5d 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28024000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28024000 0x00 0x1000>; - interrupts = <0x00 0x64 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - uart@2802A000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2802a000 0x00 0x1000>; - interrupts = <0x00 0x67 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@28032000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28032000 0x00 0x1000>; - interrupts = <0x00 0x6b 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 root=/dev/mmcblk0p1 rootfstype=ext4 rootwait rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory@00 { - device_type = "memory"; - reg = <0x20 0x40000000 0x00 0x40000000>; - }; - - leds { - compatible = "gpio-leds"; - - sysled { - label = "sysled"; - gpios = <0x14 0x05 0x00>; - linux,default-trigger = "none"; - }; - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "phytium,pe220x-i2s-audio"; - simple-audio-card,pin-switches = "mic-in"; - simple-audio-card,widgets = "Microphone\0mic-in\0Headphone\0Headphones"; - simple-audio-card,routing = "MIC2\0mic-in"; - simple-audio-card,hp-det-gpio = <0x15 0x0b 0x01>; - - simple-audio-card,cpu { - sound-dai = <0x16>; - }; - - simple-audio-card,codec { - sound-dai = <0x17>; - }; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.toml b/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.toml deleted file mode 100644 index 84d2ab001..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-e2000-smp2.toml +++ /dev/null @@ -1,63 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 2 -# The physical CPU ids. -phys_cpu_ids = [0x200, 0x00] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_4008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x20_4008_0000 -## The file path of the kernel image. -kernel_path = "/guest/linux/phytiumpi" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_4000_0000 -#dtb_path = "/path/to/axvisor/configs/vms/linux-aarch64-e2000_smp2.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_4000_0000, 0x4000_0000, 0x7, 1], # System RAM MAP_IDENTICAL - # [0xa000_0000, 0x2000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -interrupt_mode = "passthrough" -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.dts b/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.dts deleted file mode 100644 index a37731fbd..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.dts +++ /dev/null @@ -1,397 +0,0 @@ -/dts-v1/; - -/ { - interrupt-parent = <0x8002>; - dma-coherent; - model = "linux,dummy-virt"; - #size-cells = <0x02>; - #address-cells = <0x02>; - compatible = "linux,dummy-virt"; - - psci { - migrate = <0xc4000005>; - cpu_on = <0xc4000003>; - cpu_off = <0x84000002>; - cpu_suspend = <0xc4000001>; - method = "smc"; - compatible = "arm,psci-1.0\0arm,psci-0.2\0arm,psci"; - }; - - memory@80000000 { - reg = <0x00 0x80000000 0x00 0x40000000>; - device_type = "memory"; - }; - - platform-bus@c000000 { - interrupt-parent = <0x8002>; - ranges = <0x00 0x00 0xc000000 0x2000000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - compatible = "qemu,platform\0simple-bus"; - }; - - fw-cfg@9020000 { - dma-coherent; - reg = <0x00 0x9020000 0x00 0x18>; - compatible = "qemu,fw-cfg-mmio"; - }; - - virtio_mmio@a000000 { - dma-coherent; - interrupts = <0x00 0x10 0x01>; - reg = <0x00 0xa000000 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000200 { - dma-coherent; - interrupts = <0x00 0x11 0x01>; - reg = <0x00 0xa000200 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000400 { - dma-coherent; - interrupts = <0x00 0x12 0x01>; - reg = <0x00 0xa000400 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000600 { - dma-coherent; - interrupts = <0x00 0x13 0x01>; - reg = <0x00 0xa000600 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000800 { - dma-coherent; - interrupts = <0x00 0x14 0x01>; - reg = <0x00 0xa000800 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000a00 { - dma-coherent; - interrupts = <0x00 0x15 0x01>; - reg = <0x00 0xa000a00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000c00 { - dma-coherent; - interrupts = <0x00 0x16 0x01>; - reg = <0x00 0xa000c00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a000e00 { - dma-coherent; - interrupts = <0x00 0x17 0x01>; - reg = <0x00 0xa000e00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001000 { - dma-coherent; - interrupts = <0x00 0x18 0x01>; - reg = <0x00 0xa001000 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001200 { - dma-coherent; - interrupts = <0x00 0x19 0x01>; - reg = <0x00 0xa001200 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001400 { - dma-coherent; - interrupts = <0x00 0x1a 0x01>; - reg = <0x00 0xa001400 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001600 { - dma-coherent; - interrupts = <0x00 0x1b 0x01>; - reg = <0x00 0xa001600 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001800 { - dma-coherent; - interrupts = <0x00 0x1c 0x01>; - reg = <0x00 0xa001800 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001a00 { - dma-coherent; - interrupts = <0x00 0x1d 0x01>; - reg = <0x00 0xa001a00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001c00 { - dma-coherent; - interrupts = <0x00 0x1e 0x01>; - reg = <0x00 0xa001c00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a001e00 { - dma-coherent; - interrupts = <0x00 0x1f 0x01>; - reg = <0x00 0xa001e00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002000 { - dma-coherent; - interrupts = <0x00 0x20 0x01>; - reg = <0x00 0xa002000 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002200 { - dma-coherent; - interrupts = <0x00 0x21 0x01>; - reg = <0x00 0xa002200 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002400 { - dma-coherent; - interrupts = <0x00 0x22 0x01>; - reg = <0x00 0xa002400 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002600 { - dma-coherent; - interrupts = <0x00 0x23 0x01>; - reg = <0x00 0xa002600 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002800 { - dma-coherent; - interrupts = <0x00 0x24 0x01>; - reg = <0x00 0xa002800 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002a00 { - dma-coherent; - interrupts = <0x00 0x25 0x01>; - reg = <0x00 0xa002a00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002c00 { - dma-coherent; - interrupts = <0x00 0x26 0x01>; - reg = <0x00 0xa002c00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a002e00 { - dma-coherent; - interrupts = <0x00 0x27 0x01>; - reg = <0x00 0xa002e00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003000 { - dma-coherent; - interrupts = <0x00 0x28 0x01>; - reg = <0x00 0xa003000 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003200 { - dma-coherent; - interrupts = <0x00 0x29 0x01>; - reg = <0x00 0xa003200 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003400 { - dma-coherent; - interrupts = <0x00 0x2a 0x01>; - reg = <0x00 0xa003400 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003600 { - dma-coherent; - interrupts = <0x00 0x2b 0x01>; - reg = <0x00 0xa003600 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003800 { - dma-coherent; - interrupts = <0x00 0x2c 0x01>; - reg = <0x00 0xa003800 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003a00 { - dma-coherent; - interrupts = <0x00 0x2d 0x01>; - reg = <0x00 0xa003a00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003c00 { - dma-coherent; - interrupts = <0x00 0x2e 0x01>; - reg = <0x00 0xa003c00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@a003e00 { - dma-coherent; - interrupts = <0x00 0x2f 0x01>; - reg = <0x00 0xa003e00 0x00 0x200>; - compatible = "virtio,mmio"; - }; - - gpio-keys { - compatible = "gpio-keys"; - - poweroff { - gpios = <0x8004 0x03 0x00>; - linux,code = <0x74>; - label = "GPIO Key Poweroff"; - }; - }; - - pl061@9030000 { - phandle = <0x8004>; - clock-names = "apb_pclk"; - clocks = <0x8000>; - interrupts = <0x00 0x07 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - compatible = "arm,pl061\0arm,primecell"; - reg = <0x00 0x9030000 0x00 0x1000>; - }; - - // pcie@10000000 { - // interrupt-map-mask = <0x1800 0x00 0x00 0x07>; - // interrupt-map = <0x00 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x8002 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x8002 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x8002 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x8002 0x00 0x00 0x00 0x05 0x04>; - // #interrupt-cells = <0x01>; - // ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>; - // reg = <0x40 0x10000000 0x00 0x10000000>; - // msi-map = <0x00 0x8003 0x00 0x10000>; - // dma-coherent; - // bus-range = <0x00 0xff>; - // linux,pci-domain = <0x00>; - // #size-cells = <0x02>; - // #address-cells = <0x03>; - // device_type = "pci"; - // compatible = "pci-host-ecam-generic"; - // }; - - pl031@9010000 { - clock-names = "apb_pclk"; - clocks = <0x8000>; - interrupts = <0x00 0x02 0x04>; - reg = <0x00 0x9010000 0x00 0x1000>; - compatible = "arm,pl031\0arm,primecell"; - }; - - pl011@9000000 { - clock-names = "uartclk\0apb_pclk"; - clocks = <0x8000 0x8000>; - interrupts = <0x00 0x01 0x04>; - reg = <0x00 0x9000000 0x00 0x1000>; - compatible = "arm,pl011\0arm,primecell"; - }; - - pmu { - interrupts = <0x01 0x07 0x04>; - compatible = "arm,armv8-pmuv3"; - }; - - intc@8000000 { - phandle = <0x8002>; - interrupts = <0x01 0x09 0x04>; - reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; - #redistributor-regions = <0x01>; - compatible = "arm,gic-v3"; - ranges; - #size-cells = <0x02>; - #address-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x03>; - - its@8080000 { - phandle = <0x8003>; - reg = <0x00 0x8080000 0x00 0x20000>; - #msi-cells = <0x01>; - msi-controller; - compatible = "arm,gic-v3-its"; - }; - }; - - flash@0 { - bank-width = <0x04>; - reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>; - compatible = "cfi-flash"; - }; - - cpus { - #size-cells = <0x00>; - #address-cells = <0x01>; - - cpu-map { - - socket0 { - - cluster0 { - - core0 { - cpu = <0x8001>; - }; - }; - }; - }; - - cpu@0 { - phandle = <0x8001>; - reg = <0x00>; - compatible = "arm,cortex-a72"; - device_type = "cpu"; - }; - }; - - timer { - interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>; - always-on; - compatible = "arm,armv8-timer\0arm,armv7-timer"; - }; - - apb-pclk { - phandle = <0x8000>; - clock-output-names = "clk24mhz"; - clock-frequency = <0x16e3600>; - #clock-cells = <0x00>; - compatible = "fixed-clock"; - }; - - aliases { - serial0 = "/pl011@9000000"; - }; - - chosen { - bootargs = "root=/dev/vda rw init=/init"; - stdout-path = "/pl011@9000000"; - rng-seed = <0xc63e7c7f 0x25869631 0xfa243fbb 0xe48363de 0xfd9dc3ce 0x5127dfe3 0x88de6403 0xb04a0eef>; - kaslr-seed = <0xdcf4331d 0x73a35db>; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml b/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml deleted file mode 100644 index 6e40040d5..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml +++ /dev/null @@ -1,70 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux-qemu" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu sets. -phys_cpu_ids = [0] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8020_0000 -# The location of image: "memory" | "fs". -# load from memory. -image_location = "memory" -# The file path of the kernel image. -# kernel_path = "linux-6.6.62.bin" -kernel_path = "/tmp/axvisor/qemu_aarch64_linux/qemu-aarch64" -# The load address of the kernel image. -kernel_load_addr = 0x8020_0000 -# The file path of the device tree blob (DTB). -#dtb_path = "tmp/linux-aarch64-qemu-smp1.dtb" -# The load address of the device tree blob (DTB). -dtb_load_addr = 0x8000_0000 - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] - -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [ - # ["gppt-gicd", 0x0800_0000, 0x1_0000, 0, 0x21, []], - # ["gppt-gicr", 0x080a_0000, 0x2_0000, 0, 0x20, [1, 0x2_0000, 0]], # 1 vcpu, stride 0x20000, starts with pcpu 0 - # ["gppt-gits", 0x0808_0000, 0x2_0000, 0, 0x22, [0x0808_0000]], # host_gits_base -] - -interrupt_mode = "passthrough" diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.dts b/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.dts deleted file mode 100644 index bdc546145..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.dts +++ /dev/null @@ -1,6108 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000008300000 0x000000000001c000; -/memreserve/ 0x000000000a200000 0x00000000008cf15d; -/ { - serial-number = "425ca8fc29ade692"; - compatible = "rockchip,rk3568-firefly-roc-pc-se\0rockchip,rk3568"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Firefly RK3568-ROC-PC-SE HDMI (Linux)"; - - memory { - reg = <0x00 0x80000000 0x00 0x60000000>; - device_type = "memory"; - }; - - ddr3-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x12c>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x21>; - phy_ca_drv_odten = <0x21>; - phy_clk_drv_odten = <0x21>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x21>; - phy_ca_drv_odtoff = <0x21>; - phy_clk_drv_odtoff = <0x21>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0xa7>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x14d>; - phy_odt_en_freq = <0x14d>; - phy_dq_sr_odten = <0x0f>; - phy_ca_sr_odten = <0x03>; - phy_clk_sr_odten = <0x00>; - phy_dq_sr_odtoff = <0x0f>; - phy_ca_sr_odtoff = <0x03>; - phy_clk_sr_odtoff = <0x00>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x15>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - phandle = <0xb7>; - }; - - ddr4-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x271>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x25>; - phy_ca_drv_odten = <0x25>; - phy_clk_drv_odten = <0x25>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x25>; - phy_ca_drv_odtoff = <0x25>; - phy_clk_drv_odtoff = <0x25>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0x8b>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x1f4>; - phy_odt_en_freq = <0x1f4>; - phy_dq_sr_odten = <0x0e>; - phy_ca_sr_odten = <0x01>; - phy_clk_sr_odten = <0x01>; - phy_dq_sr_odtoff = <0x0e>; - phy_ca_sr_odtoff = <0x01>; - phy_clk_sr_odtoff = <0x01>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x0c>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x22777788>; - dq_map_cs0_dq_h = <0xd7888877>; - dq_map_cs1_dq_l = <0x22777788>; - dq_map_cs1_dq_h = <0xd7888877>; - phandle = <0xb8>; - }; - - lpddr3-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x25>; - phy_ca_drv_odten = <0x25>; - phy_clk_drv_odten = <0x27>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x25>; - phy_ca_drv_odtoff = <0x25>; - phy_clk_drv_odtoff = <0x27>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0x94>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x14d>; - phy_odt_en_freq = <0x14d>; - phy_dq_sr_odten = <0x0f>; - phy_ca_sr_odten = <0x01>; - phy_clk_sr_odten = <0x0f>; - phy_dq_sr_odtoff = <0x0f>; - phy_ca_sr_odtoff = <0x01>; - phy_clk_sr_odtoff = <0x0f>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0x8d>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - phandle = <0xb9>; - }; - - lpddr4-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x618>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x1e>; - phy_ca_drv_odten = <0x26>; - phy_clk_drv_odten = <0x26>; - dram_dq_drv_odten = <0x28>; - phy_dq_drv_odtoff = <0x1e>; - phy_ca_drv_odtoff = <0x26>; - phy_clk_drv_odtoff = <0x26>; - dram_dq_drv_odtoff = <0x28>; - dram_odt = <0x50>; - phy_odt = <0x3c>; - phy_odt_puup_en = <0x00>; - phy_odt_pudn_en = <0x00>; - dram_dq_odt_en_freq = <0x320>; - phy_odt_en_freq = <0x320>; - phy_dq_sr_odten = <0x00>; - phy_ca_sr_odten = <0x0f>; - phy_clk_sr_odten = <0x0f>; - phy_dq_sr_odtoff = <0x00>; - phy_ca_sr_odtoff = <0x0f>; - phy_clk_sr_odtoff = <0x0f>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - lp4_ca_odt = <0x78>; - lp4_drv_pu_cal_odten = <0x01>; - lp4_drv_pu_cal_odtoff = <0x01>; - phy_lp4_drv_pulldown_en_odten = <0x00>; - phy_lp4_drv_pulldown_en_odtoff = <0x00>; - lp4_ca_odt_en_freq = <0x320>; - phy_lp4_cs_drv_odten = <0x00>; - phy_lp4_cs_drv_odtoff = <0x00>; - lp4_odte_ck_en = <0x01>; - lp4_odte_cs_en = <0x01>; - lp4_odtd_ca_en = <0x00>; - phy_lp4_dq_vref_odten = <0xa6>; - lp4_dq_vref_odten = <0x12c>; - lp4_ca_vref_odten = <0x17c>; - phy_lp4_dq_vref_odtoff = <0x1a4>; - lp4_dq_vref_odtoff = <0x1a4>; - lp4_ca_vref_odtoff = <0x1a4>; - phandle = <0xba>; - }; - - lpddr4x-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x618>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x1d>; - phy_ca_drv_odten = <0x24>; - phy_clk_drv_odten = <0x24>; - dram_dq_drv_odten = <0x28>; - phy_dq_drv_odtoff = <0x1d>; - phy_ca_drv_odtoff = <0x24>; - phy_clk_drv_odtoff = <0x24>; - dram_dq_drv_odtoff = <0x28>; - dram_odt = <0x50>; - phy_odt = <0x3c>; - phy_odt_puup_en = <0x00>; - phy_odt_pudn_en = <0x00>; - dram_dq_odt_en_freq = <0x320>; - phy_odt_en_freq = <0x320>; - phy_dq_sr_odten = <0x00>; - phy_ca_sr_odten = <0x00>; - phy_clk_sr_odten = <0x00>; - phy_dq_sr_odtoff = <0x00>; - phy_ca_sr_odtoff = <0x00>; - phy_clk_sr_odtoff = <0x00>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - lp4_ca_odt = <0x78>; - lp4_drv_pu_cal_odten = <0x00>; - lp4_drv_pu_cal_odtoff = <0x00>; - phy_lp4_drv_pulldown_en_odten = <0x00>; - phy_lp4_drv_pulldown_en_odtoff = <0x00>; - lp4_ca_odt_en_freq = <0x320>; - phy_lp4_cs_drv_odten = <0x00>; - phy_lp4_cs_drv_odtoff = <0x00>; - lp4_odte_ck_en = <0x00>; - lp4_odte_cs_en = <0x00>; - lp4_odtd_ca_en = <0x00>; - phy_lp4_dq_vref_odten = <0xa6>; - lp4_dq_vref_odten = <0xe4>; - lp4_ca_vref_odten = <0x157>; - phy_lp4_dq_vref_odtoff = <0x1a4>; - lp4_dq_vref_odtoff = <0x1a4>; - lp4_ca_vref_odtoff = <0x157>; - phandle = <0xbb>; - }; - - aliases { - csi2dphy0 = "/csi2-dphy0"; - csi2dphy1 = "/csi2-dphy1"; - csi2dphy2 = "/csi2-dphy2"; - dsi0 = "/dsi@fe060000"; - dsi1 = "/dsi@fe070000"; - ethernet0 = "/ethernet@fe2a0000"; - ethernet1 = "/ethernet@fe010000"; - gpio0 = "/pinctrl/gpio0@fdd60000"; - gpio1 = "/pinctrl/gpio1@fe740000"; - gpio2 = "/pinctrl/gpio2@fe750000"; - gpio3 = "/pinctrl/gpio3@fe760000"; - gpio4 = "/pinctrl/gpio4@fe770000"; - i2c0 = "/i2c@fdd40000"; - i2c1 = "/i2c@fe5a0000"; - i2c2 = "/i2c@fe5b0000"; - i2c3 = "/i2c@fe5c0000"; - i2c4 = "/i2c@fe5d0000"; - i2c5 = "/i2c@fe5e0000"; - mmc0 = "/sdhci@fe310000"; - mmc1 = "/dwmmc@fe2b0000"; - mmc2 = "/dwmmc@fe2c0000"; - mmc3 = "/dwmmc@fe000000"; - serial0 = "/serial@fdd50000"; - serial1 = "/serial@fe650000"; - serial2 = "/serial@fe660000"; - serial3 = "/serial@fe670000"; - serial4 = "/serial@fe680000"; - serial5 = "/serial@fe690000"; - serial6 = "/serial@fe6a0000"; - serial7 = "/serial@fe6b0000"; - serial8 = "/serial@fe6c0000"; - serial9 = "/serial@fe6d0000"; - spi0 = "/spi@fe610000"; - spi1 = "/spi@fe620000"; - spi2 = "/spi@fe630000"; - spi3 = "/spi@fe640000"; - lvds0 = "/syscon@fdc60000/lvds"; - lvds1 = "/syscon@fdc60000/lvds1"; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x00>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - #cooling-cells = <0x02>; - dynamic-power-coefficient = <0xbb>; - cpu-supply = <0x05>; - phandle = <0x0c>; - }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x00 0x100>; - // enable-method = "psci"; - // clocks = <0x02 0x00>; - // operating-points-v2 = <0x03>; - // cpu-idle-states = <0x04>; - // phandle = <0x0d>; - // }; - - // cpu@200 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x00 0x200>; - // enable-method = "psci"; - // clocks = <0x02 0x00>; - // operating-points-v2 = <0x03>; - // cpu-idle-states = <0x04>; - // phandle = <0x0e>; - // }; - - // cpu@300 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x00 0x300>; - // enable-method = "psci"; - // clocks = <0x02 0x00>; - // operating-points-v2 = <0x03>; - // cpu-idle-states = <0x04>; - // phandle = <0x0f>; - // }; - - idle-states { - entry-method = "psci"; - - cpu-sleep { - compatible = "arm,idle-state"; - local-timer-stop; - arm,psci-suspend-param = <0x10000>; - entry-latency-us = <0x64>; - exit-latency-us = <0x78>; - min-residency-us = <0x3e8>; - phandle = <0x04>; - }; - }; - }; - - cpu0-opp-table { - compatible = "operating-points-v2"; - opp-shared; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x06 0x07 0x08 0x09 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0x118c30>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-freq = <0x639c0>; - rockchip,pvtm-volt = <0xdbba0>; - rockchip,pvtm-ch = <0x00 0x05>; - rockchip,pvtm-sample-time = <0x3e8>; - rockchip,pvtm-number = <0x0a>; - rockchip,pvtm-error = <0x3e8>; - rockchip,pvtm-ref-temp = <0x28>; - rockchip,pvtm-temp-prop = <0x1a 0x1a>; - rockchip,thermal-zone = "soc-thermal"; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x7c8 0x124f8>; - phandle = <0x03>; - - opp-408000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x18519600>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-816000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x30a32c00>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - opp-suspend; - }; - - opp-1104000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x41cdb400>; - opp-microvolt = <0xdbba0 0xdbba0 0x118c30>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0x118c30>; - opp-microvolt-L1 = <0xcf850 0xcf850 0x118c30>; - opp-microvolt-L2 = <0xcf850 0xcf850 0x118c30>; - opp-microvolt-L3 = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1416000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x54667200>; - opp-microvolt = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L0 = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L1 = <0xee098 0xee098 0x118c30>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0x118c30>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1608000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x5fd82200>; - opp-microvolt = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L0 = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L1 = <0x100590 0x100590 0x118c30>; - opp-microvolt-L2 = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L3 = <0xf4240 0xf4240 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1800000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x6b49d200>; - opp-microvolt = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L0 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L1 = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L2 = <0x106738 0x106738 0x118c30>; - opp-microvolt-L3 = <0x100590 0x100590 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1992000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x76bb8200>; - opp-microvolt = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L0 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L1 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L2 = <0x112a88 0x112a88 0x118c30>; - opp-microvolt-L3 = <0x10c8e0 0x10c8e0 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-j-1008000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x3c14dc00>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-j-1416000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x54667200>; - opp-microvolt = <0xdbba0 0xdbba0 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-m-1608000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x5fd82200>; - opp-microvolt = <0xf4240 0xf4240 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - }; - - arm-pmu { - compatible = "arm,cortex-a55-pmu\0arm,armv8-pmuv3"; - interrupts = <0x00 0xe4 0x04 0x00 0xe5 0x04 0x00 0xe6 0x04 0x00 0xe7 0x04>; - interrupt-affinity = <0x0c 0x0d 0x0e 0x0f>; - }; - - cpuinfo { - compatible = "rockchip,cpuinfo"; - nvmem-cells = <0x10 0x11 0x12>; - nvmem-cell-names = "id\0cpu-version\0cpu-code"; - }; - - display-subsystem { - compatible = "rockchip,display-subsystem"; - memory-region = <0x13 0x14>; - memory-region-names = "drm-logo\0drm-cubic-lut"; - ports = <0x15>; - devfreq = <0x16>; - - route { - - route-dsi0 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x17>; - }; - - route-dsi1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x18>; - }; - - route-edp { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x19>; - }; - - route-hdmi { - status = "okay"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1a>; - }; - - route-lvds { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1b>; - }; - - route-rgb { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1c>; - }; - }; - }; - - edac { - compatible = "rockchip,rk3568-edac"; - interrupts = <0x00 0xad 0x04 0x00 0xaf 0x04>; - interrupt-names = "ce\0ue"; - status = "disabled"; - }; - - firmware { - - scmi { - compatible = "arm,scmi-smc"; - shmem = <0x1d>; - arm,smc-id = <0x82000010>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@14 { - reg = <0x14>; - #clock-cells = <0x01>; - rockchip,clk-init = <0x41cdb400>; - phandle = <0x02>; - }; - }; - - sdei { - compatible = "arm,sdei-1.0"; - method = "smc"; - }; - }; - - mipi-csi2 { - compatible = "rockchip,rk3568-mipi-csi2"; - rockchip,hw = <0x1e>; - status = "disabled"; - }; - - mpp-srv { - compatible = "rockchip,mpp-service"; - rockchip,taskqueue-count = <0x06>; - rockchip,resetgroup-count = <0x06>; - status = "okay"; - phandle = <0x7b>; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - }; - - reserved-memory { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - drm-logo@00000000 { - compatible = "rockchip,drm-logo"; - reg = <0x00 0xedf00000 0x00 0x2e0000>; - phandle = <0x13>; - }; - - drm-cubic-lut@00000000 { - compatible = "rockchip,drm-cubic-lut"; - reg = <0x00 0xeff00000 0x00 0x8000>; - phandle = <0x14>; - }; - - ramoops@110000 { - compatible = "ramoops"; - reg = <0x00 0x110000 0x00 0xf0000>; - record-size = <0x20000>; - console-size = <0x80000>; - ftrace-size = <0x00>; - pmsg-size = <0x50000>; - }; - }; - - rockchip-suspend { - compatible = "rockchip,pm-rk3568"; - status = "okay"; - rockchip,sleep-debug-en = <0x01>; - rockchip,sleep-mode-config = <0x5ec>; - rockchip,wakeup-config = <0x10>; - }; - - rockchip-system-monitor { - compatible = "rockchip,system-monitor"; - rockchip,thermal-zone = "soc-thermal"; - }; - - thermal-zones { - - soc-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - sustainable-power = <0x389>; - thermal-sensors = <0x1f 0x00>; - - trips { - - trip-point-0 { - temperature = <0x124f8>; - hysteresis = <0x7d0>; - type = "passive"; - }; - - trip-point-1 { - temperature = <0x14c08>; - hysteresis = <0x7d0>; - type = "passive"; - phandle = <0x20>; - }; - - soc-crit { - temperature = <0x1c138>; - hysteresis = <0x7d0>; - type = "critical"; - }; - }; - - cooling-maps { - - map0 { - trip = <0x20>; - cooling-device = <0x0c 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - - map1 { - trip = <0x20>; - cooling-device = <0x21 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - }; - }; - - gpu-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x1f 0x01>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; - arm,no-tick-in-suspend; - }; - - external-gmac0-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "gmac0_clkin"; - #clock-cells = <0x00>; - phandle = <0xc7>; - }; - - external-gmac1-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "gmac1_clkin"; - #clock-cells = <0x00>; - phandle = <0x92>; - }; - - xpcs-gmac0-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "clk_gmac0_xpcs_mii"; - #clock-cells = <0x00>; - }; - - xpcs-gmac1-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "clk_gmac1_xpcs_mii"; - #clock-cells = <0x00>; - }; - - i2s1-mclkin-rx { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s1_mclkin_rx"; - }; - - i2s1-mclkin-tx { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s1_mclkin_tx"; - }; - - i2s2-mclkin { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s2_mclkin"; - }; - - i2s3-mclkin { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s3_mclkin"; - }; - - mpll { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2faf0800>; - clock-output-names = "mpll"; - }; - - xin24m { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x16e3600>; - clock-output-names = "xin24m"; - }; - - xin32k { - compatible = "fixed-clock"; - clock-frequency = <0x8000>; - clock-output-names = "xin32k"; - #clock-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x22>; - }; - - scmi-shmem@10f000 { - compatible = "arm,scmi-shmem"; - reg = <0x00 0x10f000 0x00 0x100>; - phandle = <0x1d>; - }; - - sata@fc000000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc000000 0x00 0x1000>; - clocks = <0x23 0x96 0x23 0x97 0x23 0x98>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x5e 0x04>; - interrupt-names = "hostc"; - phys = <0x24 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "disabled"; - }; - - sata@fc400000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc400000 0x00 0x1000>; - clocks = <0x23 0x9b 0x23 0x9c 0x23 0x9d>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x5f 0x04>; - interrupt-names = "hostc"; - phys = <0x26 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "disabled"; - }; - - sata@fc800000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc800000 0x00 0x1000>; - clocks = <0x23 0xa0 0x23 0xa1 0x23 0xa2>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x60 0x04>; - interrupt-names = "hostc"; - phys = <0x27 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "okay"; - }; - - usbdrd { - compatible = "rockchip,rk3568-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x23 0xa6 0x23 0xa7 0x23 0xa5 0x23 0x7f>; - clock-names = "ref_clk\0suspend_clk\0bus_clk\0pipe_clk"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - dwc3@fcc00000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfcc00000 0x00 0x400000>; - interrupts = <0x00 0xa9 0x04>; - dr_mode = "otg"; - phys = <0x28 0x24 0x04>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - power-domains = <0x25 0x0f>; - resets = <0x23 0x94>; - reset-names = "usb3-otg"; - snps,dis_enblslpm_quirk; - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,dis_rxdet_inp3_quirk; - snps,parkmode-disable-hs-quirk; - snps,parkmode-disable-ss-quirk; - quirk-skip-phy-init; - status = "okay"; - extcon = <0x29>; - usb-role-switch; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x2a>; - phandle = <0x4d>; - }; - }; - }; - }; - - usbhost { - compatible = "rockchip,rk3568-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x23 0xa9 0x23 0xaa 0x23 0xa8 0x23 0x7f>; - clock-names = "ref_clk\0suspend_clk\0bus_clk\0pipe_clk"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - dwc3@fd000000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfd000000 0x00 0x400000>; - interrupts = <0x00 0xaa 0x04>; - dr_mode = "host"; - phys = <0x2b 0x26 0x04>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - power-domains = <0x25 0x0f>; - resets = <0x23 0x95>; - reset-names = "usb3-host"; - snps,dis_enblslpm_quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,dis_rxdet_inp3_quirk; - snps,parkmode-disable-hs-quirk; - snps,parkmode-disable-ss-quirk; - status = "okay"; - }; - }; - - interrupt-controller@fd400000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0xfd400000 0x00 0x10000 0x00 0xfd460000 0x00 0xc0000>; - interrupts = <0x01 0x09 0x04>; - phandle = <0x01>; - - interrupt-controller@fd440000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <0x01>; - reg = <0x00 0xfd440000 0x00 0x20000>; - status = "okay"; - phandle = <0xbe>; - }; - }; - - usb@fd800000 { - compatible = "generic-ehci"; - reg = <0x00 0xfd800000 0x00 0x40000>; - interrupts = <0x00 0x82 0x04>; - clocks = <0x23 0xbd 0x23 0xbe 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2d>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd840000 { - compatible = "generic-ohci"; - reg = <0x00 0xfd840000 0x00 0x40000>; - interrupts = <0x00 0x83 0x04>; - clocks = <0x23 0xbd 0x23 0xbe 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2d>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd880000 { - compatible = "generic-ehci"; - reg = <0x00 0xfd880000 0x00 0x40000>; - interrupts = <0x00 0x85 0x04>; - clocks = <0x23 0xbf 0x23 0xc0 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2e>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd8c0000 { - compatible = "generic-ohci"; - reg = <0x00 0xfd8c0000 0x00 0x40000>; - interrupts = <0x00 0x86 0x04>; - clocks = <0x23 0xbf 0x23 0xc0 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2e>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - syscon@fda00000 { - compatible = "rockchip,rk3568-xpcs\0syscon"; - reg = <0x00 0xfda00000 0x00 0x200000>; - status = "disabled"; - }; - - syscon@fdc20000 { - compatible = "rockchip,rk3568-pmugrf\0syscon\0simple-mfd"; - reg = <0x00 0xfdc20000 0x00 0x10000>; - phandle = <0x3c>; - - io-domains { - compatible = "rockchip,rk3568-pmu-io-voltage-domain"; - status = "okay"; - pmuio1-supply = <0x2f>; - pmuio2-supply = <0x2f>; - vccio1-supply = <0x30>; - vccio3-supply = <0x31>; - vccio4-supply = <0x32>; - vccio5-supply = <0x33>; - vccio6-supply = <0x32>; - vccio7-supply = <0x33>; - }; - - reboot-mode { - compatible = "syscon-reboot-mode"; - offset = <0x200>; - mode-bootloader = <0x5242c301>; - mode-charge = <0x5242c30b>; - mode-fastboot = <0x5242c309>; - mode-loader = <0x5242c301>; - mode-normal = <0x5242c300>; - mode-recovery = <0x5242c303>; - mode-ums = <0x5242c30c>; - mode-panic = <0x5242c307>; - mode-watchdog = <0x5242c308>; - }; - }; - - syscon@fdc50000 { - compatible = "rockchip,rk3568-pipegrf\0syscon"; - reg = <0x00 0xfdc50000 0x00 0x1000>; - phandle = <0x12a>; - }; - - syscon@fdc60000 { - compatible = "rockchip,rk3568-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfdc60000 0x00 0x10000>; - phandle = <0x3b>; - - io-domains { - compatible = "rockchip,rk3568-io-voltage-domain"; - status = "disabled"; - }; - - lvds { - compatible = "rockchip,rk3568-lvds"; - phys = <0x34>; - phy-names = "phy"; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x1b>; - status = "disabled"; - phandle = <0xa3>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x35>; - status = "disabled"; - phandle = <0xa5>; - }; - }; - }; - }; - - lvds1 { - compatible = "rockchip,rk3568-lvds"; - phys = <0x36>; - phy-names = "phy"; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x37>; - phandle = <0xa4>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x38>; - phandle = <0xa7>; - }; - }; - }; - }; - - rgb { - compatible = "rockchip,rk3568-rgb"; - pinctrl-names = "default"; - pinctrl-0 = <0x39>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x1c>; - status = "disabled"; - phandle = <0xa6>; - }; - }; - }; - }; - }; - - syscon@fdc70000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc70000 0x00 0x1000>; - phandle = <0x12b>; - }; - - syscon@fdc80000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc80000 0x00 0x1000>; - phandle = <0x12c>; - }; - - syscon@fdc90000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc90000 0x00 0x1000>; - phandle = <0x12d>; - }; - - syscon@fdca0000 { - compatible = "rockchip,rk3568-usb2phy-grf\0syscon"; - reg = <0x00 0xfdca0000 0x00 0x8000>; - phandle = <0x135>; - }; - - syscon@fdca8000 { - compatible = "rockchip,rk3568-usb2phy-grf\0syscon"; - reg = <0x00 0xfdca8000 0x00 0x8000>; - phandle = <0x137>; - }; - - syscon@fdcb0000 { - compatible = "rockchip,rk3568-edp-phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfdcb0000 0x00 0x100>; - clocks = <0x23 0x192>; - - edp-phy { - compatible = "rockchip,rk3568-edp-phy"; - clocks = <0x3a 0x29>; - clock-names = "refclk"; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0xae>; - }; - }; - - syscon@fdcb8000 { - compatible = "rockchip,pcie30-phy-grf\0syscon"; - reg = <0x00 0xfdcb8000 0x00 0x10000>; - phandle = <0x138>; - }; - - sram@fdcc0000 { - compatible = "mmio-sram"; - reg = <0x00 0xfdcc0000 0x00 0xb000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0xfdcc0000 0xb000>; - - rkvdec-sram@0 { - reg = <0x00 0xb000>; - phandle = <0x84>; - }; - }; - - clock-controller@fdd00000 { - compatible = "rockchip,rk3568-pmucru"; - reg = <0x00 0xfdd00000 0x00 0x1000>; - rockchip,grf = <0x3b>; - rockchip,pmugrf = <0x3c>; - #clock-cells = <0x01>; - #reset-cells = <0x01>; - assigned-clocks = <0x3a 0x32>; - assigned-clock-parents = <0x3a 0x05>; - phandle = <0x3a>; - }; - - clock-controller@fdd20000 { - compatible = "rockchip,rk3568-cru"; - reg = <0x00 0xfdd20000 0x00 0x1000>; - rockchip,grf = <0x3b>; - #clock-cells = <0x01>; - #reset-cells = <0x01>; - assigned-clocks = <0x3a 0x05 0x23 0x106 0x23 0x10b 0x3a 0x01 0x3a 0x2b 0x23 0x03 0x23 0x19b 0x23 0x09 0x23 0x19c 0x23 0x19d 0x23 0x1a1 0x23 0x19e 0x23 0x19f 0x23 0x1a0 0x23 0x04 0x23 0x10d 0x23 0x10e 0x23 0x173 0x23 0x174 0x23 0x175 0x23 0x176 0x23 0xc9 0x23 0xca 0x23 0x06 0x23 0x7e 0x23 0x7f 0x23 0x3d 0x23 0x41 0x23 0x45 0x23 0x49 0x23 0x4d 0x23 0x4d 0x23 0x55 0x23 0x51 0x23 0x5d 0x23 0xdd>; - assigned-clock-rates = <0x8000 0x11e1a300 0x11e1a300 0xbebc200 0x5f5e100 0x3b9aca00 0x1dcd6500 0x13d92d40 0xee6b280 0x7735940 0x5f5e100 0x3b9aca0 0x2faf080 0x17d7840 0x46cf7100 0x8f0d180 0x5f5e100 0x1dcd6500 0x17d78400 0x8f0d180 0x5f5e100 0x11e1a300 0x8f0d180 0x47868c00 0x17d78400 0x5f5e100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x1dcd6500>; - assigned-clock-parents = <0x3a 0x08 0x23 0x04 0x23 0x04>; - phandle = <0x23>; - }; - - i2c@fdd40000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfdd40000 0x00 0x1000>; - clocks = <0x3a 0x07 0x3a 0x2d>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x2e 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x3d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - tcs4525@1c { - compatible = "tcs,tcs452x"; - reg = <0x1c>; - vin-supply = <0x3e>; - regulator-compatible = "fan53555-reg"; - regulator-name = "vdd_cpu"; - regulator-min-microvolt = <0xadf34>; - regulator-max-microvolt = <0x1535b0>; - regulator-ramp-delay = <0x8fc>; - fcs,suspend-voltage-selector = <0x01>; - regulator-boot-on; - regulator-always-on; - phandle = <0x05>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - pmic@20 { - compatible = "rockchip,rk809"; - reg = <0x20>; - interrupt-parent = <0x3f>; - interrupts = <0x03 0x08>; - pinctrl-names = "default\0pmic-sleep\0pmic-power-off\0pmic-reset"; - pinctrl-0 = <0x40>; - pinctrl-1 = <0x41 0x42>; - pinctrl-2 = <0x43 0x44>; - pinctrl-3 = <0x43 0x45>; - rockchip,system-power-controller; - wakeup-source; - #clock-cells = <0x01>; - clock-output-names = "rk808-clkout1\0rk808-clkout2"; - pmic-reset-func = <0x00>; - not-save-power-en = <0x01>; - vcc1-supply = <0x46>; - vcc2-supply = <0x46>; - vcc3-supply = <0x46>; - vcc4-supply = <0x46>; - vcc5-supply = <0x46>; - vcc6-supply = <0x46>; - vcc7-supply = <0x46>; - vcc8-supply = <0x46>; - vcc9-supply = <0x46>; - phandle = <0x152>; - - pwrkey { - status = "okay"; - }; - - pinctrl_rk8xx { - gpio-controller; - #gpio-cells = <0x02>; - - rk817_slppin_null { - pins = "gpio_slp"; - function = "pin_fun0"; - }; - - rk817_slppin_slp { - pins = "gpio_slp"; - function = "pin_fun1"; - phandle = <0x42>; - }; - - rk817_slppin_pwrdn { - pins = "gpio_slp"; - function = "pin_fun2"; - phandle = <0x44>; - }; - - rk817_slppin_rst { - pins = "gpio_slp"; - function = "pin_fun3"; - phandle = <0x45>; - }; - }; - - regulators { - - DCDC_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_logic"; - phandle = <0x75>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_gpu"; - phandle = <0x77>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-initial-mode = <0x02>; - regulator-name = "vcc_ddr"; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - DCDC_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_npu"; - phandle = <0x71>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG1 { - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda0v9_image"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda_0v9"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda0v9_pmu"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0xdbba0>; - }; - }; - - LDO_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vccio_acodec"; - phandle = <0x30>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vccio_sd"; - phandle = <0x31>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG6 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vcc3v3_pmu"; - phandle = <0x2f>; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x325aa0>; - }; - }; - - LDO_REG7 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca_1v8"; - phandle = <0x129>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG8 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca1v8_pmu"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x1b7740>; - }; - }; - - LDO_REG9 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca1v8_image"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcc_1v8"; - phandle = <0x32>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - SWITCH_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vcc_3v3"; - phandle = <0x33>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - SWITCH_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vcc3v3_sd"; - phandle = <0xcf>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - - codec { - #sound-dai-cells = <0x00>; - compatible = "rockchip,rk809-codec\0rockchip,rk817-codec"; - clocks = <0x23 0x1a3>; - clock-names = "mclk"; - assigned-clocks = <0x23 0x1a3 0x23 0x1a6>; - assigned-clock-rates = <0xbb8000>; - assigned-clock-parents = <0x23 0x48 0x23 0x48>; - pinctrl-names = "default\0spk_gpio"; - pinctrl-0 = <0x47>; - pinctrl-1 = <0x48>; - hp-volume = <0x03>; - spk-volume = <0x03>; - mic-in-differential; - board-spk-from-hp; - capture-volume = <0x00>; - io-channels = <0x49 0x07>; - hp-det-adc-value = <0x3e8>; - status = "okay"; - hp-adc-drift-scope = <0x64>; - phandle = <0x14b>; - }; - - rtc { - status = "disabled"; - }; - }; - - fusb302@22 { - compatible = "fcs,fusb302"; - reg = <0x22>; - interrupt-parent = <0x3f>; - fcs,int_n = <0x3f 0x11 0x08>; - fusb340-switch-gpios = <0x4a 0x12 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4b>; - vbus-supply = <0x4c>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x4d>; - phandle = <0x2a>; - }; - }; - }; - - connector { - compatible = "usb-c-connector"; - label = "USB-C"; - data-role = "dual"; - power-role = "dual"; - try-power-role = "sink"; - op-sink-microwatt = <0xf4240>; - sink-pdos = <0x40190fa>; - source-pdos = <0x4019096>; - }; - }; - }; - - serial@fdd50000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfdd50000 0x00 0x100>; - interrupts = <0x00 0x74 0x04>; - clocks = <0x3a 0x0b 0x3a 0x2c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x00 0x4e 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x4f>; - status = "disabled"; - }; - - pwm@fdd70000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70000 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x50>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70010 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x51>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70020 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x52>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70030 0x00 0x10>; - interrupts = <0x00 0x52 0x04 0x00 0x56 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x53>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - power-management@fdd90000 { - compatible = "rockchip,rk3568-pmu\0syscon\0simple-mfd"; - reg = <0x00 0xfdd90000 0x00 0x1000>; - - power-controller { - compatible = "rockchip,rk3568-power-controller"; - #power-domain-cells = <0x01>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x25>; - - pd_npu@6 { - reg = <0x06>; - clocks = <0x23 0x27 0x23 0x25 0x23 0x26>; - pm_qos = <0x54>; - }; - - pd_gpu@7 { - reg = <0x07>; - clocks = <0x23 0x19 0x23 0x1a>; - pm_qos = <0x55>; - }; - - pd_vi@8 { - reg = <0x08>; - clocks = <0x23 0xcc 0x23 0xcd>; - pm_qos = <0x56 0x57 0x58>; - }; - - pd_vo@9 { - reg = <0x09>; - clocks = <0x23 0xda 0x23 0xdb 0x23 0xdc>; - pm_qos = <0x59 0x5a 0x5b>; - }; - - pd_rga@10 { - reg = <0x0a>; - clocks = <0x23 0xf1 0x23 0xf2>; - pm_qos = <0x5c 0x5d 0x5e 0x5f 0x60 0x61>; - }; - - pd_vpu@11 { - reg = <0x0b>; - clocks = <0x23 0xed>; - pm_qos = <0x62>; - }; - - pd_rkvdec@13 { - clocks = <0x23 0x107>; - reg = <0x0d>; - pm_qos = <0x63>; - }; - - pd_rkvenc@14 { - reg = <0x0e>; - clocks = <0x23 0x102>; - pm_qos = <0x64 0x65 0x66>; - }; - - pd_pipe@15 { - reg = <0x0f>; - clocks = <0x23 0x7f>; - pm_qos = <0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e>; - }; - }; - }; - - pvtm@fde00000 { - compatible = "rockchip,rk3568-core-pvtm"; - reg = <0x00 0xfde00000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@0 { - reg = <0x00>; - clocks = <0x23 0x13 0x23 0x1c2>; - clock-names = "clk\0pclk"; - resets = <0x23 0x1a 0x23 0x19>; - reset-names = "rts\0rst-p"; - thermal-zone = "soc-thermal"; - }; - }; - - npu@fde40000 { - compatible = "rockchip,rk3568-rknpu\0rockchip,rknpu"; - reg = <0x00 0xfde40000 0x00 0x10000>; - interrupts = <0x00 0x97 0x04>; - clocks = <0x02 0x02 0x23 0x23 0x23 0x28 0x23 0x29>; - clock-names = "scmi_clk\0clk\0aclk\0hclk"; - assigned-clocks = <0x23 0x23>; - assigned-clock-rates = <0x23c34600>; - resets = <0x23 0x2b 0x23 0x2c>; - reset-names = "srst_a\0srst_h"; - power-domains = <0x25 0x06>; - operating-points-v2 = <0x6f>; - iommus = <0x70>; - status = "okay"; - rknpu-supply = <0x71>; - }; - - npu-opp-table { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x72 0x07 0x08 0x73 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x3e8 0xc350>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x6f>; - - opp-200000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0xbebc200>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-300000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-400000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-700000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L0 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L1 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L2 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L3 = <0xcf850 0xcf850 0xf4240>; - }; - - opp-800000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L0 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L1 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L2 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L3 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-900000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xee098 0xee098 0xf4240>; - opp-microvolt-L0 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L1 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L2 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L3 = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-1000000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L0 = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L1 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0xf4240>; - status = "disabled"; - }; - - opp-j-600000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-m-900000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xe1d48 0xe1d48 0xf4240>; - }; - }; - - bus-npu { - compatible = "rockchip,rk3568-bus"; - rockchip,busfreq-policy = "clkfreq"; - clocks = <0x02 0x02>; - clock-names = "bus"; - operating-points-v2 = <0x74>; - status = "okay"; - bus-supply = <0x75>; - pvtm-supply = <0x05>; - }; - - bus-npu-opp-table { - compatible = "operating-points-v2"; - opp-shared; - nvmem-cells = <0x07>; - nvmem-cell-names = "pvtm"; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x16378 0x01 0x16379 0x186a0 0x02>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x74>; - - opp-700000000 { - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - opp-microvolt-L2 = <0xd59f8>; - }; - - opp-900000000 { - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xdbba0>; - }; - - opp-1000000000 { - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xe7ef0>; - opp-microvolt-L0 = <0xe7ef0>; - opp-microvolt-L1 = <0xe1d48>; - opp-microvolt-L2 = <0xdbba0>; - }; - }; - - iommu@fde4b000 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfde4b000 0x00 0x40>; - interrupts = <0x00 0x97 0x04>; - interrupt-names = "rknpu_mmu"; - clocks = <0x23 0x28 0x23 0x29>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x06>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x70>; - }; - - gpu@fde60000 { - compatible = "arm,mali-bifrost"; - reg = <0x00 0xfde60000 0x00 0x4000>; - interrupts = <0x00 0x27 0x04 0x00 0x29 0x04 0x00 0x28 0x04>; - interrupt-names = "GPU\0MMU\0JOB"; - upthreshold = <0x28>; - downdifferential = <0x0a>; - clocks = <0x02 0x01 0x23 0x1b>; - clock-names = "clk_mali\0clk_gpu"; - power-domains = <0x25 0x07>; - #cooling-cells = <0x02>; - operating-points-v2 = <0x76>; - status = "okay"; - mali-supply = <0x77>; - phandle = <0x21>; - - power-model { - compatible = "simple-power-model"; - leakage-range = <0x05 0x0f>; - ls = <0xffffa23e 0x5927 0x00>; - static-coefficient = <0x186a0>; - dynamic-coefficient = <0x3b9>; - ts = <0xfffe56a6 0xf87a 0xfffffab5 0x14>; - thermal-zone = "gpu-thermal"; - }; - }; - - opp-table2 { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x78 0x07 0x08 0x79 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x320 0xc350>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x76>; - - opp-200000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0xbebc200>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-300000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x11e1a300>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-400000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L1 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L2 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L3 = <0xcf850 0xcf850 0xf4240>; - }; - - opp-700000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L0 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L1 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L2 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L3 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-800000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L0 = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L1 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0xf4240>; - }; - - opp-j-600000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-m-800000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xe7ef0 0xe7ef0 0xf4240>; - }; - }; - - pvtm@fde80000 { - compatible = "rockchip,rk3568-gpu-pvtm"; - reg = <0x00 0xfde80000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@1 { - reg = <0x01>; - clocks = <0x23 0x1e 0x23 0x1d>; - clock-names = "clk\0pclk"; - resets = <0x23 0x24 0x23 0x23>; - reset-names = "rts\0rst-p"; - thermal-zone = "gpu-thermal"; - }; - }; - - pvtm@fde90000 { - compatible = "rockchip,rk3568-npu-pvtm"; - reg = <0x00 0xfde90000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@2 { - reg = <0x02>; - clocks = <0x23 0x2b 0x23 0x2a 0x23 0x25>; - clock-names = "clk\0pclk\0hclk"; - resets = <0x23 0x2e 0x23 0x2d>; - reset-names = "rts\0rst-p"; - thermal-zone = "soc-thermal"; - }; - }; - - vdpu@fdea0400 { - compatible = "rockchip,vpu-decoder-v2"; - reg = <0x00 0xfdea0400 0x00 0x400>; - interrupts = <0x00 0x8b 0x04>; - interrupt-names = "irq_dec"; - clocks = <0x23 0xee 0x23 0xef>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - resets = <0x23 0x11a 0x23 0x11b>; - reset-names = "video_a\0video_h"; - iommus = <0x7a>; - power-domains = <0x25 0x0b>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x00>; - rockchip,resetgroup-node = <0x00>; - status = "okay"; - }; - - iommu@fdea0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdea0800 0x00 0x40>; - interrupts = <0x00 0x8a 0x04>; - interrupt-names = "vdpu_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xee 0x23 0xef>; - power-domains = <0x25 0x0b>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7a>; - }; - - rk_rga@fdeb0000 { - compatible = "rockchip,rga2"; - reg = <0x00 0xfdeb0000 0x00 0x1000>; - interrupts = <0x00 0x5a 0x04>; - clocks = <0x23 0xf3 0x23 0xf4 0x23 0xf5>; - clock-names = "aclk_rga\0hclk_rga\0clk_rga"; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - ebc@fdec0000 { - compatible = "rockchip,rk3568-ebc-tcon"; - reg = <0x00 0xfdec0000 0x00 0x5000>; - interrupts = <0x00 0x11 0x04>; - clocks = <0x23 0xf9 0x23 0xfa>; - clock-names = "hclk\0dclk"; - power-domains = <0x25 0x0a>; - rockchip,grf = <0x3b>; - pinctrl-names = "default"; - pinctrl-0 = <0x7c>; - status = "disabled"; - }; - - jpegd@fded0000 { - compatible = "rockchip,rkv-jpeg-decoder-v1"; - reg = <0x00 0xfded0000 0x00 0x400>; - interrupts = <0x00 0x3e 0x04>; - clocks = <0x23 0xfb 0x23 0xfc>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,disable-auto-freq; - resets = <0x23 0x12c 0x23 0x12d>; - reset-names = "video_a\0video_h"; - iommus = <0x7d>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x01>; - rockchip,resetgroup-node = <0x01>; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - iommu@fded0480 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfded0480 0x00 0x40>; - interrupts = <0x00 0x3d 0x04>; - interrupt-names = "jpegd_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xfb 0x23 0xfc>; - power-domains = <0x25 0x0a>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7d>; - }; - - vepu@fdee0000 { - compatible = "rockchip,vpu-encoder-v2"; - reg = <0x00 0xfdee0000 0x00 0x400>; - interrupts = <0x00 0x40 0x04>; - clocks = <0x23 0xfd 0x23 0xfe>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,disable-auto-freq; - resets = <0x23 0x12e 0x23 0x12f>; - reset-names = "video_a\0video_h"; - iommus = <0x7e>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x02>; - rockchip,resetgroup-node = <0x02>; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - iommu@fdee0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdee0800 0x00 0x40>; - interrupts = <0x00 0x3f 0x04>; - interrupt-names = "vepu_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xfd 0x23 0xfe>; - power-domains = <0x25 0x0a>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7e>; - }; - - iep@fdef0000 { - compatible = "rockchip,iep-v2"; - reg = <0x00 0xfdef0000 0x00 0x500>; - interrupts = <0x00 0x38 0x04>; - clocks = <0x23 0xf6 0x23 0xf7 0x23 0xf8>; - clock-names = "aclk\0hclk\0sclk"; - resets = <0x23 0x127 0x23 0x128 0x23 0x129>; - reset-names = "rst_a\0rst_h\0rst_s"; - power-domains = <0x25 0x0a>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x05>; - rockchip,resetgroup-node = <0x05>; - iommus = <0x7f>; - status = "okay"; - }; - - iommu@fdef0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdef0800 0x00 0x100>; - interrupts = <0x00 0x38 0x04>; - interrupt-names = "iep_mmu"; - clocks = <0x23 0xf6 0x23 0xf7>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - power-domains = <0x25 0x0a>; - status = "okay"; - phandle = <0x7f>; - }; - - eink@fdf00000 { - compatible = "rockchip,rk3568-eink-tcon"; - reg = <0x00 0xfdf00000 0x00 0x74>; - interrupts = <0x00 0xb2 0x04>; - clocks = <0x23 0xff 0x23 0x100>; - clock-names = "pclk\0hclk"; - status = "disabled"; - }; - - rkvenc@fdf40000 { - compatible = "rockchip,rkv-encoder-v1"; - reg = <0x00 0xfdf40000 0x00 0x400>; - interrupts = <0x00 0x8c 0x04>; - interrupt-names = "irq_enc"; - clocks = <0x23 0x103 0x23 0x104 0x23 0x105>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core"; - rockchip,normal-rates = <0x11b3dc40 0x00 0x11b3dc40>; - resets = <0x23 0x133 0x23 0x134 0x23 0x135>; - reset-names = "video_a\0video_h\0video_core"; - assigned-clocks = <0x23 0x103 0x23 0x105>; - assigned-clock-rates = <0x11b3dc40 0x11b3dc40>; - iommus = <0x80>; - node-name = "rkvenc"; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x03>; - rockchip,resetgroup-node = <0x03>; - power-domains = <0x25 0x0e>; - operating-points-v2 = <0x81>; - status = "okay"; - venc-supply = <0x75>; - }; - - rkvenc-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x07>; - nvmem-cell-names = "pvtm"; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x16378 0x01 0x16379 0x186a0 0x02>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x81>; - - opp-297000000 { - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - opp-microvolt-L2 = <0xd59f8>; - }; - - opp-400000000 { - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xe7ef0>; - opp-microvolt-L0 = <0xe7ef0>; - opp-microvolt-L1 = <0xe1d48>; - opp-microvolt-L2 = <0xdbba0>; - }; - }; - - iommu@fdf40f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdf40f00 0x00 0x40 0x00 0xfdf40f40 0x00 0x40>; - interrupts = <0x00 0x8d 0x04 0x00 0x8e 0x04>; - interrupt-names = "rkvenc_mmu0\0rkvenc_mmu1"; - clocks = <0x23 0x103 0x23 0x104>; - clock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - #iommu-cells = <0x00>; - power-domains = <0x25 0x0e>; - status = "okay"; - phandle = <0x80>; - }; - - rkvdec@fdf80200 { - compatible = "rockchip,rkv-decoder-rk3568\0rockchip,rkv-decoder-v2"; - reg = <0x00 0xfdf80200 0x00 0x400 0x00 0xfdf80100 0x00 0x100>; - reg-names = "regs\0link"; - interrupts = <0x00 0x5b 0x04>; - interrupt-names = "irq_dec"; - clocks = <0x23 0x108 0x23 0x109 0x23 0x10a 0x23 0x10b 0x23 0x10c>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_cabac\0clk_core\0clk_hevc_cabac"; - rockchip,normal-rates = <0x11b3dc40 0x00 0x11b3dc40 0x11b3dc40 0x23c34600>; - rockchip,advanced-rates = <0x179a7b00 0x00 0x179a7b00 0x179a7b00 0x23c34600>; - rockchip,default-max-load = <0x1fe000>; - resets = <0x23 0x142 0x23 0x143 0x23 0x144 0x23 0x145 0x23 0x146>; - assigned-clocks = <0x23 0x108 0x23 0x10a 0x23 0x10b 0x23 0x10c>; - assigned-clock-rates = <0x11b3dc40 0x11b3dc40 0x11b3dc40 0x11b3dc40>; - reset-names = "video_a\0video_h\0video_cabac\0video_core\0video_hevc_cabac"; - power-domains = <0x25 0x0d>; - operating-points-v2 = <0x82>; - vdec-supply = <0x75>; - iommus = <0x83>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x04>; - rockchip,resetgroup-node = <0x04>; - rockchip,sram = <0x84>; - rockchip,rcb-iova = <0x10000000 0x10000>; - rockchip,rcb-min-width = <0x200>; - rockchip,task-capacity = <0x10>; - status = "okay"; - }; - - rkvdec-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x85 0x07>; - nvmem-cell-names = "leakage\0pvtm"; - rockchip,leakage-voltage-sel = <0x01 0x50 0x00 0x51 0xfe 0x01>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x186a0 0x01>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x82>; - - opp-297000000 { - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - }; - - opp-400000000 { - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xdbba0>; - }; - }; - - iommu@fdf80800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdf80800 0x00 0x40 0x00 0xfdf80840 0x00 0x40>; - interrupts = <0x00 0x5c 0x04>; - interrupt-names = "rkvdec_mmu"; - clocks = <0x23 0x108 0x23 0x109>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x0d>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x83>; - }; - - mipi-csi2-hw@fdfb0000 { - compatible = "rockchip,rk3568-mipi-csi2-hw"; - reg = <0x00 0xfdfb0000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x08 0x04 0x00 0x09 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x23 0xd5>; - clock-names = "pclk_csi2host"; - resets = <0x23 0xff>; - reset-names = "srst_csihost_p"; - status = "okay"; - phandle = <0x1e>; - }; - - rkcif@fdfe0000 { - compatible = "rockchip,rk3568-cif"; - reg = <0x00 0xfdfe0000 0x00 0x8000>; - reg-names = "cif_regs"; - interrupts = <0x00 0x92 0x04>; - interrupt-names = "cif-intr"; - clocks = <0x23 0xce 0x23 0xcf 0x23 0xd0 0x23 0xd1>; - clock-names = "aclk_cif\0hclk_cif\0dclk_cif\0iclk_cif_g"; - resets = <0x23 0xf7 0x23 0xf8 0x23 0xf9 0x23 0xfb 0x23 0xfa>; - reset-names = "rst_cif_a\0rst_cif_h\0rst_cif_d\0rst_cif_p\0rst_cif_i"; - assigned-clocks = <0x23 0xd0>; - assigned-clock-rates = <0x11e1a300>; - power-domains = <0x25 0x08>; - rockchip,grf = <0x3b>; - iommus = <0x86>; - status = "disabled"; - phandle = <0x87>; - }; - - iommu@fdfe0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdfe0800 0x00 0x100>; - interrupts = <0x00 0x92 0x04>; - interrupt-names = "cif_mmu"; - clocks = <0x23 0xce 0x23 0xcf>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x08>; - rockchip,disable-mmu-reset; - #iommu-cells = <0x00>; - status = "disabled"; - phandle = <0x86>; - }; - - rkcif_dvp { - compatible = "rockchip,rkcif-dvp"; - rockchip,hw = <0x87>; - status = "disabled"; - phandle = <0x88>; - }; - - rkcif_dvp_sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x88>; - status = "disabled"; - }; - - rkcif_mipi_lvds { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x87>; - status = "disabled"; - phandle = <0x89>; - }; - - rkcif_mipi_lvds_sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x89>; - status = "disabled"; - }; - - rkisp@fdff0000 { - compatible = "rockchip,rk3568-rkisp"; - reg = <0x00 0xfdff0000 0x00 0x10000>; - interrupts = <0x00 0x39 0x04 0x00 0x3a 0x04 0x00 0x3c 0x04>; - interrupt-names = "mipi_irq\0mi_irq\0isp_irq"; - clocks = <0x23 0xd2 0x23 0xd3 0x23 0xd4>; - clock-names = "aclk_isp\0hclk_isp\0clk_isp"; - resets = <0x23 0xfd 0x23 0xfc>; - reset-names = "isp\0isp-h"; - rockchip,grf = <0x3b>; - power-domains = <0x25 0x08>; - iommus = <0x8a>; - rockchip,iq-feature = <0x1bfb 0xfffe67ff>; - status = "okay"; - phandle = <0x8b>; - }; - - iommu@fdff1a00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdff1a00 0x00 0x100>; - interrupts = <0x00 0x3b 0x04>; - interrupt-names = "isp_mmu"; - clocks = <0x23 0xd2 0x23 0xd3>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x08>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "okay"; - phandle = <0x8a>; - }; - - rkisp-vir0 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x8b>; - status = "okay"; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x8c>; - phandle = <0x134>; - }; - }; - }; - - rkisp-vir1 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x8b>; - status = "disabled"; - }; - - uio@fe010000 { - compatible = "rockchip,uio-gmac"; - reg = <0x00 0xfe010000 0x00 0x10000>; - rockchip,ethernet = <0x8d>; - status = "disabled"; - }; - - ethernet@fe010000 { - local-mac-address = [5e 4f fd 70 05 c6]; - compatible = "rockchip,rk3568-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe010000 0x00 0x10000>; - interrupts = <0x00 0x20 0x04 0x00 0x1d 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0x3b>; - clocks = <0x23 0x186 0x23 0x189 0x23 0x189 0x23 0xc7 0x23 0xc3 0x23 0xc4 0x23 0x189 0x23 0xc8 0x23 0xac 0x23 0xab>; - clock-names = "stmmaceth\0mac_clk_rx\0mac_clk_tx\0clk_mac_refout\0aclk_mac\0pclk_mac\0clk_mac_speed\0ptp_ref\0pclk_xpcs\0clk_xpcs_eee"; - resets = <0x23 0xec>; - reset-names = "stmmaceth"; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0x8e>; - snps,mtl-rx-config = <0x8f>; - snps,mtl-tx-config = <0x90>; - status = "okay"; - phy-mode = "rgmii"; - clock_in_out = "input"; - snps,reset-gpio = <0x91 0x19 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - assigned-clocks = <0x23 0x189 0x23 0x186>; - assigned-clock-parents = <0x23 0x187 0x92>; - pinctrl-names = "default"; - pinctrl-0 = <0x93 0x94 0x95 0x96 0x97 0x98>; - tx_delay = <0x3e>; - rx_delay = <0x32>; - phy-handle = <0x99>; - phandle = <0x8d>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x00>; - led_status_value = <0x6940>; - phandle = <0x99>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0x8e>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x01>; - phandle = <0x8f>; - - queue0 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x01>; - phandle = <0x90>; - - queue0 { - }; - }; - }; - - vop@fe040000 { - compatible = "rockchip,rk3568-vop"; - reg = <0x00 0xfe040000 0x00 0x3000 0x00 0xfe044000 0x00 0x1000>; - reg-names = "regs\0gamma_lut"; - rockchip,grf = <0x3b>; - interrupts = <0x00 0x94 0x04>; - clocks = <0x23 0xdd 0x23 0xde 0x23 0xdf 0x23 0xe0 0x23 0xe1>; - clock-names = "aclk_vop\0hclk_vop\0dclk_vp0\0dclk_vp1\0dclk_vp2"; - iommus = <0x9a>; - power-domains = <0x25 0x09>; - status = "okay"; - assigned-clocks = <0x23 0xdf 0x23 0xe0>; - assigned-clock-parents = <0x3a 0x02 0x23 0x05>; - disable-win-move; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - phandle = <0x15>; - - port@0 { - rockchip,primary-plane = <0x04>; - rockchip,plane-mask = <0x15>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x9b>; - phandle = <0x17>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x9c>; - phandle = <0x18>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x9d>; - phandle = <0x19>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0x9e>; - phandle = <0x1a>; - }; - }; - - port@1 { - rockchip,primary-plane = <0x05>; - rockchip,plane-mask = <0x22>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x01>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x9f>; - phandle = <0xa8>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa0>; - phandle = <0xa9>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xa1>; - phandle = <0xaf>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0xa2>; - phandle = <0xad>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0xa3>; - phandle = <0x1b>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0xa4>; - phandle = <0x37>; - }; - }; - - port@2 { - rockchip,primary-plane = <0x03>; - rockchip,plane-mask = <0x08>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x02>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xa5>; - phandle = <0x35>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa6>; - phandle = <0x1c>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xa7>; - phandle = <0x38>; - }; - }; - }; - }; - - iommu@fe043e00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfe043e00 0x00 0x100 0x00 0xfe043f00 0x00 0x100>; - interrupts = <0x00 0x94 0x04>; - interrupt-names = "vop_mmu"; - clocks = <0x23 0xdd 0x23 0xde>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - rockchip,disable-device-link-resume; - status = "okay"; - phandle = <0x9a>; - }; - - dsi@fe060000 { - compatible = "rockchip,rk3568-mipi-dsi"; - reg = <0x00 0xfe060000 0x00 0x10000>; - interrupts = <0x00 0x44 0x04>; - clocks = <0x23 0xe8 0x23 0xda>; - clock-names = "pclk\0hclk"; - resets = <0x23 0x110>; - reset-names = "apb"; - phys = <0x34>; - phy-names = "dphy"; - power-domains = <0x25 0x09>; - rockchip,grf = <0x3b>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x17>; - status = "disabled"; - phandle = <0x9b>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa8>; - status = "disabled"; - phandle = <0x9f>; - }; - }; - }; - }; - - dsi@fe070000 { - compatible = "rockchip,rk3568-mipi-dsi"; - reg = <0x00 0xfe070000 0x00 0x10000>; - interrupts = <0x00 0x45 0x04>; - clocks = <0x23 0xe9 0x23 0xda>; - clock-names = "pclk\0hclk"; - resets = <0x23 0x111>; - reset-names = "apb"; - phys = <0x36>; - phy-names = "dphy"; - power-domains = <0x25 0x09>; - rockchip,grf = <0x3b>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x18>; - status = "disabled"; - phandle = <0x9c>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa9>; - status = "disabled"; - phandle = <0xa0>; - }; - }; - }; - }; - - hdmi@fe0a0000 { - compatible = "rockchip,rk3568-dw-hdmi"; - reg = <0x00 0xfe0a0000 0x00 0x20000>; - interrupts = <0x00 0x2d 0x04>; - clocks = <0x23 0xe6 0x23 0xe7 0x23 0x193 0x3a 0x02 0x23 0xde>; - clock-names = "iahb\0isfr\0cec\0ref\0hclk"; - power-domains = <0x25 0x09>; - reg-io-width = <0x04>; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xaa 0xab 0xac>; - status = "okay"; - phandle = <0x147>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x1a>; - status = "okay"; - phandle = <0x9e>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xad>; - status = "disabled"; - phandle = <0xa2>; - }; - }; - }; - }; - - edp@fe0c0000 { - compatible = "rockchip,rk3568-edp"; - reg = <0x00 0xfe0c0000 0x00 0x10000>; - interrupts = <0x00 0x12 0x04>; - clocks = <0x3a 0x29 0x23 0xea 0x23 0xeb 0x23 0xda>; - clock-names = "dp\0pclk\0spdif\0hclk"; - resets = <0x23 0x113 0x23 0x112>; - reset-names = "dp\0apb"; - phys = <0xae>; - phy-names = "dp"; - power-domains = <0x25 0x09>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x19>; - status = "disabled"; - phandle = <0x9d>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xaf>; - status = "disabled"; - phandle = <0xa1>; - }; - }; - }; - }; - - nocp-cpu@fe102000 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102000 0x00 0x400>; - phandle = <0xb5>; - }; - - nocp-gpu-vpu-rga-venc@fe102400 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102400 0x00 0x400>; - }; - - nocp-vdec@fe102800 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102800 0x00 0x400>; - }; - - nocp-vi-usb-peri-pipe@fe102c00 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102c00 0x00 0x400>; - }; - - nocp-vo@fe103000 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe103000 0x00 0x400>; - }; - - qos@fe128000 { - compatible = "syscon"; - reg = <0x00 0xfe128000 0x00 0x20>; - phandle = <0x55>; - }; - - qos@fe138080 { - compatible = "syscon"; - reg = <0x00 0xfe138080 0x00 0x20>; - phandle = <0x64>; - }; - - qos@fe138100 { - compatible = "syscon"; - reg = <0x00 0xfe138100 0x00 0x20>; - phandle = <0x65>; - }; - - qos@fe138180 { - compatible = "syscon"; - reg = <0x00 0xfe138180 0x00 0x20>; - phandle = <0x66>; - }; - - qos@fe148000 { - compatible = "syscon"; - reg = <0x00 0xfe148000 0x00 0x20>; - phandle = <0x56>; - }; - - qos@fe148080 { - compatible = "syscon"; - reg = <0x00 0xfe148080 0x00 0x20>; - phandle = <0x57>; - }; - - qos@fe148100 { - compatible = "syscon"; - reg = <0x00 0xfe148100 0x00 0x20>; - phandle = <0x58>; - }; - - qos@fe150000 { - compatible = "syscon"; - reg = <0x00 0xfe150000 0x00 0x20>; - phandle = <0x62>; - }; - - qos@fe158000 { - compatible = "syscon"; - reg = <0x00 0xfe158000 0x00 0x20>; - phandle = <0x5c>; - }; - - qos@fe158100 { - compatible = "syscon"; - reg = <0x00 0xfe158100 0x00 0x20>; - phandle = <0x5d>; - }; - - qos@fe158180 { - compatible = "syscon"; - reg = <0x00 0xfe158180 0x00 0x20>; - phandle = <0x5e>; - }; - - qos@fe158200 { - compatible = "syscon"; - reg = <0x00 0xfe158200 0x00 0x20>; - phandle = <0x5f>; - }; - - qos@fe158280 { - compatible = "syscon"; - reg = <0x00 0xfe158280 0x00 0x20>; - phandle = <0x60>; - }; - - qos@fe158300 { - compatible = "syscon"; - reg = <0x00 0xfe158300 0x00 0x20>; - phandle = <0x61>; - }; - - qos@fe180000 { - compatible = "syscon"; - reg = <0x00 0xfe180000 0x00 0x20>; - phandle = <0x54>; - }; - - qos@fe190000 { - compatible = "syscon"; - reg = <0x00 0xfe190000 0x00 0x20>; - phandle = <0x67>; - }; - - qos@fe190080 { - compatible = "syscon"; - reg = <0x00 0xfe190080 0x00 0x20>; - phandle = <0x68>; - }; - - qos@fe190100 { - compatible = "syscon"; - reg = <0x00 0xfe190100 0x00 0x20>; - phandle = <0x69>; - }; - - qos@fe190200 { - compatible = "syscon"; - reg = <0x00 0xfe190200 0x00 0x20>; - phandle = <0x6a>; - }; - - qos@fe190280 { - compatible = "syscon"; - reg = <0x00 0xfe190280 0x00 0x20>; - phandle = <0x6b>; - }; - - qos@fe190300 { - compatible = "syscon"; - reg = <0x00 0xfe190300 0x00 0x20>; - phandle = <0x6c>; - }; - - qos@fe190380 { - compatible = "syscon"; - reg = <0x00 0xfe190380 0x00 0x20>; - phandle = <0x6d>; - }; - - qos@fe190400 { - compatible = "syscon"; - reg = <0x00 0xfe190400 0x00 0x20>; - phandle = <0x6e>; - }; - - qos@fe198000 { - compatible = "syscon"; - reg = <0x00 0xfe198000 0x00 0x20>; - phandle = <0x63>; - }; - - qos@fe1a8000 { - compatible = "syscon"; - reg = <0x00 0xfe1a8000 0x00 0x20>; - phandle = <0x59>; - }; - - qos@fe1a8080 { - compatible = "syscon"; - reg = <0x00 0xfe1a8080 0x00 0x20>; - phandle = <0x5a>; - }; - - qos@fe1a8100 { - compatible = "syscon"; - reg = <0x00 0xfe1a8100 0x00 0x20>; - phandle = <0x5b>; - }; - - dwmmc@fe000000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe000000 0x00 0x4000>; - interrupts = <0x00 0x64 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xc1 0x23 0xc2 0x23 0x18e 0x23 0x18f>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xeb>; - reset-names = "reset"; - status = "okay"; - no-sd; - no-mmc; - bus-width = <0x04>; - disable-wp; - cap-sd-highspeed; - cap-sdio-irq; - keep-power-in-suspend; - pinctrl-names = "default"; - pinctrl-0 = <0xb0 0xb1 0xb2>; - sd-uhs-sdr104; - mmc-pwrseq = <0xb3>; - non-removable; - }; - - dfi@fe230000 { - reg = <0x00 0xfe230000 0x00 0x400>; - compatible = "rockchip,rk3568-dfi"; - rockchip,pmugrf = <0x3c>; - status = "okay"; - phandle = <0xb4>; - }; - - dmc { - compatible = "rockchip,rk3568-dmc"; - interrupts = <0x00 0x0a 0x04>; - interrupt-names = "complete"; - devfreq-events = <0xb4 0xb5>; - clocks = <0x02 0x03>; - clock-names = "dmc_clk"; - operating-points-v2 = <0xb6>; - vop-bw-dmc-freq = <0x00 0x11e 0x4f1a0 0x11f 0x1869f 0x80e80>; - vop-frame-bw-dmc-freq = <0x00 0x26c 0x4f1a0 0x26d 0x1869f 0xbe6e0>; - cpu-bw-dmc-freq = <0x00 0x15e 0x4f1a0 0x15f 0x190 0x80e80 0x191 0x1869f 0xbe6e0>; - upthreshold = <0x28>; - downdifferential = <0x14>; - system-status-level = <0x01 0x04 0x08 0x08 0x02 0x01 0x10 0x04 0x10000 0x04 0x1000 0x08 0x4000 0x08 0x2000 0x08 0xc00 0x08>; - auto-min-freq = <0x4f1a0>; - auto-freq-en = <0x01>; - #cooling-cells = <0x02>; - status = "okay"; - center-supply = <0x75>; - phandle = <0x16>; - }; - - dmc-fsp { - compatible = "rockchip,rk3568-dmc-fsp"; - debug_print_level = <0x00>; - ddr3_params = <0xb7>; - ddr4_params = <0xb8>; - lpddr3_params = <0xb9>; - lpddr4_params = <0xba>; - lpddr4x_params = <0xbb>; - status = "okay"; - }; - - dmc-opp-table { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x85 0x07 0x08 0xbc 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x618 0x124f8>; - rockchip,leakage-voltage-sel = <0x01 0x50 0x00 0x51 0xfe 0x01>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x186a0 0x01>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0xb6>; - - opp-1560000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x5cfbb600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L1 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-j-m-1560000000 { - opp-supported-hw = <0x06 0xffff>; - opp-hz = <0x00 0x5cfbb600>; - opp-microvolt = <0xd59f8 0xd59f8 0xf4240>; - }; - }; - - pcie@fe260000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x00 0x0f>; - clocks = <0x23 0x81 0x23 0x82 0x23 0x83 0x23 0x84 0x23 0x85>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0x4b 0x04 0x00 0x4a 0x04 0x00 0x49 0x04 0x00 0x48 0x04 0x00 0x47 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xbd 0x00 0x00 0x00 0x00 0x02 0xbd 0x01 0x00 0x00 0x00 0x03 0xbd 0x02 0x00 0x00 0x00 0x04 0xbd 0x03>; - linux,pci-domain = <0x00>; - num-ib-windows = <0x06>; - num-viewport = <0x08>; - num-ob-windows = <0x02>; - max-link-speed = <0x02>; - msi-map = <0x00 0xbe 0x00 0x1000>; - num-lanes = <0x01>; - phys = <0x27 0x02>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf4000000 0x00 0xf4000000 0x00 0x100000 0x81000000 0x00 0xf4100000 0x00 0xf4100000 0x00 0x100000 0x82000000 0x00 0xf4200000 0x00 0xf4200000 0x00 0x1e00000 0xc3000000 0x03 0x00 0x03 0x00 0x00 0x40000000>; - reg = <0x03 0xc0000000 0x00 0x400000 0x00 0xfe260000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xa1>; - reset-names = "pipe"; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x48 0x01>; - phandle = <0xbd>; - }; - }; - - pcie@fe270000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x10 0x1f>; - clocks = <0x23 0x88 0x23 0x89 0x23 0x8a 0x23 0x8b 0x23 0x8c>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0xa0 0x04 0x00 0x9f 0x04 0x00 0x9e 0x04 0x00 0x9d 0x04 0x00 0x9c 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xbf 0x00 0x00 0x00 0x00 0x02 0xbf 0x01 0x00 0x00 0x00 0x03 0xbf 0x02 0x00 0x00 0x00 0x04 0xbf 0x03>; - linux,pci-domain = <0x01>; - num-ib-windows = <0x06>; - num-ob-windows = <0x02>; - num-viewport = <0x08>; - max-link-speed = <0x03>; - msi-map = <0x1000 0xbe 0x1000 0x1000>; - num-lanes = <0x01>; - phys = <0xc0>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf2000000 0x00 0xf2000000 0x00 0x100000 0x81000000 0x00 0xf2100000 0x00 0xf2100000 0x00 0x100000 0x82000000 0x00 0xf2200000 0x00 0xf2200000 0x00 0x1e00000 0xc3000000 0x03 0x40000000 0x03 0x40000000 0x00 0x40000000>; - reg = <0x03 0xc0400000 0x00 0x400000 0x00 0xfe270000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xb1>; - reset-names = "pipe"; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9d 0x01>; - phandle = <0xbf>; - }; - }; - - pcie@fe280000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x20 0x2f>; - clocks = <0x23 0x8f 0x23 0x90 0x23 0x91 0x23 0x92 0x23 0x93>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0xa5 0x04 0x00 0xa4 0x04 0x00 0xa3 0x04 0x00 0xa2 0x04 0x00 0xa1 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xc1 0x00 0x00 0x00 0x00 0x02 0xc1 0x01 0x00 0x00 0x00 0x03 0xc1 0x02 0x00 0x00 0x00 0x04 0xc1 0x03>; - linux,pci-domain = <0x02>; - num-ib-windows = <0x06>; - num-viewport = <0x08>; - num-ob-windows = <0x02>; - max-link-speed = <0x03>; - msi-map = <0x2000 0xbe 0x2000 0x1000>; - num-lanes = <0x02>; - phys = <0xc0>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf0000000 0x00 0xf0000000 0x00 0x100000 0x81000000 0x00 0xf0100000 0x00 0xf0100000 0x00 0x100000 0x82000000 0x00 0xf0200000 0x00 0xf0200000 0x00 0x1e00000 0xc3000000 0x03 0x80000000 0x03 0x80000000 0x00 0x40000000>; - reg = <0x03 0xc0800000 0x00 0x400000 0x00 0xfe280000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xc1>; - reset-names = "pipe"; - status = "okay"; - reset-gpios = <0x91 0x1e 0x00>; - vpcie3v3-supply = <0xc2>; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xa2 0x01>; - phandle = <0xc1>; - }; - }; - - uio@fe2a0000 { - compatible = "rockchip,uio-gmac"; - reg = <0x00 0xfe2a0000 0x00 0x10000>; - rockchip,ethernet = <0xc3>; - status = "disabled"; - }; - - ethernet@fe2a0000 { - local-mac-address = [5a 4f fd 70 05 c6]; - compatible = "rockchip,rk3568-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe2a0000 0x00 0x10000>; - interrupts = <0x00 0x1b 0x04 0x00 0x18 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0x3b>; - clocks = <0x23 0x182 0x23 0x185 0x23 0x185 0x23 0xb8 0x23 0xb4 0x23 0xb5 0x23 0x185 0x23 0xb9 0x23 0xac 0x23 0xab>; - clock-names = "stmmaceth\0mac_clk_rx\0mac_clk_tx\0clk_mac_refout\0aclk_mac\0pclk_mac\0clk_mac_speed\0ptp_ref\0pclk_xpcs\0clk_xpcs_eee"; - resets = <0x23 0xd7>; - reset-names = "stmmaceth"; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0xc4>; - snps,mtl-rx-config = <0xc5>; - snps,mtl-tx-config = <0xc6>; - status = "okay"; - phy-mode = "rgmii"; - clock_in_out = "input"; - snps,reset-gpio = <0x91 0x1b 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - assigned-clocks = <0x23 0x185 0x23 0x182>; - assigned-clock-parents = <0x23 0x183 0xc7>; - pinctrl-names = "default"; - pinctrl-0 = <0xc8 0xc9 0xca 0xcb 0xcc 0xcd>; - tx_delay = <0x4a>; - rx_delay = <0x2e>; - phy-handle = <0xce>; - phandle = <0xc3>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x00>; - led_status_value = <0x6940>; - phandle = <0xce>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0xc4>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x01>; - phandle = <0xc5>; - - queue0 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x01>; - phandle = <0xc6>; - - queue0 { - }; - }; - }; - - dwmmc@fe2b0000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2b0000 0x00 0x4000>; - interrupts = <0x00 0x62 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xb0 0x23 0xb1 0x23 0x18a 0x23 0x18b>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xd4>; - reset-names = "reset"; - status = "okay"; - no-sdio; - no-mmc; - bus-width = <0x04>; - cap-mmc-highspeed; - cap-sd-highspeed; - disable-wp; - sd-uhs-sdr104; - vmmc-supply = <0xcf>; - vqmmc-supply = <0x31>; - pinctrl-names = "default"; - pinctrl-0 = <0xd0 0xd1 0xd2 0xd3>; - }; - - dwmmc@fe2c0000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2c0000 0x00 0x4000>; - interrupts = <0x00 0x63 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xb2 0x23 0xb3 0x23 0x18c 0x23 0x18d>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xd6>; - reset-names = "reset"; - status = "disabled"; - }; - - spi@fe300000 { - compatible = "rockchip,sfc"; - reg = <0x00 0xfe300000 0x00 0x4000>; - interrupts = <0x00 0x65 0x04>; - clocks = <0x23 0x78 0x23 0x76>; - clock-names = "clk_sfc\0hclk_sfc"; - assigned-clocks = <0x23 0x78>; - assigned-clock-rates = <0x2faf080>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <0xd4>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x00>; - spi-max-frequency = <0x2faf080>; - spi-rx-bus-width = <0x01>; - spi-tx-bus-width = <0x01>; - }; - }; - - sdhci@fe310000 { - compatible = "rockchip,rk3568-dwcmshc\0rockchip,dwcmshc-sdhci"; - reg = <0x00 0xfe310000 0x00 0x10000>; - interrupts = <0x00 0x13 0x04>; - assigned-clocks = <0x23 0x7b 0x23 0x7d 0x23 0x7c>; - assigned-clock-rates = <0xbebc200 0x16e3600 0xbebc200>; - clocks = <0x23 0x7c 0x23 0x7a 0x23 0x79 0x23 0x7b 0x23 0x7d>; - clock-names = "core\0bus\0axi\0block\0timer"; - resets = <0x23 0x78 0x23 0x76 0x23 0x75 0x23 0x77 0x23 0x79>; - reset-names = "core\0bus\0axi\0block\0timer"; - status = "okay"; - bus-width = <0x08>; - no-sdio; - no-sd; - non-removable; - max-frequency = <0xbebc200>; - full-pwr-cycle-in-suspend; - }; - - nandc@fe330000 { - compatible = "rockchip,rk-nandc-v9"; - reg = <0x00 0xfe330000 0x00 0x4000>; - interrupts = <0x00 0x46 0x04>; - nandc_id = <0x00>; - clocks = <0x23 0x75 0x23 0x74>; - clock-names = "clk_nandc\0hclk_nandc"; - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - nand@0 { - reg = <0x00>; - nand-bus-width = <0x08>; - nand-ecc-mode = "hw"; - nand-ecc-strength = <0x10>; - nand-ecc-step-size = <0x400>; - }; - }; - - crypto@fe380000 { - compatible = "rockchip,rk3568-crypto"; - reg = <0x00 0xfe380000 0x00 0x4000>; - interrupts = <0x00 0x04 0x04>; - clocks = <0x23 0x6a 0x23 0x6b 0x23 0x6c 0x23 0x6d>; - clock-names = "aclk\0hclk\0sclk\0apb_pclk"; - assigned-clocks = <0x23 0x6c>; - assigned-clock-rates = <0xbebc200>; - resets = <0x23 0x69>; - reset-names = "crypto-rst"; - status = "disabled"; - }; - - rng@fe388000 { - compatible = "rockchip,cryptov2-rng"; - reg = <0x00 0xfe388000 0x00 0x2000>; - clocks = <0x23 0x70 0x23 0x6f>; - clock-names = "clk_trng\0hclk_trng"; - resets = <0x23 0x6d>; - reset-names = "reset"; - status = "okay"; - }; - - otp@fe38c000 { - compatible = "rockchip,rk3568-otp"; - reg = <0x00 0xfe38c000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - clocks = <0x23 0x73 0x23 0x72 0x23 0x71 0x23 0x181>; - clock-names = "usr\0sbpi\0apb\0phy"; - resets = <0x23 0x1cf>; - reset-names = "otp_phy"; - - cpu-code@2 { - reg = <0x02 0x02>; - phandle = <0x12>; - }; - - specification-serial-number@7 { - reg = <0x07 0x01>; - bits = <0x00 0x05>; - phandle = <0x0a>; - }; - - cpu-version@8 { - reg = <0x08 0x01>; - bits = <0x03 0x03>; - phandle = <0x11>; - }; - - mbist-vmin@9 { - reg = <0x09 0x01>; - bits = <0x00 0x04>; - phandle = <0x08>; - }; - - id@a { - reg = <0x0a 0x10>; - phandle = <0x10>; - }; - - cpu-leakage@1a { - reg = <0x1a 0x01>; - phandle = <0x06>; - }; - - log-leakage@1b { - reg = <0x1b 0x01>; - phandle = <0x85>; - }; - - npu-leakage@1c { - reg = <0x1c 0x01>; - phandle = <0x72>; - }; - - gpu-leakage@1d { - reg = <0x1d 0x01>; - phandle = <0x78>; - }; - - core-pvtm@2a { - reg = <0x2a 0x02>; - phandle = <0x07>; - }; - - cpu-tsadc-trim-l@2e { - reg = <0x2e 0x01>; - phandle = <0x125>; - }; - - cpu-tsadc-trim-h@2f { - reg = <0x2f 0x01>; - bits = <0x00 0x04>; - phandle = <0x126>; - }; - - npu-tsadc-trim-l@30 { - reg = <0x30 0x01>; - phandle = <0x127>; - }; - - npu-tsadc-trim-h@31 { - reg = <0x31 0x01>; - bits = <0x00 0x04>; - phandle = <0x128>; - }; - - tsadc-trim-base-frac@31 { - reg = <0x31 0x01>; - bits = <0x04 0x04>; - phandle = <0x122>; - }; - - tsadc-trim-base@32 { - reg = <0x32 0x01>; - phandle = <0x121>; - }; - - cpu-opp-info@36 { - reg = <0x36 0x06>; - phandle = <0x09>; - }; - - gpu-opp-info@3c { - reg = <0x3c 0x06>; - phandle = <0x79>; - }; - - npu-opp-info@42 { - reg = <0x42 0x06>; - phandle = <0x73>; - }; - - dmc-opp-info@48 { - reg = <0x48 0x06>; - phandle = <0xbc>; - }; - - remark-spec-serial-number@56 { - reg = <0x56 0x01>; - bits = <0x00 0x05>; - phandle = <0x0b>; - }; - }; - - i2s@fe400000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe400000 0x00 0x1000>; - interrupts = <0x00 0x34 0x04>; - clocks = <0x23 0x3f 0x23 0x43 0x23 0x39>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x00>; - dma-names = "tx"; - resets = <0x23 0x50 0x23 0x51>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x146>; - }; - - i2s@fe410000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe410000 0x00 0x1000>; - interrupts = <0x00 0x35 0x04>; - clocks = <0x23 0x47 0x23 0x4b 0x23 0x3a>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x02 0xd5 0x03>; - dma-names = "tx\0rx"; - resets = <0x23 0x52 0x23 0x53>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xd6 0xd7 0xd8 0xd9>; - status = "okay"; - rockchip,clk-trcm = <0x01>; - phandle = <0xe8>; - }; - - i2s@fe420000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe420000 0x00 0x1000>; - interrupts = <0x00 0x36 0x04>; - clocks = <0x23 0x4f 0x23 0x4f 0x23 0x3b>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x04 0xd5 0x05>; - dma-names = "tx\0rx"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,clk-trcm = <0x01>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xda 0xdb 0xdc 0xdd>; - status = "disabled"; - }; - - i2s@fe430000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe430000 0x00 0x1000>; - interrupts = <0x00 0x37 0x04>; - clocks = <0x23 0x53 0x23 0x57 0x23 0x3c>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x06 0xd5 0x07>; - dma-names = "tx\0rx"; - resets = <0x23 0x55 0x23 0x56>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,clk-trcm = <0x01>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xde 0xdf 0xe0 0xe1>; - status = "disabled"; - phandle = <0x144>; - }; - - pdm@fe440000 { - compatible = "rockchip,rk3568-pdm\0rockchip,pdm"; - reg = <0x00 0xfe440000 0x00 0x1000>; - clocks = <0x23 0x5a 0x23 0x59>; - clock-names = "pdm_clk\0pdm_hclk"; - dmas = <0xd5 0x09>; - dma-names = "rx"; - pinctrl-names = "default"; - pinctrl-0 = <0xe2 0xe3 0xe4 0xe5 0xe6 0xe7>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x149>; - }; - - vad@fe450000 { - compatible = "rockchip,rk3568-vad"; - reg = <0x00 0xfe450000 0x00 0x10000>; - reg-names = "vad"; - clocks = <0x23 0x5b>; - clock-names = "hclk"; - interrupts = <0x00 0x89 0x04>; - rockchip,audio-src = <0xe8>; - rockchip,det-channel = <0x00>; - rockchip,mode = <0x00>; - #sound-dai-cells = <0x00>; - status = "disabled"; - rockchip,buffer-time-ms = <0x80>; - phandle = <0x14e>; - }; - - spdif@fe460000 { - compatible = "rockchip,rk3568-spdif"; - reg = <0x00 0xfe460000 0x00 0x1000>; - interrupts = <0x00 0x66 0x04>; - dmas = <0xd5 0x01>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x23 0x5f 0x23 0x5c>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xe9>; - status = "disabled"; - phandle = <0x14c>; - }; - - audpwm@fe470000 { - compatible = "rockchip,rk3568-audio-pwm\0rockchip,audio-pwm-v1"; - reg = <0x00 0xfe470000 0x00 0x1000>; - clocks = <0x23 0x63 0x23 0x60>; - clock-names = "clk\0hclk"; - dmas = <0xd5 0x08>; - dma-names = "tx"; - #sound-dai-cells = <0x00>; - rockchip,sample-width-bits = <0x0b>; - rockchip,interpolat-points = <0x01>; - status = "disabled"; - }; - - codec-digital@fe478000 { - compatible = "rockchip,rk3568-codec-digital\0rockchip,codec-digital-v1"; - reg = <0x00 0xfe478000 0x00 0x1000>; - clocks = <0x23 0x67 0x23 0x66 0x23 0x65 0x23 0x64>; - clock-names = "adc\0dac\0i2c\0pclk"; - pinctrl-names = "default"; - pinctrl-0 = <0xea>; - resets = <0x23 0x5f>; - reset-names = "reset"; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x145>; - }; - - dmac@fe530000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfe530000 0x00 0x4000>; - interrupts = <0x00 0x0e 0x04 0x00 0x0d 0x04>; - clocks = <0x23 0x10d>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0x4e>; - }; - - dmac@fe550000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfe550000 0x00 0x4000>; - interrupts = <0x00 0x10 0x04 0x00 0x0f 0x04>; - clocks = <0x23 0x10d>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0xd5>; - }; - - rkscr@fe560000 { - compatible = "rockchip-scr"; - reg = <0x00 0xfe560000 0x00 0x10000>; - interrupts = <0x00 0x61 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xeb>; - clocks = <0x23 0x114>; - clock-names = "g_pclk_sim_card"; - status = "disabled"; - }; - - can@fe570000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe570000 0x00 0x1000>; - interrupts = <0x00 0x01 0x04>; - clocks = <0x23 0x141 0x23 0x140>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x155 0x23 0x154>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - }; - - can@fe580000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe580000 0x00 0x1000>; - interrupts = <0x00 0x02 0x04>; - clocks = <0x23 0x143 0x23 0x142>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x157 0x23 0x156>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "okay"; - assigned-clocks = <0x23 0x143>; - assigned-clock-rates = <0xbebc200>; - pinctrl-names = "default"; - pinctrl-0 = <0xec>; - }; - - can@fe590000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe590000 0x00 0x1000>; - interrupts = <0x00 0x03 0x04>; - clocks = <0x23 0x145 0x23 0x144>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x159 0x23 0x158>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - assigned-clocks = <0x23 0x145>; - assigned-clock-rates = <0xbebc200>; - pinctrl-names = "default"; - pinctrl-0 = <0xed>; - }; - - i2c@fe5a0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5a0000 0x00 0x1000>; - clocks = <0x23 0x148 0x23 0x147>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x2f 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xee>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - clock-frequency = <0x186a0>; - - gpio@21 { - status = "disabled"; - compatible = "nxp,pca9555"; - reg = <0x21>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-group-num = <0xc8>; - phandle = <0x102>; - }; - - gt1x@14 { - status = "disabled"; - compatible = "goodix,gt1x"; - reg = <0x14>; - pinctrl-names = "default"; - pinctrl-0 = <0xef>; - goodix,rst-gpio = <0x3f 0x0e 0x00>; - goodix,irq-gpio = <0x3f 0x0d 0x08>; - }; - }; - - i2c@fe5b0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5b0000 0x00 0x1000>; - clocks = <0x23 0x14a 0x23 0x149>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x30 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf0>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@fe5c0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5c0000 0x00 0x1000>; - clocks = <0x23 0x14c 0x23 0x14b>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x31 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf1>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@fe5d0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5d0000 0x00 0x1000>; - clocks = <0x23 0x14e 0x23 0x14d>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x32 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf2>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - clock-frequency = <0x61a80>; - - gc8034@37 { - compatible = "galaxycore,gc8034"; - status = "disabled"; - reg = <0x37>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x01>; - pwdn-gpios = <0xf4 0x0c 0x01>; - rockchip,grf = <0x3b>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "RK-CMK-8M-2-v1"; - rockchip,camera-module-lens-name = "CK8401"; - - port { - - endpoint { - remote-endpoint = <0xf5>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x130>; - }; - }; - }; - - os04a10@36 { - status = "disabled"; - compatible = "ovti,os04a10"; - reg = <0x36>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x01>; - pwdn-gpios = <0xf4 0x0c 0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT1607-FV1"; - rockchip,camera-module-lens-name = "M12-40IRC-4MP-F16"; - - port { - - endpoint { - remote-endpoint = <0xf6>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x12f>; - }; - }; - }; - - ov5695@36 { - status = "disabled"; - compatible = "ovti,ov5695"; - reg = <0x36>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x00>; - pwdn-gpios = <0xf4 0x0c 0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "TongJu"; - rockchip,camera-module-lens-name = "CHT842-MD"; - - port { - - endpoint { - remote-endpoint = <0xf7>; - data-lanes = <0x01 0x02>; - phandle = <0x131>; - }; - }; - }; - - XC7160b@1b { - status = "okay"; - compatible = "firefly,xc7160"; - reg = <0x1b>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x3f 0x1d 0x00>; - pwdn-gpios = <0xf4 0x0c 0x00>; - firefly,clkout-enabled-index = <0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "NC"; - rockchip,camera-module-lens-name = "NC"; - - port { - - endpoint { - remote-endpoint = <0xf8>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x132>; - }; - }; - }; - - imx415@37 { - status = "okay"; - compatible = "sony,imx415"; - reg = <0x37>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x3f 0x1d 0x01>; - pwdn-gpios = <0xf4 0x0c 0x00>; - firefly,clkout-enabled-index = <0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT2022-PX1"; - rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; - - port { - - endpoint { - remote-endpoint = <0xf9>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x133>; - }; - }; - }; - }; - - i2c@fe5e0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5e0000 0x00 0x1000>; - clocks = <0x23 0x150 0x23 0x14f>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x33 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xfa>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - hym8563@51 { - status = "okay"; - compatible = "haoyu,hym8563"; - reg = <0x51>; - #clock-cells = <0x00>; - rtc-irq-gpio = <0x3f 0x1b 0x02>; - clock-frequency = <0x8000>; - }; - - mc3230sensor@4c { - compatible = "gs_mc3230"; - reg = <0x4c>; - type = <0x02>; - irq_enable = <0x00>; - poll_delay_ms = <0x1e>; - layout = <0x04>; - status = "okay"; - }; - - mxc6655xa@15 { - status = "disabled"; - compatible = "gs_mxc6655xa"; - pinctrl-names = "default"; - pinctrl-0 = <0xfb>; - reg = <0x15>; - irq-gpio = <0x4a 0x11 0x08>; - irq_enable = <0x00>; - poll_delay_ms = <0x1e>; - type = <0x02>; - power-off-in-suspend = <0x01>; - layout = <0x01>; - }; - }; - - timer@fe5f0000 { - compatible = "rockchip,rk3568-timer\0rockchip,rk3288-timer"; - reg = <0x00 0xfe5f0000 0x00 0x1000>; - interrupts = <0x00 0x6d 0x04>; - clocks = <0x23 0x16c 0x23 0x16d>; - clock-names = "pclk\0timer"; - }; - - watchdog@fe600000 { - compatible = "snps,dw-wdt"; - reg = <0x00 0xfe600000 0x00 0x100>; - clocks = <0x23 0x116 0x23 0x115>; - clock-names = "tclk\0pclk"; - interrupts = <0x00 0x95 0x04>; - status = "okay"; - }; - - spi@fe610000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe610000 0x00 0x1000>; - interrupts = <0x00 0x67 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x152 0x23 0x151>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x14 0x4e 0x15>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0xfc 0xfd 0xfe>; - pinctrl-1 = <0xfc 0xfd 0xff>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@fe620000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe620000 0x00 0x1000>; - interrupts = <0x00 0x68 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x154 0x23 0x153>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x16 0x4e 0x17>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x100>; - pinctrl-1 = <0x101>; - num-cs = <0x02>; - status = "disabled"; - max-freq = <0x2dc6c00>; - dev-port = <0x00>; - - spi_wk2xxx@0 { - status = "disabled"; - compatible = "firefly,spi-wk2xxx"; - reg = <0x00>; - spi-max-frequency = <0x989680>; - power-gpio = <0x102 0x0f 0x00>; - reset-gpio = <0x102 0x09 0x00>; - irq-gpio = <0x3f 0x06 0x02>; - cs-gpio = <0x4a 0x01 0x00>; - }; - }; - - spi@fe630000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe630000 0x00 0x1000>; - interrupts = <0x00 0x69 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x156 0x23 0x155>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x18 0x4e 0x19>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x103 0x104 0x105>; - pinctrl-1 = <0x103 0x104 0x106>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@fe640000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe640000 0x00 0x1000>; - interrupts = <0x00 0x6a 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x158 0x23 0x157>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x1a 0x4e 0x1b>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x107 0x108 0x109>; - pinctrl-1 = <0x107 0x108 0x10a>; - num-cs = <0x02>; - status = "disabled"; - }; - - serial@fe650000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe650000 0x00 0x100>; - interrupts = <0x00 0x75 0x04>; - clocks = <0x23 0x11f 0x23 0x11c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x02 0x4e 0x03>; - pinctrl-names = "default"; - pinctrl-0 = <0x10b>; - status = "disabled"; - }; - - serial@fe660000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe660000 0x00 0x100>; - interrupts = <0x00 0x76 0x04>; - clocks = <0x23 0x123 0x23 0x120>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x04 0x4e 0x05>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "disabled"; - }; - - serial@fe670000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe670000 0x00 0x100>; - interrupts = <0x00 0x77 0x04>; - clocks = <0x23 0x127 0x23 0x124>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x06 0x4e 0x07>; - pinctrl-names = "default"; - pinctrl-0 = <0x10d>; - status = "okay"; - }; - - serial@fe680000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe680000 0x00 0x100>; - interrupts = <0x00 0x78 0x04>; - clocks = <0x23 0x12b 0x23 0x128>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x08 0x4e 0x09>; - pinctrl-names = "default"; - pinctrl-0 = <0x10e>; - status = "okay"; - }; - - serial@fe690000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe690000 0x00 0x100>; - interrupts = <0x00 0x79 0x04>; - clocks = <0x23 0x12f 0x23 0x12c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0a 0x4e 0x0b>; - pinctrl-names = "default"; - pinctrl-0 = <0x10f>; - status = "disabled"; - }; - - serial@fe6a0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6a0000 0x00 0x100>; - interrupts = <0x00 0x7a 0x04>; - clocks = <0x23 0x133 0x23 0x130>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0c 0x4e 0x0d>; - pinctrl-names = "default"; - pinctrl-0 = <0x110>; - status = "disabled"; - }; - - serial@fe6b0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6b0000 0x00 0x100>; - interrupts = <0x00 0x7b 0x04>; - clocks = <0x23 0x137 0x23 0x134>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0e 0x4e 0x0f>; - pinctrl-names = "default"; - pinctrl-0 = <0x111>; - status = "okay"; - }; - - serial@fe6c0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6c0000 0x00 0x100>; - interrupts = <0x00 0x7c 0x04>; - clocks = <0x23 0x13b 0x23 0x138>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x10 0x4e 0x11>; - pinctrl-names = "default"; - pinctrl-0 = <0x112 0x113>; - status = "okay"; - }; - - serial@fe6d0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6d0000 0x00 0x100>; - interrupts = <0x00 0x7d 0x04>; - clocks = <0x23 0x13f 0x23 0x13c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x12 0x4e 0x13>; - pinctrl-names = "default"; - pinctrl-0 = <0x114>; - status = "okay"; - }; - - pwm@fe6e0000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0000 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x115>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - }; - - pwm@fe6e0010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0010 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x116>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - }; - - pwm@fe6e0020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0020 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x117>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6e0030 { - compatible = "rockchip,remotectl-pwm"; - reg = <0x00 0xfe6e0030 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "default"; - pinctrl-0 = <0x118>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - remote_pwm_id = <0x03>; - handle_cpu_id = <0x01>; - remote_support_psci = <0x00>; - - ir_key_firefly { - rockchip,usercode = <0xff00>; - rockchip,key_table = <0xeb 0x74 0xec 0x8b 0xfe 0x9e 0xb7 0x66 0xa3 0x96 0xf4 0x73 0xa7 0x72 0xf8 0xe8 0xfc 0x67 0xfd 0x6c 0xf1 0x69 0xe5 0x6a>; - }; - }; - - pwm@fe6f0000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0000 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x119>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0010 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11a>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0020 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11b>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0030 0x00 0x10>; - interrupts = <0x00 0x54 0x04 0x00 0x58 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11c>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - phandle = <0x157>; - }; - - pwm@fe700000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700000 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11d>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700010 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11e>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700020 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11f>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700030 0x00 0x10>; - interrupts = <0x00 0x55 0x04 0x00 0x59 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x120>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - tsadc@fe710000 { - compatible = "rockchip,rk3568-tsadc"; - reg = <0x00 0xfe710000 0x00 0x100>; - interrupts = <0x00 0x73 0x04>; - rockchip,grf = <0x3b>; - clocks = <0x23 0x111 0x23 0x10f>; - clock-names = "tsadc\0apb_pclk"; - assigned-clocks = <0x23 0x110 0x23 0x111>; - assigned-clock-rates = <0x1036640 0xaae60>; - resets = <0x23 0x182 0x23 0x181 0x23 0x1d7>; - reset-names = "tsadc\0tsadc-apb\0tsadc-phy"; - #thermal-sensor-cells = <0x01>; - nvmem-cells = <0x121 0x122>; - nvmem-cell-names = "trim_base\0trim_base_frac"; - rockchip,hw-tshut-temp = <0x1d4c0>; - rockchip,hw-tshut-mode = <0x00>; - rockchip,hw-tshut-polarity = <0x00>; - pinctrl-names = "gpio\0otpout"; - pinctrl-0 = <0x123>; - pinctrl-1 = <0x124>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x1f>; - - tsadc@0 { - reg = <0x00>; - nvmem-cells = <0x125 0x126>; - nvmem-cell-names = "trim_l\0trim_h"; - }; - - tsadc@1 { - reg = <0x01>; - nvmem-cells = <0x127 0x128>; - nvmem-cell-names = "trim_l\0trim_h"; - }; - }; - - saradc@fe720000 { - compatible = "rockchip,rk3568-saradc\0rockchip,rk3399-saradc"; - reg = <0x00 0xfe720000 0x00 0x100>; - interrupts = <0x00 0x5d 0x04>; - #io-channel-cells = <0x01>; - clocks = <0x23 0x113 0x23 0x112>; - clock-names = "saradc\0apb_pclk"; - resets = <0x23 0x180>; - reset-names = "saradc-apb"; - status = "okay"; - vref-supply = <0x129>; - phandle = <0x49>; - }; - - mailbox@fe780000 { - compatible = "rockchip,rk3568-mailbox\0rockchip,rk3368-mailbox"; - reg = <0x00 0xfe780000 0x00 0x1000>; - interrupts = <0x00 0xb7 0x04 0x00 0xb8 0x04 0x00 0xb9 0x04 0x00 0xba 0x04>; - clocks = <0x23 0x11b>; - clock-names = "pclk_mailbox"; - #mbox-cells = <0x01>; - status = "disabled"; - }; - - phy@fe820000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe820000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x1f 0x23 0x17c 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x1f>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c4 0x23 0x1c5>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12b>; - status = "okay"; - phandle = <0x24>; - }; - - phy@fe830000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe830000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x22 0x23 0x17d 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x22>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c6 0x23 0x1c7>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12c>; - status = "okay"; - phandle = <0x26>; - }; - - phy@fe840000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe840000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x25 0x23 0x17e 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x25>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c8 0x23 0x1c9>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12d>; - status = "okay"; - phandle = <0x27>; - }; - - phy@fe850000 { - compatible = "rockchip,rk3568-dsi-dphy\0rockchip,rk3568-video-phy"; - reg = <0x00 0xfe850000 0x00 0x10000 0x00 0xfe060000 0x00 0x10000>; - reg-names = "phy\0host"; - clocks = <0x3a 0x17 0x23 0x17a 0x23 0xe8>; - clock-names = "ref\0pclk\0pclk_host"; - #clock-cells = <0x00>; - resets = <0x23 0x1bb>; - reset-names = "apb"; - power-domains = <0x25 0x09>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x34>; - }; - - phy@fe860000 { - compatible = "rockchip,rk3568-dsi-dphy\0rockchip,rk3568-video-phy"; - reg = <0x00 0xfe860000 0x00 0x10000 0x00 0xfe070000 0x00 0x10000>; - reg-names = "phy\0host"; - clocks = <0x3a 0x19 0x23 0x17b 0x23 0xe9>; - clock-names = "ref\0pclk\0pclk_host"; - #clock-cells = <0x00>; - resets = <0x23 0x1bc>; - reset-names = "apb"; - power-domains = <0x25 0x09>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x36>; - }; - - csi2-dphy-hw@fe870000 { - compatible = "rockchip,rk3568-csi2-dphy-hw"; - reg = <0x00 0xfe870000 0x00 0x1000>; - clocks = <0x23 0x179>; - clock-names = "pclk"; - rockchip,grf = <0x3b>; - status = "okay"; - phandle = <0x12e>; - }; - - csi2-dphy0 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x12f>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf6>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x130>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf5>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0x131>; - data-lanes = <0x01 0x02>; - phandle = <0xf7>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0x132>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf8>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0x133>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf9>; - }; - }; - - port@1 { - reg = <0x01>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x134>; - phandle = <0x8c>; - }; - }; - }; - }; - - csi2-dphy1 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "disabled"; - }; - - csi2-dphy2 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "disabled"; - }; - - usb2-phy@fe8a0000 { - compatible = "rockchip,rk3568-usb2phy"; - reg = <0x00 0xfe8a0000 0x00 0x10000>; - interrupts = <0x00 0x87 0x04>; - clocks = <0x3a 0x13>; - clock-names = "phyclk"; - #clock-cells = <0x00>; - assigned-clocks = <0x23 0x0b>; - assigned-clock-parents = <0x29>; - clock-output-names = "usb480m_phy"; - rockchip,usbgrf = <0x135>; - status = "okay"; - phandle = <0x29>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2b>; - }; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0x28>; - }; - }; - - usb2-phy@fe8b0000 { - compatible = "rockchip,rk3568-usb2phy"; - reg = <0x00 0xfe8b0000 0x00 0x10000>; - interrupts = <0x00 0x88 0x04>; - clocks = <0x3a 0x15>; - clock-names = "phyclk"; - #clock-cells = <0x00>; - rockchip,usbgrf = <0x137>; - status = "okay"; - phandle = <0x2c>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2e>; - }; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2d>; - }; - }; - - phy@fe8c0000 { - compatible = "rockchip,rk3568-pcie3-phy"; - reg = <0x00 0xfe8c0000 0x00 0x20000>; - #phy-cells = <0x00>; - clocks = <0x3a 0x26 0x3a 0x27 0x23 0x177>; - clock-names = "refclk_m\0refclk_n\0pclk"; - resets = <0x23 0x1be>; - reset-names = "phy"; - rockchip,phy-grf = <0x138>; - status = "okay"; - phandle = <0xc0>; - }; - - pinctrl { - compatible = "rockchip,rk3568-pinctrl"; - rockchip,grf = <0x3b>; - rockchip,pmu = <0x3c>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - gpio0@fdd60000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfdd60000 0x00 0x100>; - interrupts = <0x00 0x21 0x04>; - clocks = <0x3a 0x2e 0x3a 0x0c>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x3f>; - }; - - gpio1@fe740000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe740000 0x00 0x100>; - interrupts = <0x00 0x22 0x04>; - clocks = <0x23 0x163 0x23 0x164>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x159>; - }; - - gpio2@fe750000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe750000 0x00 0x100>; - interrupts = <0x00 0x23 0x04>; - clocks = <0x23 0x165 0x23 0x166>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x91>; - }; - - gpio3@fe760000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe760000 0x00 0x100>; - interrupts = <0x00 0x24 0x04>; - clocks = <0x23 0x167 0x23 0x168>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x4a>; - }; - - gpio4@fe770000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe770000 0x00 0x100>; - interrupts = <0x00 0x25 0x04>; - clocks = <0x23 0x169 0x23 0x16a>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0xf4>; - }; - - pcfg-pull-up { - bias-pull-up; - phandle = <0x13b>; - }; - - pcfg-pull-down { - bias-pull-down; - phandle = <0x142>; - }; - - pcfg-pull-none { - bias-disable; - phandle = <0x139>; - }; - - pcfg-pull-none-drv-level-1 { - bias-disable; - drive-strength = <0x01>; - phandle = <0x13d>; - }; - - pcfg-pull-none-drv-level-2 { - bias-disable; - drive-strength = <0x02>; - phandle = <0x13c>; - }; - - pcfg-pull-none-drv-level-3 { - bias-disable; - drive-strength = <0x03>; - phandle = <0x141>; - }; - - pcfg-pull-up-drv-level-1 { - bias-pull-up; - drive-strength = <0x01>; - phandle = <0x140>; - }; - - pcfg-pull-up-drv-level-2 { - bias-pull-up; - drive-strength = <0x02>; - phandle = <0x13a>; - }; - - pcfg-pull-none-smt { - bias-disable; - input-schmitt-enable; - phandle = <0x13e>; - }; - - pcfg-output-low-pull-down { - output-low; - bias-pull-down; - phandle = <0x13f>; - }; - - acodec { - - acodec-pins { - rockchip,pins = <0x01 0x09 0x05 0x139 0x01 0x01 0x05 0x139 0x01 0x00 0x05 0x139 0x01 0x07 0x05 0x139 0x01 0x08 0x05 0x139 0x01 0x03 0x05 0x139 0x01 0x05 0x05 0x139>; - phandle = <0xea>; - }; - }; - - cam { - - vcc-cam { - rockchip,pins = <0x00 0x11 0x00 0x139>; - phandle = <0x158>; - }; - }; - - can1 { - - can1m1-pins { - rockchip,pins = <0x04 0x12 0x03 0x139 0x04 0x13 0x03 0x139>; - phandle = <0xec>; - }; - }; - - can2 { - - can2m0-pins { - rockchip,pins = <0x04 0x0c 0x03 0x139 0x04 0x0d 0x03 0x139>; - phandle = <0xed>; - }; - }; - - cif { - - cif-clk { - rockchip,pins = <0x04 0x10 0x01 0x139>; - phandle = <0xf3>; - }; - }; - - clk32k { - - clk32k-out0 { - rockchip,pins = <0x00 0x08 0x02 0x139>; - phandle = <0x22>; - }; - }; - - ebc { - - ebc-pins { - rockchip,pins = <0x04 0x10 0x02 0x139 0x04 0x0b 0x02 0x139 0x04 0x0c 0x02 0x139 0x04 0x06 0x02 0x139 0x04 0x11 0x02 0x139 0x03 0x16 0x02 0x139 0x03 0x17 0x02 0x139 0x03 0x18 0x02 0x139 0x03 0x19 0x02 0x139 0x03 0x1a 0x02 0x139 0x03 0x1b 0x02 0x139 0x03 0x1c 0x02 0x139 0x03 0x1d 0x02 0x139 0x03 0x1e 0x02 0x139 0x03 0x1f 0x02 0x139 0x04 0x00 0x02 0x139 0x04 0x01 0x02 0x139 0x04 0x02 0x02 0x139 0x04 0x03 0x02 0x139 0x04 0x04 0x02 0x139 0x04 0x05 0x02 0x139 0x04 0x0e 0x02 0x139 0x04 0x0f 0x02 0x139>; - phandle = <0x7c>; - }; - }; - - fspi { - - fspi-pins { - rockchip,pins = <0x01 0x18 0x01 0x139 0x01 0x1b 0x01 0x139 0x01 0x19 0x01 0x139 0x01 0x1a 0x01 0x139 0x01 0x17 0x02 0x139 0x01 0x1c 0x01 0x139>; - phandle = <0xd4>; - }; - }; - - gmac0 { - - gmac0-miim { - rockchip,pins = <0x02 0x13 0x02 0x139 0x02 0x14 0x02 0x139>; - phandle = <0xc8>; - }; - - gmac0-clkinout { - rockchip,pins = <0x02 0x12 0x02 0x139>; - phandle = <0xcd>; - }; - - gmac0-rx-bus2 { - rockchip,pins = <0x02 0x0e 0x01 0x139 0x02 0x0f 0x02 0x139 0x02 0x10 0x02 0x139>; - phandle = <0xca>; - }; - - gmac0-tx-bus2 { - rockchip,pins = <0x02 0x0b 0x01 0x13c 0x02 0x0c 0x01 0x13c 0x02 0x0d 0x01 0x139>; - phandle = <0xc9>; - }; - - gmac0-rgmii-clk { - rockchip,pins = <0x02 0x05 0x02 0x139 0x02 0x08 0x02 0x13d>; - phandle = <0xcb>; - }; - - gmac0-rgmii-bus { - rockchip,pins = <0x02 0x03 0x02 0x139 0x02 0x04 0x02 0x139 0x02 0x06 0x02 0x13c 0x02 0x07 0x02 0x13c>; - phandle = <0xcc>; - }; - }; - - gmac1 { - - gmac1m1-miim { - rockchip,pins = <0x04 0x0e 0x03 0x139 0x04 0x0f 0x03 0x139>; - phandle = <0x93>; - }; - - gmac1m1-clkinout { - rockchip,pins = <0x04 0x11 0x03 0x139>; - phandle = <0x98>; - }; - - gmac1m1-rx-bus2 { - rockchip,pins = <0x04 0x07 0x03 0x139 0x04 0x08 0x03 0x139 0x04 0x09 0x03 0x139>; - phandle = <0x95>; - }; - - gmac1m1-tx-bus2 { - rockchip,pins = <0x04 0x04 0x03 0x13c 0x04 0x05 0x03 0x13c 0x04 0x06 0x03 0x139>; - phandle = <0x94>; - }; - - gmac1m1-rgmii-clk { - rockchip,pins = <0x04 0x03 0x03 0x139 0x04 0x00 0x03 0x13d>; - phandle = <0x96>; - }; - - gmac1m1-rgmii-bus { - rockchip,pins = <0x04 0x01 0x03 0x139 0x04 0x02 0x03 0x139 0x03 0x1e 0x03 0x13c 0x03 0x1f 0x03 0x13c>; - phandle = <0x97>; - }; - }; - - hdmitx { - - hdmitxm0-cec { - rockchip,pins = <0x04 0x19 0x01 0x139>; - phandle = <0xac>; - }; - - hdmitx-scl { - rockchip,pins = <0x04 0x17 0x01 0x139>; - phandle = <0xaa>; - }; - - hdmitx-sda { - rockchip,pins = <0x04 0x18 0x01 0x139>; - phandle = <0xab>; - }; - }; - - i2c0 { - - i2c0-xfer { - rockchip,pins = <0x00 0x09 0x01 0x13e 0x00 0x0a 0x01 0x13e>; - phandle = <0x3d>; - }; - }; - - i2c1 { - - i2c1-xfer { - rockchip,pins = <0x00 0x0b 0x01 0x13e 0x00 0x0c 0x01 0x13e>; - phandle = <0xee>; - }; - }; - - i2c2 { - - i2c2m0-xfer { - rockchip,pins = <0x00 0x0d 0x01 0x13e 0x00 0x0e 0x01 0x13e>; - phandle = <0xf0>; - }; - }; - - i2c3 { - - i2c3m0-xfer { - rockchip,pins = <0x01 0x01 0x01 0x13e 0x01 0x00 0x01 0x13e>; - phandle = <0xf1>; - }; - }; - - i2c4 { - - i2c4m0-xfer { - rockchip,pins = <0x04 0x0b 0x01 0x13e 0x04 0x0a 0x01 0x13e>; - phandle = <0xf2>; - }; - }; - - i2c5 { - - i2c5m0-xfer { - rockchip,pins = <0x03 0x0b 0x04 0x13e 0x03 0x0c 0x04 0x13e>; - phandle = <0xfa>; - }; - }; - - i2s1 { - - i2s1m0-lrcktx { - rockchip,pins = <0x01 0x05 0x01 0x13e>; - phandle = <0xd7>; - }; - - i2s1m0-mclk { - rockchip,pins = <0x01 0x02 0x01 0x13e>; - phandle = <0x47>; - }; - - i2s1m0-sclktx { - rockchip,pins = <0x01 0x03 0x01 0x13e>; - phandle = <0xd6>; - }; - - i2s1m0-sdi0 { - rockchip,pins = <0x01 0x0b 0x01 0x139>; - phandle = <0xd8>; - }; - - i2s1m0-sdo0 { - rockchip,pins = <0x01 0x07 0x01 0x139>; - phandle = <0xd9>; - }; - }; - - i2s2 { - - i2s2m0-lrcktx { - rockchip,pins = <0x02 0x13 0x01 0x13e>; - phandle = <0xdb>; - }; - - i2s2m0-sclktx { - rockchip,pins = <0x02 0x12 0x01 0x13e>; - phandle = <0xda>; - }; - - i2s2m0-sdi { - rockchip,pins = <0x02 0x15 0x01 0x139>; - phandle = <0xdc>; - }; - - i2s2m0-sdo { - rockchip,pins = <0x02 0x14 0x01 0x139>; - phandle = <0xdd>; - }; - }; - - i2s3 { - - i2s3m0-lrck { - rockchip,pins = <0x03 0x04 0x04 0x13e>; - phandle = <0xdf>; - }; - - i2s3m0-sclk { - rockchip,pins = <0x03 0x03 0x04 0x13e>; - phandle = <0xde>; - }; - - i2s3m0-sdi { - rockchip,pins = <0x03 0x06 0x04 0x139>; - phandle = <0xe0>; - }; - - i2s3m0-sdo { - rockchip,pins = <0x03 0x05 0x04 0x139>; - phandle = <0xe1>; - }; - }; - - lcdc { - - lcdc-ctl { - rockchip,pins = <0x03 0x00 0x01 0x139 0x02 0x18 0x01 0x139 0x02 0x19 0x01 0x139 0x02 0x1a 0x01 0x139 0x02 0x1b 0x01 0x139 0x02 0x1c 0x01 0x139 0x02 0x1d 0x01 0x139 0x02 0x1e 0x01 0x139 0x02 0x1f 0x01 0x139 0x03 0x01 0x01 0x139 0x03 0x02 0x01 0x139 0x03 0x03 0x01 0x139 0x03 0x04 0x01 0x139 0x03 0x05 0x01 0x139 0x03 0x06 0x01 0x139 0x03 0x07 0x01 0x139 0x03 0x08 0x01 0x139 0x03 0x09 0x01 0x139 0x03 0x0a 0x01 0x139 0x03 0x0b 0x01 0x139 0x03 0x0c 0x01 0x139 0x03 0x0d 0x01 0x139 0x03 0x0e 0x01 0x139 0x03 0x0f 0x01 0x139 0x03 0x10 0x01 0x139 0x03 0x13 0x01 0x139 0x03 0x11 0x01 0x139 0x03 0x12 0x01 0x139>; - phandle = <0x39>; - }; - }; - - pdm { - - pdmm0-clk { - rockchip,pins = <0x01 0x06 0x03 0x139>; - phandle = <0xe2>; - }; - - pdmm0-clk1 { - rockchip,pins = <0x01 0x04 0x03 0x139>; - phandle = <0xe3>; - }; - - pdmm0-sdi0 { - rockchip,pins = <0x01 0x0b 0x02 0x139>; - phandle = <0xe4>; - }; - - pdmm0-sdi1 { - rockchip,pins = <0x01 0x0a 0x03 0x139>; - phandle = <0xe5>; - }; - - pdmm0-sdi2 { - rockchip,pins = <0x01 0x09 0x03 0x139>; - phandle = <0xe6>; - }; - - pdmm0-sdi3 { - rockchip,pins = <0x01 0x08 0x03 0x139>; - phandle = <0xe7>; - }; - }; - - pmic { - - pmic_int { - rockchip,pins = <0x00 0x03 0x00 0x13b>; - phandle = <0x40>; - }; - - soc_slppin_gpio { - rockchip,pins = <0x00 0x02 0x00 0x13f>; - phandle = <0x43>; - }; - - soc_slppin_slp { - rockchip,pins = <0x00 0x02 0x01 0x13b>; - phandle = <0x41>; - }; - - soc_slppin_rst { - rockchip,pins = <0x00 0x02 0x02 0x139>; - }; - - spk_ctl_gpio { - rockchip,pins = <0x03 0x15 0x00 0x13b>; - phandle = <0x48>; - }; - }; - - pwm0 { - - pwm0m0-pins { - rockchip,pins = <0x00 0x0f 0x01 0x139>; - phandle = <0x50>; - }; - }; - - pwm1 { - - pwm1m0-pins { - rockchip,pins = <0x00 0x10 0x01 0x139>; - phandle = <0x51>; - }; - }; - - pwm2 { - - pwm2m0-pins { - rockchip,pins = <0x00 0x11 0x01 0x139>; - phandle = <0x52>; - }; - }; - - pwm3 { - - pwm3-pins { - rockchip,pins = <0x00 0x12 0x01 0x139>; - phandle = <0x53>; - }; - }; - - pwm4 { - - pwm4-pins { - rockchip,pins = <0x00 0x13 0x01 0x139>; - phandle = <0x115>; - }; - }; - - pwm5 { - - pwm5-pins { - rockchip,pins = <0x00 0x14 0x01 0x139>; - phandle = <0x116>; - }; - }; - - pwm6 { - - pwm6-pins { - rockchip,pins = <0x00 0x15 0x01 0x139>; - phandle = <0x117>; - }; - }; - - pwm7 { - - pwm7-pins { - rockchip,pins = <0x00 0x16 0x01 0x139>; - phandle = <0x118>; - }; - }; - - pwm8 { - - pwm8m0-pins { - rockchip,pins = <0x03 0x09 0x05 0x139>; - phandle = <0x119>; - }; - }; - - pwm9 { - - pwm9m0-pins { - rockchip,pins = <0x03 0x0a 0x05 0x139>; - phandle = <0x11a>; - }; - }; - - pwm10 { - - pwm10m0-pins { - rockchip,pins = <0x03 0x0d 0x05 0x139>; - phandle = <0x11b>; - }; - }; - - pwm11 { - - pwm11m0-pins { - rockchip,pins = <0x03 0x0e 0x05 0x139>; - phandle = <0x11c>; - }; - }; - - pwm12 { - - pwm12m0-pins { - rockchip,pins = <0x03 0x0f 0x02 0x139>; - phandle = <0x11d>; - }; - }; - - pwm13 { - - pwm13m0-pins { - rockchip,pins = <0x03 0x10 0x02 0x139>; - phandle = <0x11e>; - }; - }; - - pwm14 { - - pwm14m0-pins { - rockchip,pins = <0x03 0x14 0x01 0x139>; - phandle = <0x11f>; - }; - }; - - pwm15 { - - pwm15m0-pins { - rockchip,pins = <0x03 0x15 0x01 0x139>; - phandle = <0x120>; - }; - }; - - scr { - - scr-pins { - rockchip,pins = <0x01 0x02 0x03 0x139 0x01 0x07 0x03 0x13b 0x01 0x03 0x03 0x13b 0x01 0x05 0x03 0x139>; - phandle = <0xeb>; - }; - }; - - sdmmc0 { - - sdmmc0-bus4 { - rockchip,pins = <0x01 0x1d 0x01 0x13a 0x01 0x1e 0x01 0x13a 0x01 0x1f 0x01 0x13a 0x02 0x00 0x01 0x13a>; - phandle = <0xd0>; - }; - - sdmmc0-clk { - rockchip,pins = <0x02 0x02 0x01 0x13a>; - phandle = <0xd1>; - }; - - sdmmc0-cmd { - rockchip,pins = <0x02 0x01 0x01 0x13a>; - phandle = <0xd2>; - }; - - sdmmc0-det { - rockchip,pins = <0x00 0x04 0x01 0x13b>; - phandle = <0xd3>; - }; - }; - - sdmmc2 { - - sdmmc2m0-bus4 { - rockchip,pins = <0x03 0x16 0x03 0x13a 0x03 0x17 0x03 0x13a 0x03 0x18 0x03 0x13a 0x03 0x19 0x03 0x13a>; - phandle = <0xb0>; - }; - - sdmmc2m0-clk { - rockchip,pins = <0x03 0x1b 0x03 0x13a>; - phandle = <0xb2>; - }; - - sdmmc2m0-cmd { - rockchip,pins = <0x03 0x1a 0x03 0x13a>; - phandle = <0xb1>; - }; - }; - - spdif { - - spdifm1-tx { - rockchip,pins = <0x03 0x15 0x02 0x139>; - phandle = <0xe9>; - }; - }; - - spi0 { - - spi0m0-pins { - rockchip,pins = <0x00 0x0d 0x02 0x139 0x00 0x15 0x02 0x139 0x00 0x0e 0x02 0x139>; - phandle = <0xfe>; - }; - - spi0m0-cs0 { - rockchip,pins = <0x00 0x16 0x02 0x139>; - phandle = <0xfc>; - }; - - spi0m0-cs1 { - rockchip,pins = <0x00 0x14 0x02 0x139>; - phandle = <0xfd>; - }; - }; - - spi1 { - - spi1m1-pins { - rockchip,pins = <0x03 0x13 0x03 0x139 0x03 0x12 0x03 0x139 0x03 0x11 0x03 0x139>; - phandle = <0x100>; - }; - }; - - spi2 { - - spi2m0-pins { - rockchip,pins = <0x02 0x11 0x04 0x139 0x02 0x12 0x04 0x139 0x02 0x13 0x04 0x139>; - phandle = <0x105>; - }; - - spi2m0-cs0 { - rockchip,pins = <0x02 0x14 0x04 0x139>; - phandle = <0x103>; - }; - - spi2m0-cs1 { - rockchip,pins = <0x02 0x15 0x04 0x139>; - phandle = <0x104>; - }; - }; - - spi3 { - - spi3m0-pins { - rockchip,pins = <0x04 0x0b 0x04 0x139 0x04 0x08 0x04 0x139 0x04 0x0a 0x04 0x139>; - phandle = <0x109>; - }; - - spi3m0-cs0 { - rockchip,pins = <0x04 0x06 0x04 0x139>; - phandle = <0x107>; - }; - - spi3m0-cs1 { - rockchip,pins = <0x04 0x07 0x04 0x139>; - phandle = <0x108>; - }; - }; - - tsadc { - - tsadc-shutorg { - rockchip,pins = <0x00 0x01 0x02 0x139>; - phandle = <0x124>; - }; - }; - - uart0 { - - uart0-xfer { - rockchip,pins = <0x00 0x10 0x03 0x13b 0x00 0x11 0x03 0x13b>; - phandle = <0x4f>; - }; - }; - - uart1 { - - uart1m0-xfer { - rockchip,pins = <0x02 0x0b 0x02 0x13b 0x02 0x0c 0x02 0x13b>; - phandle = <0x10b>; - }; - }; - - uart2 { - - uart2m0-xfer { - rockchip,pins = <0x00 0x18 0x01 0x13b 0x00 0x19 0x01 0x13b>; - phandle = <0x10c>; - }; - }; - - uart3 { - - uart3m1-xfer { - rockchip,pins = <0x03 0x10 0x04 0x13b 0x03 0x0f 0x04 0x13b>; - phandle = <0x10d>; - }; - }; - - uart4 { - - uart4m1-xfer { - rockchip,pins = <0x03 0x09 0x04 0x13b 0x03 0x0a 0x04 0x13b>; - phandle = <0x10e>; - }; - }; - - uart5 { - - uart5m0-xfer { - rockchip,pins = <0x02 0x01 0x03 0x13b 0x02 0x02 0x03 0x13b>; - phandle = <0x10f>; - }; - }; - - uart6 { - - uart6m0-xfer { - rockchip,pins = <0x02 0x03 0x03 0x13b 0x02 0x04 0x03 0x13b>; - phandle = <0x110>; - }; - }; - - uart7 { - - uart7m1-xfer { - rockchip,pins = <0x03 0x15 0x04 0x13b 0x03 0x14 0x04 0x13b>; - phandle = <0x111>; - }; - }; - - uart8 { - - uart8m0-xfer { - rockchip,pins = <0x02 0x16 0x02 0x13b 0x02 0x15 0x03 0x13b>; - phandle = <0x112>; - }; - - uart8m0-ctsn { - rockchip,pins = <0x02 0x0a 0x03 0x139>; - phandle = <0x113>; - }; - - uart8m0-rtsn { - rockchip,pins = <0x02 0x09 0x03 0x139>; - phandle = <0x155>; - }; - }; - - uart9 { - - uart9m1-xfer { - rockchip,pins = <0x04 0x16 0x04 0x13b 0x04 0x15 0x04 0x13b>; - phandle = <0x114>; - }; - }; - - spi0-hs { - - spi0m0-pins { - rockchip,pins = <0x00 0x0d 0x02 0x140 0x00 0x15 0x02 0x140 0x00 0x0e 0x02 0x140>; - phandle = <0xff>; - }; - }; - - spi1-hs { - - spi1m1-pins { - rockchip,pins = <0x03 0x13 0x03 0x140 0x03 0x12 0x03 0x140 0x03 0x11 0x03 0x140>; - phandle = <0x101>; - }; - }; - - spi2-hs { - - spi2m0-pins { - rockchip,pins = <0x02 0x11 0x04 0x140 0x02 0x12 0x04 0x140 0x02 0x13 0x04 0x140>; - phandle = <0x106>; - }; - }; - - spi3-hs { - - spi3m0-pins { - rockchip,pins = <0x04 0x0b 0x04 0x140 0x04 0x08 0x04 0x140 0x04 0x0a 0x04 0x140>; - phandle = <0x10a>; - }; - }; - - gpio-func { - - tsadc-gpio-func { - rockchip,pins = <0x00 0x01 0x00 0x139>; - phandle = <0x123>; - }; - }; - - usb { - - vcc5v0-host-en { - rockchip,pins = <0x00 0x06 0x00 0x139>; - phandle = <0x150>; - }; - - vcc5v0-otg-en { - rockchip,pins = <0x00 0x05 0x00 0x139>; - phandle = <0x151>; - }; - - vcc-hub-reset-en { - rockchip,pins = <0x01 0x04 0x00 0x139>; - phandle = <0x15a>; - }; - }; - - headphone { - - hp-det { - rockchip,pins = <0x03 0x12 0x00 0x142>; - phandle = <0x148>; - }; - }; - - sdio-pwrseq { - - wifi-enable-h { - rockchip,pins = <0x03 0x1d 0x00 0x139>; - phandle = <0x153>; - }; - }; - - wireless-wlan { - - wifi-host-wake-irq { - rockchip,pins = <0x03 0x1c 0x00 0x142>; - phandle = <0x154>; - }; - }; - - wireless-bluetooth { - - uart8-gpios { - rockchip,pins = <0x02 0x09 0x00 0x139>; - phandle = <0x156>; - }; - }; - - touch { - - touch-gpio { - rockchip,pins = <0x00 0x0d 0x00 0x13b 0x00 0x0e 0x00 0x139>; - phandle = <0xef>; - }; - }; - - mxc6655xa { - - mxc6655xa_irq_gpio { - rockchip,pins = <0x03 0x11 0x00 0x139>; - phandle = <0xfb>; - }; - }; - - pcie { - - pcie-pi6c-oe-en { - rockchip,pins = <0x03 0x07 0x00 0x139>; - phandle = <0x15b>; - }; - }; - - leds { - - leds-gpio { - rockchip,pins = <0x01 0x0a 0x00 0x139 0x01 0x09 0x00 0x139 0x01 0x08 0x00 0x139 0x02 0x11 0x00 0x139>; - phandle = <0x15d>; - }; - }; - - 4g { - - vcc-4g-power-en { - rockchip,pins = <0x03 0x03 0x00 0x139>; - phandle = <0x15c>; - }; - }; - - usb-typec { - - usbc0-int { - rockchip,pins = <0x00 0x11 0x00 0x13b>; - phandle = <0x4b>; - }; - - vcc5v0-typec0-en { - rockchip,pins = <0x00 0x05 0x00 0x139>; - }; - }; - }; - - audiopwmout-diff { - status = "disabled"; - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "rockchip,audiopwmout-diff"; - simple-audio-card,mclk-fs = <0x100>; - simple-audio-card,bitclock-master = <0x143>; - simple-audio-card,frame-master = <0x143>; - - simple-audio-card,cpu { - sound-dai = <0x144>; - }; - - simple-audio-card,codec { - sound-dai = <0x145>; - phandle = <0x143>; - }; - }; - - dc-12v { - compatible = "regulator-fixed"; - regulator-name = "dc_12v"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b00>; - regulator-max-microvolt = <0xb71b00>; - phandle = <0x14f>; - }; - - hdmi-sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,mclk-fs = <0x80>; - simple-audio-card,name = "rockchip,hdmi"; - status = "okay"; - - simple-audio-card,cpu { - sound-dai = <0x146>; - }; - - simple-audio-card,codec { - sound-dai = <0x147>; - }; - }; - - rk-headset { - status = "disabled"; - compatible = "rockchip_headset"; - headset_gpio = <0x4a 0x12 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x148>; - }; - - dummy-codec { - status = "disabled"; - compatible = "rockchip,dummy-codec"; - #sound-dai-cells = <0x00>; - phandle = <0x14a>; - }; - - pdm-mic-array { - status = "disabled"; - compatible = "simple-audio-card"; - simple-audio-card,name = "rockchip,pdm-mic-array"; - - simple-audio-card,cpu { - sound-dai = <0x149>; - }; - - simple-audio-card,codec { - sound-dai = <0x14a>; - }; - }; - - rk809-sound { - status = "okay"; - compatible = "rockchip,multicodecs-card"; - rockchip,card-name = "rockchip-rk809"; - rockchip,format = "i2s"; - rockchip,mclk-fs = <0x100>; - rockchip,cpu = <0xe8>; - rockchip,codec = <0x14b>; - }; - - spdif-sound { - status = "okay"; - compatible = "simple-audio-card"; - simple-audio-card,name = "ROCKCHIP,SPDIF"; - - simple-audio-card,cpu { - sound-dai = <0x14c>; - }; - - simple-audio-card,codec { - sound-dai = <0x14d>; - }; - }; - - spdif-out { - status = "okay"; - compatible = "linux,spdif-dit"; - #sound-dai-cells = <0x00>; - phandle = <0x14d>; - }; - - vad-sound { - status = "disabled"; - compatible = "rockchip,multicodecs-card"; - rockchip,card-name = "rockchip,rk3568-vad"; - rockchip,cpu = <0xe8>; - rockchip,codec = <0x14b 0x14e>; - }; - - vcc3v3-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - vin-supply = <0x14f>; - phandle = <0x46>; - }; - - vcc5v0-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - vin-supply = <0x14f>; - phandle = <0x3e>; - }; - - vcc5v0-host-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x3f 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x150>; - regulator-name = "vcc5v0_host"; - regulator-always-on; - regulator-boot-on; - phandle = <0x136>; - }; - - vcc5v0-otg-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x3f 0x05 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x151>; - regulator-name = "vcc5v0_otg"; - phandle = <0x4c>; - }; - - vcc3v3-lcd0-n { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_lcd0_n"; - regulator-boot-on; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc3v3-lcd1-n { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_lcd1_n"; - regulator-boot-on; - status = "disabled"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - test-power { - status = "okay"; - }; - - chosen { - // linux,initrd-end = <0x00 0xaacf15d>; - // linux,initrd-start = <0x00 0xa200000>; - bootargs = "storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal androidboot.verifiedbootstate=orange rw rootwait earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 root=PARTLABEL=rootfs rootfstype=ext4 overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 swiotlb=0x10000 net.ifnames=0 comm-05/20/2025 androidboot.fwver=ddr-v1.21-2d653b3476,spl-v1.14,bl31-v1.44,bl32-v2.12,uboot--boot"; - }; - - fiq-debugger { - compatible = "rockchip,fiq-debugger"; - rockchip,serial-id = <0x02>; - rockchip,wake-irq = <0x00>; - rockchip,irq-mode-enable = <0x01>; - rockchip,baudrate = <0x16e360>; - interrupts = <0x00 0xfc 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "okay"; - }; - - debug@fd904000 { - compatible = "rockchip,debug"; - reg = <0x00 0xfd904000 0x00 0x1000 0x00 0xfd905000 0x00 0x1000 0x00 0xfd906000 0x00 0x1000 0x00 0xfd907000 0x00 0x1000>; - }; - - cspmu@fd90c000 { - compatible = "rockchip,cspmu"; - reg = <0x00 0xfd90c000 0x00 0x1000 0x00 0xfd90d000 0x00 0x1000 0x00 0xfd90e000 0x00 0x1000 0x00 0xfd90f000 0x00 0x1000>; - }; - - adc-keys { - compatible = "adc-keys"; - io-channels = <0x49 0x00>; - io-channel-names = "buttons"; - keyup-threshold-microvolt = <0x1b7740>; - poll-interval = <0x64>; - - recovery-key { - label = "F12"; - linux,code = <0x58>; - press-threshold-microvolt = <0x6d6>; - }; - - vol-down-key { - label = "volume down"; - linux,code = <0x72>; - press-threshold-microvolt = <0x48a1c>; - }; - - menu-key { - label = "menu"; - linux,code = <0x8b>; - press-threshold-microvolt = <0xef420>; - }; - - back-key { - label = "back"; - linux,code = <0x9e>; - press-threshold-microvolt = <0x13eb9c>; - }; - }; - - vcc2v5-ddr { - compatible = "regulator-fixed"; - regulator-name = "vcc2v5-sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x2625a0>; - regulator-max-microvolt = <0x2625a0>; - vin-supply = <0x46>; - }; - - pcie30-avdd0v9 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd0v9"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - vin-supply = <0x46>; - }; - - pcie30-avdd1v8 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd1v8"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - vin-supply = <0x46>; - }; - - gpio-regulator { - compatible = "regulator-gpio"; - regulator-name = "pcie30_3v3"; - regulator-min-microvolt = <0x186a0>; - regulator-max-microvolt = <0x325aa0>; - gpios = <0x3f 0x1c 0x00>; - gpios-states = <0x01>; - states = <0x186a0 0x00 0x325aa0 0x01>; - phandle = <0xc2>; - }; - - vcc3v3-bu { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_bu"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - vin-supply = <0x3e>; - }; - - sdio-pwrseq { - compatible = "mmc-pwrseq-simple"; - clocks = <0x152 0x01>; - clock-names = "ext_clock"; - pinctrl-names = "default"; - pinctrl-0 = <0x153>; - post-power-on-delay-ms = <0x64>; - reset-gpios = <0x4a 0x1d 0x01>; - status = "okay"; - phandle = <0xb3>; - }; - - wireless-wlan { - compatible = "wlan-platdata"; - rockchip,grf = <0x3b>; - wifi_chip_type = "ap6256"; - pinctrl-names = "default"; - pinctrl-0 = <0x154>; - WIFI,host_wake_irq = <0x4a 0x1c 0x00>; - status = "okay"; - }; - - wireless-bluetooth { - compatible = "bluetooth-platdata"; - clocks = <0x152 0x01>; - clock-names = "ext_clock"; - uart_rts_gpios = <0x91 0x09 0x01>; - pinctrl-names = "default\0rts_gpio"; - pinctrl-0 = <0x155>; - pinctrl-1 = <0x156>; - BT,reset_gpio = <0x4a 0x00 0x00>; - BT,wake_gpio = <0x4a 0x02 0x00>; - BT,wake_host_irq = <0x4a 0x01 0x00>; - status = "okay"; - }; - - flash-led { - compatible = "led,rgb13h"; - label = "pwm-flash-led"; - led-max-microamp = <0x4e20>; - flash-max-microamp = <0x4e20>; - flash-max-timeout-us = <0xf4240>; - pwms = <0x157 0x00 0x61a8 0x00>; - rockchip,camera-module-index = <0x01>; - rockchip,camera-module-facing = "front"; - status = "disabled"; - }; - - vcc-camera-regulator { - compatible = "regulator-fixed"; - gpio = <0x102 0x03 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x158>; - regulator-name = "vcc_camera"; - enable-active-high; - status = "disabled"; - }; - - vcc-hub-reset-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x159 0x04 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x15a>; - regulator-name = "vcc_hub_reset_en"; - regulator-always-on; - }; - - pcie-pi6c-oe-regulator { - compatible = "regulator-fixed"; - gpio = <0x4a 0x07 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x15b>; - regulator-name = "pcie_pi6c_oe_en"; - regulator-always-on; - }; - - vcc-4g-power-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x4a 0x03 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x15c>; - regulator-name = "vcc_4g_power_en"; - regulator-always-on; - }; - - leds { - status = "okay"; - compatible = "gpio-leds"; - pinctrl-names = "default"; - pinctrl-0 = <0x15d>; - - power { - label = "firefly:blue:power"; - linux,default-trigger = "ir-power-click"; - default-state = "on"; - gpios = <0x159 0x0a 0x00>; - }; - - user { - label = "firefly:yellow:user"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x159 0x09 0x00>; - }; - - diy1 { - label = "firefly:green:diy"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x159 0x08 0x00>; - }; - - diy2 { - label = "firefly:yellow:diy"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x91 0x11 0x00>; - }; - }; -}; \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.toml b/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.toml deleted file mode 100644 index ed0cf421f..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp1.toml +++ /dev/null @@ -1,62 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 2 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x00] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8008_0000 -# The location of image: "memory" | "fs". -# Load from memory. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x8008_0000 -## The file path of the kernel image. -kernel_path = "/userdata/rootfs_overlay/guest/linux/roc-rk3568-pc" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x8000_0000 -#dtb_path = "/path/linux-aarch64-rk3568_smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x6000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] -# -# Device specifications -# -[devices] -# The interrupt mode. -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.dts b/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.dts deleted file mode 100644 index 5475dbe6e..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.dts +++ /dev/null @@ -1,6108 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000008300000 0x000000000001c000; -/memreserve/ 0x000000000a200000 0x00000000008cf15d; -/ { - serial-number = "425ca8fc29ade692"; - compatible = "rockchip,rk3568-firefly-roc-pc-se\0rockchip,rk3568"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Firefly RK3568-ROC-PC-SE HDMI (Linux)"; - - memory { - reg = <0x00 0x80000000 0x00 0x60000000>; - device_type = "memory"; - }; - - ddr3-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x12c>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x21>; - phy_ca_drv_odten = <0x21>; - phy_clk_drv_odten = <0x21>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x21>; - phy_ca_drv_odtoff = <0x21>; - phy_clk_drv_odtoff = <0x21>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0xa7>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x14d>; - phy_odt_en_freq = <0x14d>; - phy_dq_sr_odten = <0x0f>; - phy_ca_sr_odten = <0x03>; - phy_clk_sr_odten = <0x00>; - phy_dq_sr_odtoff = <0x0f>; - phy_ca_sr_odtoff = <0x03>; - phy_clk_sr_odtoff = <0x00>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x15>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - phandle = <0xb7>; - }; - - ddr4-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x271>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x25>; - phy_ca_drv_odten = <0x25>; - phy_clk_drv_odten = <0x25>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x25>; - phy_ca_drv_odtoff = <0x25>; - phy_clk_drv_odtoff = <0x25>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0x8b>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x1f4>; - phy_odt_en_freq = <0x1f4>; - phy_dq_sr_odten = <0x0e>; - phy_ca_sr_odten = <0x01>; - phy_clk_sr_odten = <0x01>; - phy_dq_sr_odtoff = <0x0e>; - phy_ca_sr_odtoff = <0x01>; - phy_clk_sr_odtoff = <0x01>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x0c>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x22777788>; - dq_map_cs0_dq_h = <0xd7888877>; - dq_map_cs1_dq_l = <0x22777788>; - dq_map_cs1_dq_h = <0xd7888877>; - phandle = <0xb8>; - }; - - lpddr3-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x420>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x25>; - phy_ca_drv_odten = <0x25>; - phy_clk_drv_odten = <0x27>; - dram_dq_drv_odten = <0x22>; - phy_dq_drv_odtoff = <0x25>; - phy_ca_drv_odtoff = <0x25>; - phy_clk_drv_odtoff = <0x27>; - dram_dq_drv_odtoff = <0x22>; - dram_odt = <0x78>; - phy_odt = <0x94>; - phy_odt_puup_en = <0x01>; - phy_odt_pudn_en = <0x01>; - dram_dq_odt_en_freq = <0x14d>; - phy_odt_en_freq = <0x14d>; - phy_dq_sr_odten = <0x0f>; - phy_ca_sr_odten = <0x01>; - phy_clk_sr_odten = <0x0f>; - phy_dq_sr_odtoff = <0x0f>; - phy_ca_sr_odtoff = <0x01>; - phy_clk_sr_odtoff = <0x0f>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0x8d>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - phandle = <0xb9>; - }; - - lpddr4-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x618>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x1e>; - phy_ca_drv_odten = <0x26>; - phy_clk_drv_odten = <0x26>; - dram_dq_drv_odten = <0x28>; - phy_dq_drv_odtoff = <0x1e>; - phy_ca_drv_odtoff = <0x26>; - phy_clk_drv_odtoff = <0x26>; - dram_dq_drv_odtoff = <0x28>; - dram_odt = <0x50>; - phy_odt = <0x3c>; - phy_odt_puup_en = <0x00>; - phy_odt_pudn_en = <0x00>; - dram_dq_odt_en_freq = <0x320>; - phy_odt_en_freq = <0x320>; - phy_dq_sr_odten = <0x00>; - phy_ca_sr_odten = <0x0f>; - phy_clk_sr_odten = <0x0f>; - phy_dq_sr_odtoff = <0x00>; - phy_ca_sr_odtoff = <0x0f>; - phy_clk_sr_odtoff = <0x0f>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - lp4_ca_odt = <0x78>; - lp4_drv_pu_cal_odten = <0x01>; - lp4_drv_pu_cal_odtoff = <0x01>; - phy_lp4_drv_pulldown_en_odten = <0x00>; - phy_lp4_drv_pulldown_en_odtoff = <0x00>; - lp4_ca_odt_en_freq = <0x320>; - phy_lp4_cs_drv_odten = <0x00>; - phy_lp4_cs_drv_odtoff = <0x00>; - lp4_odte_ck_en = <0x01>; - lp4_odte_cs_en = <0x01>; - lp4_odtd_ca_en = <0x00>; - phy_lp4_dq_vref_odten = <0xa6>; - lp4_dq_vref_odten = <0x12c>; - lp4_ca_vref_odten = <0x17c>; - phy_lp4_dq_vref_odtoff = <0x1a4>; - lp4_dq_vref_odtoff = <0x1a4>; - lp4_ca_vref_odtoff = <0x1a4>; - phandle = <0xba>; - }; - - lpddr4x-params { - version = <0x100>; - expanded_version = <0x00>; - reserved = <0x00>; - freq_0 = <0x618>; - freq_1 = <0x144>; - freq_2 = <0x210>; - freq_3 = <0x30c>; - freq_4 = <0x00>; - freq_5 = <0x00>; - pd_idle = <0x0d>; - sr_idle = <0x5d>; - sr_mc_gate_idle = <0x00>; - srpd_lite_idle = <0x00>; - standby_idle = <0x00>; - pd_dis_freq = <0x42a>; - sr_dis_freq = <0x320>; - dram_dll_dis_freq = <0x00>; - phy_dll_dis_freq = <0x00>; - phy_dq_drv_odten = <0x1d>; - phy_ca_drv_odten = <0x24>; - phy_clk_drv_odten = <0x24>; - dram_dq_drv_odten = <0x28>; - phy_dq_drv_odtoff = <0x1d>; - phy_ca_drv_odtoff = <0x24>; - phy_clk_drv_odtoff = <0x24>; - dram_dq_drv_odtoff = <0x28>; - dram_odt = <0x50>; - phy_odt = <0x3c>; - phy_odt_puup_en = <0x00>; - phy_odt_pudn_en = <0x00>; - dram_dq_odt_en_freq = <0x320>; - phy_odt_en_freq = <0x320>; - phy_dq_sr_odten = <0x00>; - phy_ca_sr_odten = <0x00>; - phy_clk_sr_odten = <0x00>; - phy_dq_sr_odtoff = <0x00>; - phy_ca_sr_odtoff = <0x00>; - phy_clk_sr_odtoff = <0x00>; - ssmod_downspread = <0x00>; - ssmod_div = <0x00>; - ssmod_spread = <0x00>; - mode_2t = <0x00>; - speed_bin = <0x00>; - dram_ext_temp = <0x00>; - byte_map = <0xe4>; - dq_map_cs0_dq_l = <0x00>; - dq_map_cs0_dq_h = <0x00>; - dq_map_cs1_dq_l = <0x00>; - dq_map_cs1_dq_h = <0x00>; - lp4_ca_odt = <0x78>; - lp4_drv_pu_cal_odten = <0x00>; - lp4_drv_pu_cal_odtoff = <0x00>; - phy_lp4_drv_pulldown_en_odten = <0x00>; - phy_lp4_drv_pulldown_en_odtoff = <0x00>; - lp4_ca_odt_en_freq = <0x320>; - phy_lp4_cs_drv_odten = <0x00>; - phy_lp4_cs_drv_odtoff = <0x00>; - lp4_odte_ck_en = <0x00>; - lp4_odte_cs_en = <0x00>; - lp4_odtd_ca_en = <0x00>; - phy_lp4_dq_vref_odten = <0xa6>; - lp4_dq_vref_odten = <0xe4>; - lp4_ca_vref_odten = <0x157>; - phy_lp4_dq_vref_odtoff = <0x1a4>; - lp4_dq_vref_odtoff = <0x1a4>; - lp4_ca_vref_odtoff = <0x157>; - phandle = <0xbb>; - }; - - aliases { - csi2dphy0 = "/csi2-dphy0"; - csi2dphy1 = "/csi2-dphy1"; - csi2dphy2 = "/csi2-dphy2"; - dsi0 = "/dsi@fe060000"; - dsi1 = "/dsi@fe070000"; - ethernet0 = "/ethernet@fe2a0000"; - ethernet1 = "/ethernet@fe010000"; - gpio0 = "/pinctrl/gpio0@fdd60000"; - gpio1 = "/pinctrl/gpio1@fe740000"; - gpio2 = "/pinctrl/gpio2@fe750000"; - gpio3 = "/pinctrl/gpio3@fe760000"; - gpio4 = "/pinctrl/gpio4@fe770000"; - i2c0 = "/i2c@fdd40000"; - i2c1 = "/i2c@fe5a0000"; - i2c2 = "/i2c@fe5b0000"; - i2c3 = "/i2c@fe5c0000"; - i2c4 = "/i2c@fe5d0000"; - i2c5 = "/i2c@fe5e0000"; - mmc0 = "/sdhci@fe310000"; - mmc1 = "/dwmmc@fe2b0000"; - mmc2 = "/dwmmc@fe2c0000"; - mmc3 = "/dwmmc@fe000000"; - serial0 = "/serial@fdd50000"; - serial1 = "/serial@fe650000"; - serial2 = "/serial@fe660000"; - serial3 = "/serial@fe670000"; - serial4 = "/serial@fe680000"; - serial5 = "/serial@fe690000"; - serial6 = "/serial@fe6a0000"; - serial7 = "/serial@fe6b0000"; - serial8 = "/serial@fe6c0000"; - serial9 = "/serial@fe6d0000"; - spi0 = "/spi@fe610000"; - spi1 = "/spi@fe620000"; - spi2 = "/spi@fe630000"; - spi3 = "/spi@fe640000"; - lvds0 = "/syscon@fdc60000/lvds"; - lvds1 = "/syscon@fdc60000/lvds1"; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - // cpu@0 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x00 0x00>; - // enable-method = "psci"; - // clocks = <0x02 0x00>; - // operating-points-v2 = <0x03>; - // cpu-idle-states = <0x04>; - // #cooling-cells = <0x02>; - // dynamic-power-coefficient = <0xbb>; - // cpu-supply = <0x05>; - // phandle = <0x0c>; - // }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x00 0x100>; - // enable-method = "psci"; - // clocks = <0x02 0x00>; - // operating-points-v2 = <0x03>; - // cpu-idle-states = <0x04>; - // phandle = <0x0d>; - // }; - - cpu@200 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x200>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - phandle = <0x0e>; - }; - - cpu@300 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00 0x300>; - enable-method = "psci"; - clocks = <0x02 0x00>; - operating-points-v2 = <0x03>; - cpu-idle-states = <0x04>; - phandle = <0x0f>; - }; - - idle-states { - entry-method = "psci"; - - cpu-sleep { - compatible = "arm,idle-state"; - local-timer-stop; - arm,psci-suspend-param = <0x10000>; - entry-latency-us = <0x64>; - exit-latency-us = <0x78>; - min-residency-us = <0x3e8>; - phandle = <0x04>; - }; - }; - }; - - cpu0-opp-table { - compatible = "operating-points-v2"; - opp-shared; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x06 0x07 0x08 0x09 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0x118c30>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-freq = <0x639c0>; - rockchip,pvtm-volt = <0xdbba0>; - rockchip,pvtm-ch = <0x00 0x05>; - rockchip,pvtm-sample-time = <0x3e8>; - rockchip,pvtm-number = <0x0a>; - rockchip,pvtm-error = <0x3e8>; - rockchip,pvtm-ref-temp = <0x28>; - rockchip,pvtm-temp-prop = <0x1a 0x1a>; - rockchip,thermal-zone = "soc-thermal"; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x7c8 0x124f8>; - phandle = <0x03>; - - opp-408000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x18519600>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-816000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x30a32c00>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - opp-suspend; - }; - - opp-1104000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x41cdb400>; - opp-microvolt = <0xdbba0 0xdbba0 0x118c30>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0x118c30>; - opp-microvolt-L1 = <0xcf850 0xcf850 0x118c30>; - opp-microvolt-L2 = <0xcf850 0xcf850 0x118c30>; - opp-microvolt-L3 = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1416000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x54667200>; - opp-microvolt = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L0 = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L1 = <0xee098 0xee098 0x118c30>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0x118c30>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1608000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x5fd82200>; - opp-microvolt = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L0 = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L1 = <0x100590 0x100590 0x118c30>; - opp-microvolt-L2 = <0xfa3e8 0xfa3e8 0x118c30>; - opp-microvolt-L3 = <0xf4240 0xf4240 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1800000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x6b49d200>; - opp-microvolt = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L0 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L1 = <0x10c8e0 0x10c8e0 0x118c30>; - opp-microvolt-L2 = <0x106738 0x106738 0x118c30>; - opp-microvolt-L3 = <0x100590 0x100590 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-1992000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x76bb8200>; - opp-microvolt = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L0 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L1 = <0x118c30 0x118c30 0x118c30>; - opp-microvolt-L2 = <0x112a88 0x112a88 0x118c30>; - opp-microvolt-L3 = <0x10c8e0 0x10c8e0 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-j-1008000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x3c14dc00>; - opp-microvolt = <0xcf850 0xcf850 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-j-1416000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x54667200>; - opp-microvolt = <0xdbba0 0xdbba0 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - - opp-m-1608000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x5fd82200>; - opp-microvolt = <0xf4240 0xf4240 0x118c30>; - clock-latency-ns = <0x9c40>; - }; - }; - - arm-pmu { - compatible = "arm,cortex-a55-pmu\0arm,armv8-pmuv3"; - interrupts = <0x00 0xe4 0x04 0x00 0xe5 0x04 0x00 0xe6 0x04 0x00 0xe7 0x04>; - interrupt-affinity = <0x0c 0x0d 0x0e 0x0f>; - }; - - cpuinfo { - compatible = "rockchip,cpuinfo"; - nvmem-cells = <0x10 0x11 0x12>; - nvmem-cell-names = "id\0cpu-version\0cpu-code"; - }; - - display-subsystem { - compatible = "rockchip,display-subsystem"; - memory-region = <0x13 0x14>; - memory-region-names = "drm-logo\0drm-cubic-lut"; - ports = <0x15>; - devfreq = <0x16>; - - route { - - route-dsi0 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x17>; - }; - - route-dsi1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x18>; - }; - - route-edp { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x19>; - }; - - route-hdmi { - status = "okay"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1a>; - }; - - route-lvds { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1b>; - }; - - route-rgb { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x1c>; - }; - }; - }; - - edac { - compatible = "rockchip,rk3568-edac"; - interrupts = <0x00 0xad 0x04 0x00 0xaf 0x04>; - interrupt-names = "ce\0ue"; - status = "disabled"; - }; - - firmware { - - scmi { - compatible = "arm,scmi-smc"; - shmem = <0x1d>; - arm,smc-id = <0x82000010>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@14 { - reg = <0x14>; - #clock-cells = <0x01>; - rockchip,clk-init = <0x41cdb400>; - phandle = <0x02>; - }; - }; - - sdei { - compatible = "arm,sdei-1.0"; - method = "smc"; - }; - }; - - mipi-csi2 { - compatible = "rockchip,rk3568-mipi-csi2"; - rockchip,hw = <0x1e>; - status = "disabled"; - }; - - mpp-srv { - compatible = "rockchip,mpp-service"; - rockchip,taskqueue-count = <0x06>; - rockchip,resetgroup-count = <0x06>; - status = "okay"; - phandle = <0x7b>; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - }; - - reserved-memory { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - drm-logo@00000000 { - compatible = "rockchip,drm-logo"; - reg = <0x00 0xedf00000 0x00 0x2e0000>; - phandle = <0x13>; - }; - - drm-cubic-lut@00000000 { - compatible = "rockchip,drm-cubic-lut"; - reg = <0x00 0xeff00000 0x00 0x8000>; - phandle = <0x14>; - }; - - ramoops@110000 { - compatible = "ramoops"; - reg = <0x00 0x110000 0x00 0xf0000>; - record-size = <0x20000>; - console-size = <0x80000>; - ftrace-size = <0x00>; - pmsg-size = <0x50000>; - }; - }; - - rockchip-suspend { - compatible = "rockchip,pm-rk3568"; - status = "okay"; - rockchip,sleep-debug-en = <0x01>; - rockchip,sleep-mode-config = <0x5ec>; - rockchip,wakeup-config = <0x10>; - }; - - rockchip-system-monitor { - compatible = "rockchip,system-monitor"; - rockchip,thermal-zone = "soc-thermal"; - }; - - thermal-zones { - - soc-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - sustainable-power = <0x389>; - thermal-sensors = <0x1f 0x00>; - - trips { - - trip-point-0 { - temperature = <0x124f8>; - hysteresis = <0x7d0>; - type = "passive"; - }; - - trip-point-1 { - temperature = <0x14c08>; - hysteresis = <0x7d0>; - type = "passive"; - phandle = <0x20>; - }; - - soc-crit { - temperature = <0x1c138>; - hysteresis = <0x7d0>; - type = "critical"; - }; - }; - - cooling-maps { - - map0 { - trip = <0x20>; - cooling-device = <0x0c 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - - map1 { - trip = <0x20>; - cooling-device = <0x21 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - }; - }; - - gpu-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x1f 0x01>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; - arm,no-tick-in-suspend; - }; - - external-gmac0-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "gmac0_clkin"; - #clock-cells = <0x00>; - phandle = <0xc7>; - }; - - external-gmac1-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "gmac1_clkin"; - #clock-cells = <0x00>; - phandle = <0x92>; - }; - - xpcs-gmac0-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "clk_gmac0_xpcs_mii"; - #clock-cells = <0x00>; - }; - - xpcs-gmac1-clock { - compatible = "fixed-clock"; - clock-frequency = <0x7735940>; - clock-output-names = "clk_gmac1_xpcs_mii"; - #clock-cells = <0x00>; - }; - - i2s1-mclkin-rx { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s1_mclkin_rx"; - }; - - i2s1-mclkin-tx { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s1_mclkin_tx"; - }; - - i2s2-mclkin { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s2_mclkin"; - }; - - i2s3-mclkin { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbb8000>; - clock-output-names = "i2s3_mclkin"; - }; - - mpll { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2faf0800>; - clock-output-names = "mpll"; - }; - - xin24m { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x16e3600>; - clock-output-names = "xin24m"; - }; - - xin32k { - compatible = "fixed-clock"; - clock-frequency = <0x8000>; - clock-output-names = "xin32k"; - #clock-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x22>; - }; - - scmi-shmem@10f000 { - compatible = "arm,scmi-shmem"; - reg = <0x00 0x10f000 0x00 0x100>; - phandle = <0x1d>; - }; - - sata@fc000000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc000000 0x00 0x1000>; - clocks = <0x23 0x96 0x23 0x97 0x23 0x98>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x5e 0x04>; - interrupt-names = "hostc"; - phys = <0x24 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "disabled"; - }; - - sata@fc400000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc400000 0x00 0x1000>; - clocks = <0x23 0x9b 0x23 0x9c 0x23 0x9d>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x5f 0x04>; - interrupt-names = "hostc"; - phys = <0x26 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "disabled"; - }; - - sata@fc800000 { - compatible = "snps,dwc-ahci"; - reg = <0x00 0xfc800000 0x00 0x1000>; - clocks = <0x23 0xa0 0x23 0xa1 0x23 0xa2>; - clock-names = "sata\0pmalive\0rxoob"; - interrupts = <0x00 0x60 0x04>; - interrupt-names = "hostc"; - phys = <0x27 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - power-domains = <0x25 0x0f>; - status = "okay"; - }; - - usbdrd { - compatible = "rockchip,rk3568-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x23 0xa6 0x23 0xa7 0x23 0xa5 0x23 0x7f>; - clock-names = "ref_clk\0suspend_clk\0bus_clk\0pipe_clk"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - dwc3@fcc00000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfcc00000 0x00 0x400000>; - interrupts = <0x00 0xa9 0x04>; - dr_mode = "otg"; - phys = <0x28 0x24 0x04>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - power-domains = <0x25 0x0f>; - resets = <0x23 0x94>; - reset-names = "usb3-otg"; - snps,dis_enblslpm_quirk; - snps,dis-u1-entry-quirk; - snps,dis-u2-entry-quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,dis_rxdet_inp3_quirk; - snps,parkmode-disable-hs-quirk; - snps,parkmode-disable-ss-quirk; - quirk-skip-phy-init; - status = "okay"; - extcon = <0x29>; - usb-role-switch; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x2a>; - phandle = <0x4d>; - }; - }; - }; - }; - - usbhost { - compatible = "rockchip,rk3568-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x23 0xa9 0x23 0xaa 0x23 0xa8 0x23 0x7f>; - clock-names = "ref_clk\0suspend_clk\0bus_clk\0pipe_clk"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - dwc3@fd000000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfd000000 0x00 0x400000>; - interrupts = <0x00 0xaa 0x04>; - dr_mode = "host"; - phys = <0x2b 0x26 0x04>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - power-domains = <0x25 0x0f>; - resets = <0x23 0x95>; - reset-names = "usb3-host"; - snps,dis_enblslpm_quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,dis_rxdet_inp3_quirk; - snps,parkmode-disable-hs-quirk; - snps,parkmode-disable-ss-quirk; - status = "okay"; - }; - }; - - interrupt-controller@fd400000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0xfd400000 0x00 0x10000 0x00 0xfd460000 0x00 0xc0000>; - interrupts = <0x01 0x09 0x04>; - phandle = <0x01>; - - interrupt-controller@fd440000 { - compatible = "arm,gic-v3-its"; - msi-controller; - #msi-cells = <0x01>; - reg = <0x00 0xfd440000 0x00 0x20000>; - status = "okay"; - phandle = <0xbe>; - }; - }; - - usb@fd800000 { - compatible = "generic-ehci"; - reg = <0x00 0xfd800000 0x00 0x40000>; - interrupts = <0x00 0x82 0x04>; - clocks = <0x23 0xbd 0x23 0xbe 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2d>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd840000 { - compatible = "generic-ohci"; - reg = <0x00 0xfd840000 0x00 0x40000>; - interrupts = <0x00 0x83 0x04>; - clocks = <0x23 0xbd 0x23 0xbe 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2d>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd880000 { - compatible = "generic-ehci"; - reg = <0x00 0xfd880000 0x00 0x40000>; - interrupts = <0x00 0x85 0x04>; - clocks = <0x23 0xbf 0x23 0xc0 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2e>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - usb@fd8c0000 { - compatible = "generic-ohci"; - reg = <0x00 0xfd8c0000 0x00 0x40000>; - interrupts = <0x00 0x86 0x04>; - clocks = <0x23 0xbf 0x23 0xc0 0x23 0xbc 0x2c>; - clock-names = "usbhost\0arbiter\0pclk\0utmi"; - phys = <0x2e>; - phy-names = "usb2-phy"; - status = "okay"; - }; - - syscon@fda00000 { - compatible = "rockchip,rk3568-xpcs\0syscon"; - reg = <0x00 0xfda00000 0x00 0x200000>; - status = "disabled"; - }; - - syscon@fdc20000 { - compatible = "rockchip,rk3568-pmugrf\0syscon\0simple-mfd"; - reg = <0x00 0xfdc20000 0x00 0x10000>; - phandle = <0x3c>; - - io-domains { - compatible = "rockchip,rk3568-pmu-io-voltage-domain"; - status = "okay"; - pmuio1-supply = <0x2f>; - pmuio2-supply = <0x2f>; - vccio1-supply = <0x30>; - vccio3-supply = <0x31>; - vccio4-supply = <0x32>; - vccio5-supply = <0x33>; - vccio6-supply = <0x32>; - vccio7-supply = <0x33>; - }; - - reboot-mode { - compatible = "syscon-reboot-mode"; - offset = <0x200>; - mode-bootloader = <0x5242c301>; - mode-charge = <0x5242c30b>; - mode-fastboot = <0x5242c309>; - mode-loader = <0x5242c301>; - mode-normal = <0x5242c300>; - mode-recovery = <0x5242c303>; - mode-ums = <0x5242c30c>; - mode-panic = <0x5242c307>; - mode-watchdog = <0x5242c308>; - }; - }; - - syscon@fdc50000 { - compatible = "rockchip,rk3568-pipegrf\0syscon"; - reg = <0x00 0xfdc50000 0x00 0x1000>; - phandle = <0x12a>; - }; - - syscon@fdc60000 { - compatible = "rockchip,rk3568-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfdc60000 0x00 0x10000>; - phandle = <0x3b>; - - io-domains { - compatible = "rockchip,rk3568-io-voltage-domain"; - status = "disabled"; - }; - - lvds { - compatible = "rockchip,rk3568-lvds"; - phys = <0x34>; - phy-names = "phy"; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x1b>; - status = "disabled"; - phandle = <0xa3>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x35>; - status = "disabled"; - phandle = <0xa5>; - }; - }; - }; - }; - - lvds1 { - compatible = "rockchip,rk3568-lvds"; - phys = <0x36>; - phy-names = "phy"; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x37>; - phandle = <0xa4>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x38>; - phandle = <0xa7>; - }; - }; - }; - }; - - rgb { - compatible = "rockchip,rk3568-rgb"; - pinctrl-names = "default"; - pinctrl-0 = <0x39>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x1c>; - status = "disabled"; - phandle = <0xa6>; - }; - }; - }; - }; - }; - - syscon@fdc70000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc70000 0x00 0x1000>; - phandle = <0x12b>; - }; - - syscon@fdc80000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc80000 0x00 0x1000>; - phandle = <0x12c>; - }; - - syscon@fdc90000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfdc90000 0x00 0x1000>; - phandle = <0x12d>; - }; - - syscon@fdca0000 { - compatible = "rockchip,rk3568-usb2phy-grf\0syscon"; - reg = <0x00 0xfdca0000 0x00 0x8000>; - phandle = <0x135>; - }; - - syscon@fdca8000 { - compatible = "rockchip,rk3568-usb2phy-grf\0syscon"; - reg = <0x00 0xfdca8000 0x00 0x8000>; - phandle = <0x137>; - }; - - syscon@fdcb0000 { - compatible = "rockchip,rk3568-edp-phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfdcb0000 0x00 0x100>; - clocks = <0x23 0x192>; - - edp-phy { - compatible = "rockchip,rk3568-edp-phy"; - clocks = <0x3a 0x29>; - clock-names = "refclk"; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0xae>; - }; - }; - - syscon@fdcb8000 { - compatible = "rockchip,pcie30-phy-grf\0syscon"; - reg = <0x00 0xfdcb8000 0x00 0x10000>; - phandle = <0x138>; - }; - - sram@fdcc0000 { - compatible = "mmio-sram"; - reg = <0x00 0xfdcc0000 0x00 0xb000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0xfdcc0000 0xb000>; - - rkvdec-sram@0 { - reg = <0x00 0xb000>; - phandle = <0x84>; - }; - }; - - clock-controller@fdd00000 { - compatible = "rockchip,rk3568-pmucru"; - reg = <0x00 0xfdd00000 0x00 0x1000>; - rockchip,grf = <0x3b>; - rockchip,pmugrf = <0x3c>; - #clock-cells = <0x01>; - #reset-cells = <0x01>; - assigned-clocks = <0x3a 0x32>; - assigned-clock-parents = <0x3a 0x05>; - phandle = <0x3a>; - }; - - clock-controller@fdd20000 { - compatible = "rockchip,rk3568-cru"; - reg = <0x00 0xfdd20000 0x00 0x1000>; - rockchip,grf = <0x3b>; - #clock-cells = <0x01>; - #reset-cells = <0x01>; - assigned-clocks = <0x3a 0x05 0x23 0x106 0x23 0x10b 0x3a 0x01 0x3a 0x2b 0x23 0x03 0x23 0x19b 0x23 0x09 0x23 0x19c 0x23 0x19d 0x23 0x1a1 0x23 0x19e 0x23 0x19f 0x23 0x1a0 0x23 0x04 0x23 0x10d 0x23 0x10e 0x23 0x173 0x23 0x174 0x23 0x175 0x23 0x176 0x23 0xc9 0x23 0xca 0x23 0x06 0x23 0x7e 0x23 0x7f 0x23 0x3d 0x23 0x41 0x23 0x45 0x23 0x49 0x23 0x4d 0x23 0x4d 0x23 0x55 0x23 0x51 0x23 0x5d 0x23 0xdd>; - assigned-clock-rates = <0x8000 0x11e1a300 0x11e1a300 0xbebc200 0x5f5e100 0x3b9aca00 0x1dcd6500 0x13d92d40 0xee6b280 0x7735940 0x5f5e100 0x3b9aca0 0x2faf080 0x17d7840 0x46cf7100 0x8f0d180 0x5f5e100 0x1dcd6500 0x17d78400 0x8f0d180 0x5f5e100 0x11e1a300 0x8f0d180 0x47868c00 0x17d78400 0x5f5e100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x46cf7100 0x1dcd6500>; - assigned-clock-parents = <0x3a 0x08 0x23 0x04 0x23 0x04>; - phandle = <0x23>; - }; - - i2c@fdd40000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfdd40000 0x00 0x1000>; - clocks = <0x3a 0x07 0x3a 0x2d>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x2e 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x3d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - tcs4525@1c { - compatible = "tcs,tcs452x"; - reg = <0x1c>; - vin-supply = <0x3e>; - regulator-compatible = "fan53555-reg"; - regulator-name = "vdd_cpu"; - regulator-min-microvolt = <0xadf34>; - regulator-max-microvolt = <0x1535b0>; - regulator-ramp-delay = <0x8fc>; - fcs,suspend-voltage-selector = <0x01>; - regulator-boot-on; - regulator-always-on; - phandle = <0x05>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - pmic@20 { - compatible = "rockchip,rk809"; - reg = <0x20>; - interrupt-parent = <0x3f>; - interrupts = <0x03 0x08>; - pinctrl-names = "default\0pmic-sleep\0pmic-power-off\0pmic-reset"; - pinctrl-0 = <0x40>; - pinctrl-1 = <0x41 0x42>; - pinctrl-2 = <0x43 0x44>; - pinctrl-3 = <0x43 0x45>; - rockchip,system-power-controller; - wakeup-source; - #clock-cells = <0x01>; - clock-output-names = "rk808-clkout1\0rk808-clkout2"; - pmic-reset-func = <0x00>; - not-save-power-en = <0x01>; - vcc1-supply = <0x46>; - vcc2-supply = <0x46>; - vcc3-supply = <0x46>; - vcc4-supply = <0x46>; - vcc5-supply = <0x46>; - vcc6-supply = <0x46>; - vcc7-supply = <0x46>; - vcc8-supply = <0x46>; - vcc9-supply = <0x46>; - phandle = <0x152>; - - pwrkey { - status = "okay"; - }; - - pinctrl_rk8xx { - gpio-controller; - #gpio-cells = <0x02>; - - rk817_slppin_null { - pins = "gpio_slp"; - function = "pin_fun0"; - }; - - rk817_slppin_slp { - pins = "gpio_slp"; - function = "pin_fun1"; - phandle = <0x42>; - }; - - rk817_slppin_pwrdn { - pins = "gpio_slp"; - function = "pin_fun2"; - phandle = <0x44>; - }; - - rk817_slppin_rst { - pins = "gpio_slp"; - function = "pin_fun3"; - phandle = <0x45>; - }; - }; - - regulators { - - DCDC_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_logic"; - phandle = <0x75>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_gpu"; - phandle = <0x77>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-initial-mode = <0x02>; - regulator-name = "vcc_ddr"; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - DCDC_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x7a120>; - regulator-max-microvolt = <0x149970>; - regulator-init-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x1771>; - regulator-initial-mode = <0x02>; - regulator-name = "vdd_npu"; - phandle = <0x71>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG1 { - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda0v9_image"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda_0v9"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - regulator-name = "vdda0v9_pmu"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0xdbba0>; - }; - }; - - LDO_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vccio_acodec"; - phandle = <0x30>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vccio_sd"; - phandle = <0x31>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG6 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vcc3v3_pmu"; - phandle = <0x2f>; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x325aa0>; - }; - }; - - LDO_REG7 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca_1v8"; - phandle = <0x129>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - LDO_REG8 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca1v8_pmu"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x1b7740>; - }; - }; - - LDO_REG9 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcca1v8_image"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcc_1v8"; - phandle = <0x32>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - SWITCH_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vcc_3v3"; - phandle = <0x33>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - SWITCH_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vcc3v3_sd"; - phandle = <0xcf>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - - codec { - #sound-dai-cells = <0x00>; - compatible = "rockchip,rk809-codec\0rockchip,rk817-codec"; - clocks = <0x23 0x1a3>; - clock-names = "mclk"; - assigned-clocks = <0x23 0x1a3 0x23 0x1a6>; - assigned-clock-rates = <0xbb8000>; - assigned-clock-parents = <0x23 0x48 0x23 0x48>; - pinctrl-names = "default\0spk_gpio"; - pinctrl-0 = <0x47>; - pinctrl-1 = <0x48>; - hp-volume = <0x03>; - spk-volume = <0x03>; - mic-in-differential; - board-spk-from-hp; - capture-volume = <0x00>; - io-channels = <0x49 0x07>; - hp-det-adc-value = <0x3e8>; - status = "okay"; - hp-adc-drift-scope = <0x64>; - phandle = <0x14b>; - }; - - rtc { - status = "disabled"; - }; - }; - - fusb302@22 { - compatible = "fcs,fusb302"; - reg = <0x22>; - interrupt-parent = <0x3f>; - fcs,int_n = <0x3f 0x11 0x08>; - fusb340-switch-gpios = <0x4a 0x12 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x4b>; - vbus-supply = <0x4c>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x4d>; - phandle = <0x2a>; - }; - }; - }; - - connector { - compatible = "usb-c-connector"; - label = "USB-C"; - data-role = "dual"; - power-role = "dual"; - try-power-role = "sink"; - op-sink-microwatt = <0xf4240>; - sink-pdos = <0x40190fa>; - source-pdos = <0x4019096>; - }; - }; - }; - - serial@fdd50000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfdd50000 0x00 0x100>; - interrupts = <0x00 0x74 0x04>; - clocks = <0x3a 0x0b 0x3a 0x2c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x00 0x4e 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x4f>; - status = "disabled"; - }; - - pwm@fdd70000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70000 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x50>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70010 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x51>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70020 0x00 0x10>; - interrupts = <0x00 0x52 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x52>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fdd70030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfdd70030 0x00 0x10>; - interrupts = <0x00 0x52 0x04 0x00 0x56 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x53>; - clocks = <0x3a 0x0d 0x3a 0x30>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - power-management@fdd90000 { - compatible = "rockchip,rk3568-pmu\0syscon\0simple-mfd"; - reg = <0x00 0xfdd90000 0x00 0x1000>; - - power-controller { - compatible = "rockchip,rk3568-power-controller"; - #power-domain-cells = <0x01>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x25>; - - pd_npu@6 { - reg = <0x06>; - clocks = <0x23 0x27 0x23 0x25 0x23 0x26>; - pm_qos = <0x54>; - }; - - pd_gpu@7 { - reg = <0x07>; - clocks = <0x23 0x19 0x23 0x1a>; - pm_qos = <0x55>; - }; - - pd_vi@8 { - reg = <0x08>; - clocks = <0x23 0xcc 0x23 0xcd>; - pm_qos = <0x56 0x57 0x58>; - }; - - pd_vo@9 { - reg = <0x09>; - clocks = <0x23 0xda 0x23 0xdb 0x23 0xdc>; - pm_qos = <0x59 0x5a 0x5b>; - }; - - pd_rga@10 { - reg = <0x0a>; - clocks = <0x23 0xf1 0x23 0xf2>; - pm_qos = <0x5c 0x5d 0x5e 0x5f 0x60 0x61>; - }; - - pd_vpu@11 { - reg = <0x0b>; - clocks = <0x23 0xed>; - pm_qos = <0x62>; - }; - - pd_rkvdec@13 { - clocks = <0x23 0x107>; - reg = <0x0d>; - pm_qos = <0x63>; - }; - - pd_rkvenc@14 { - reg = <0x0e>; - clocks = <0x23 0x102>; - pm_qos = <0x64 0x65 0x66>; - }; - - pd_pipe@15 { - reg = <0x0f>; - clocks = <0x23 0x7f>; - pm_qos = <0x67 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e>; - }; - }; - }; - - pvtm@fde00000 { - compatible = "rockchip,rk3568-core-pvtm"; - reg = <0x00 0xfde00000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@0 { - reg = <0x00>; - clocks = <0x23 0x13 0x23 0x1c2>; - clock-names = "clk\0pclk"; - resets = <0x23 0x1a 0x23 0x19>; - reset-names = "rts\0rst-p"; - thermal-zone = "soc-thermal"; - }; - }; - - npu@fde40000 { - compatible = "rockchip,rk3568-rknpu\0rockchip,rknpu"; - reg = <0x00 0xfde40000 0x00 0x10000>; - interrupts = <0x00 0x97 0x04>; - clocks = <0x02 0x02 0x23 0x23 0x23 0x28 0x23 0x29>; - clock-names = "scmi_clk\0clk\0aclk\0hclk"; - assigned-clocks = <0x23 0x23>; - assigned-clock-rates = <0x23c34600>; - resets = <0x23 0x2b 0x23 0x2c>; - reset-names = "srst_a\0srst_h"; - power-domains = <0x25 0x06>; - operating-points-v2 = <0x6f>; - iommus = <0x70>; - status = "okay"; - rknpu-supply = <0x71>; - }; - - npu-opp-table { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x72 0x07 0x08 0x73 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x3e8 0xc350>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x6f>; - - opp-200000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0xbebc200>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-300000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-400000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-700000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L0 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L1 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L2 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L3 = <0xcf850 0xcf850 0xf4240>; - }; - - opp-800000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L0 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L1 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L2 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L3 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-900000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xee098 0xee098 0xf4240>; - opp-microvolt-L0 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L1 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L2 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L3 = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-1000000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L0 = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L1 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0xf4240>; - status = "disabled"; - }; - - opp-j-600000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-m-900000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xe1d48 0xe1d48 0xf4240>; - }; - }; - - bus-npu { - compatible = "rockchip,rk3568-bus"; - rockchip,busfreq-policy = "clkfreq"; - clocks = <0x02 0x02>; - clock-names = "bus"; - operating-points-v2 = <0x74>; - status = "okay"; - bus-supply = <0x75>; - pvtm-supply = <0x05>; - }; - - bus-npu-opp-table { - compatible = "operating-points-v2"; - opp-shared; - nvmem-cells = <0x07>; - nvmem-cell-names = "pvtm"; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x16378 0x01 0x16379 0x186a0 0x02>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x74>; - - opp-700000000 { - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - opp-microvolt-L2 = <0xd59f8>; - }; - - opp-900000000 { - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xdbba0>; - }; - - opp-1000000000 { - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xe7ef0>; - opp-microvolt-L0 = <0xe7ef0>; - opp-microvolt-L1 = <0xe1d48>; - opp-microvolt-L2 = <0xdbba0>; - }; - }; - - iommu@fde4b000 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfde4b000 0x00 0x40>; - interrupts = <0x00 0x97 0x04>; - interrupt-names = "rknpu_mmu"; - clocks = <0x23 0x28 0x23 0x29>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x06>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x70>; - }; - - gpu@fde60000 { - compatible = "arm,mali-bifrost"; - reg = <0x00 0xfde60000 0x00 0x4000>; - interrupts = <0x00 0x27 0x04 0x00 0x29 0x04 0x00 0x28 0x04>; - interrupt-names = "GPU\0MMU\0JOB"; - upthreshold = <0x28>; - downdifferential = <0x0a>; - clocks = <0x02 0x01 0x23 0x1b>; - clock-names = "clk_mali\0clk_gpu"; - power-domains = <0x25 0x07>; - #cooling-cells = <0x02>; - operating-points-v2 = <0x76>; - status = "okay"; - mali-supply = <0x77>; - phandle = <0x21>; - - power-model { - compatible = "simple-power-model"; - leakage-range = <0x05 0x0f>; - ls = <0xffffa23e 0x5927 0x00>; - static-coefficient = <0x186a0>; - dynamic-coefficient = <0x3b9>; - ts = <0xfffe56a6 0xf87a 0xfffffab5 0x14>; - thermal-zone = "gpu-thermal"; - }; - }; - - opp-table2 { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x78 0x07 0x08 0x79 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x320 0xc350>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x153d8 0x01 0x153d9 0x16378 0x02 0x16379 0x186a0 0x03>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x76>; - - opp-200000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0xbebc200>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-300000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x11e1a300>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-400000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xcf850 0xcf850 0xf4240>; - }; - - opp-600000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L1 = <0xd59f8 0xd59f8 0xf4240>; - opp-microvolt-L2 = <0xcf850 0xcf850 0xf4240>; - opp-microvolt-L3 = <0xcf850 0xcf850 0xf4240>; - }; - - opp-700000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L0 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L1 = <0xe1d48 0xe1d48 0xf4240>; - opp-microvolt-L2 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L3 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-800000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L0 = <0xf4240 0xf4240 0xf4240>; - opp-microvolt-L1 = <0xee098 0xee098 0xf4240>; - opp-microvolt-L2 = <0xe7ef0 0xe7ef0 0xf4240>; - opp-microvolt-L3 = <0xe1d48 0xe1d48 0xf4240>; - }; - - opp-j-600000000 { - opp-supported-hw = <0x04 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - }; - - opp-m-800000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xe7ef0 0xe7ef0 0xf4240>; - }; - }; - - pvtm@fde80000 { - compatible = "rockchip,rk3568-gpu-pvtm"; - reg = <0x00 0xfde80000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@1 { - reg = <0x01>; - clocks = <0x23 0x1e 0x23 0x1d>; - clock-names = "clk\0pclk"; - resets = <0x23 0x24 0x23 0x23>; - reset-names = "rts\0rst-p"; - thermal-zone = "gpu-thermal"; - }; - }; - - pvtm@fde90000 { - compatible = "rockchip,rk3568-npu-pvtm"; - reg = <0x00 0xfde90000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@2 { - reg = <0x02>; - clocks = <0x23 0x2b 0x23 0x2a 0x23 0x25>; - clock-names = "clk\0pclk\0hclk"; - resets = <0x23 0x2e 0x23 0x2d>; - reset-names = "rts\0rst-p"; - thermal-zone = "soc-thermal"; - }; - }; - - vdpu@fdea0400 { - compatible = "rockchip,vpu-decoder-v2"; - reg = <0x00 0xfdea0400 0x00 0x400>; - interrupts = <0x00 0x8b 0x04>; - interrupt-names = "irq_dec"; - clocks = <0x23 0xee 0x23 0xef>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - resets = <0x23 0x11a 0x23 0x11b>; - reset-names = "video_a\0video_h"; - iommus = <0x7a>; - power-domains = <0x25 0x0b>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x00>; - rockchip,resetgroup-node = <0x00>; - status = "okay"; - }; - - iommu@fdea0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdea0800 0x00 0x40>; - interrupts = <0x00 0x8a 0x04>; - interrupt-names = "vdpu_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xee 0x23 0xef>; - power-domains = <0x25 0x0b>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7a>; - }; - - rk_rga@fdeb0000 { - compatible = "rockchip,rga2"; - reg = <0x00 0xfdeb0000 0x00 0x1000>; - interrupts = <0x00 0x5a 0x04>; - clocks = <0x23 0xf3 0x23 0xf4 0x23 0xf5>; - clock-names = "aclk_rga\0hclk_rga\0clk_rga"; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - ebc@fdec0000 { - compatible = "rockchip,rk3568-ebc-tcon"; - reg = <0x00 0xfdec0000 0x00 0x5000>; - interrupts = <0x00 0x11 0x04>; - clocks = <0x23 0xf9 0x23 0xfa>; - clock-names = "hclk\0dclk"; - power-domains = <0x25 0x0a>; - rockchip,grf = <0x3b>; - pinctrl-names = "default"; - pinctrl-0 = <0x7c>; - status = "disabled"; - }; - - jpegd@fded0000 { - compatible = "rockchip,rkv-jpeg-decoder-v1"; - reg = <0x00 0xfded0000 0x00 0x400>; - interrupts = <0x00 0x3e 0x04>; - clocks = <0x23 0xfb 0x23 0xfc>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,disable-auto-freq; - resets = <0x23 0x12c 0x23 0x12d>; - reset-names = "video_a\0video_h"; - iommus = <0x7d>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x01>; - rockchip,resetgroup-node = <0x01>; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - iommu@fded0480 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfded0480 0x00 0x40>; - interrupts = <0x00 0x3d 0x04>; - interrupt-names = "jpegd_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xfb 0x23 0xfc>; - power-domains = <0x25 0x0a>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7d>; - }; - - vepu@fdee0000 { - compatible = "rockchip,vpu-encoder-v2"; - reg = <0x00 0xfdee0000 0x00 0x400>; - interrupts = <0x00 0x40 0x04>; - clocks = <0x23 0xfd 0x23 0xfe>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,disable-auto-freq; - resets = <0x23 0x12e 0x23 0x12f>; - reset-names = "video_a\0video_h"; - iommus = <0x7e>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x02>; - rockchip,resetgroup-node = <0x02>; - power-domains = <0x25 0x0a>; - status = "okay"; - }; - - iommu@fdee0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdee0800 0x00 0x40>; - interrupts = <0x00 0x3f 0x04>; - interrupt-names = "vepu_mmu"; - clock-names = "aclk\0iface"; - clocks = <0x23 0xfd 0x23 0xfe>; - power-domains = <0x25 0x0a>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x7e>; - }; - - iep@fdef0000 { - compatible = "rockchip,iep-v2"; - reg = <0x00 0xfdef0000 0x00 0x500>; - interrupts = <0x00 0x38 0x04>; - clocks = <0x23 0xf6 0x23 0xf7 0x23 0xf8>; - clock-names = "aclk\0hclk\0sclk"; - resets = <0x23 0x127 0x23 0x128 0x23 0x129>; - reset-names = "rst_a\0rst_h\0rst_s"; - power-domains = <0x25 0x0a>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x05>; - rockchip,resetgroup-node = <0x05>; - iommus = <0x7f>; - status = "okay"; - }; - - iommu@fdef0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdef0800 0x00 0x100>; - interrupts = <0x00 0x38 0x04>; - interrupt-names = "iep_mmu"; - clocks = <0x23 0xf6 0x23 0xf7>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - power-domains = <0x25 0x0a>; - status = "okay"; - phandle = <0x7f>; - }; - - eink@fdf00000 { - compatible = "rockchip,rk3568-eink-tcon"; - reg = <0x00 0xfdf00000 0x00 0x74>; - interrupts = <0x00 0xb2 0x04>; - clocks = <0x23 0xff 0x23 0x100>; - clock-names = "pclk\0hclk"; - status = "disabled"; - }; - - rkvenc@fdf40000 { - compatible = "rockchip,rkv-encoder-v1"; - reg = <0x00 0xfdf40000 0x00 0x400>; - interrupts = <0x00 0x8c 0x04>; - interrupt-names = "irq_enc"; - clocks = <0x23 0x103 0x23 0x104 0x23 0x105>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core"; - rockchip,normal-rates = <0x11b3dc40 0x00 0x11b3dc40>; - resets = <0x23 0x133 0x23 0x134 0x23 0x135>; - reset-names = "video_a\0video_h\0video_core"; - assigned-clocks = <0x23 0x103 0x23 0x105>; - assigned-clock-rates = <0x11b3dc40 0x11b3dc40>; - iommus = <0x80>; - node-name = "rkvenc"; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x03>; - rockchip,resetgroup-node = <0x03>; - power-domains = <0x25 0x0e>; - operating-points-v2 = <0x81>; - status = "okay"; - venc-supply = <0x75>; - }; - - rkvenc-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x07>; - nvmem-cell-names = "pvtm"; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x16378 0x01 0x16379 0x186a0 0x02>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x81>; - - opp-297000000 { - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - opp-microvolt-L2 = <0xd59f8>; - }; - - opp-400000000 { - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xe7ef0>; - opp-microvolt-L0 = <0xe7ef0>; - opp-microvolt-L1 = <0xe1d48>; - opp-microvolt-L2 = <0xdbba0>; - }; - }; - - iommu@fdf40f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdf40f00 0x00 0x40 0x00 0xfdf40f40 0x00 0x40>; - interrupts = <0x00 0x8d 0x04 0x00 0x8e 0x04>; - interrupt-names = "rkvenc_mmu0\0rkvenc_mmu1"; - clocks = <0x23 0x103 0x23 0x104>; - clock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - #iommu-cells = <0x00>; - power-domains = <0x25 0x0e>; - status = "okay"; - phandle = <0x80>; - }; - - rkvdec@fdf80200 { - compatible = "rockchip,rkv-decoder-rk3568\0rockchip,rkv-decoder-v2"; - reg = <0x00 0xfdf80200 0x00 0x400 0x00 0xfdf80100 0x00 0x100>; - reg-names = "regs\0link"; - interrupts = <0x00 0x5b 0x04>; - interrupt-names = "irq_dec"; - clocks = <0x23 0x108 0x23 0x109 0x23 0x10a 0x23 0x10b 0x23 0x10c>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_cabac\0clk_core\0clk_hevc_cabac"; - rockchip,normal-rates = <0x11b3dc40 0x00 0x11b3dc40 0x11b3dc40 0x23c34600>; - rockchip,advanced-rates = <0x179a7b00 0x00 0x179a7b00 0x179a7b00 0x23c34600>; - rockchip,default-max-load = <0x1fe000>; - resets = <0x23 0x142 0x23 0x143 0x23 0x144 0x23 0x145 0x23 0x146>; - assigned-clocks = <0x23 0x108 0x23 0x10a 0x23 0x10b 0x23 0x10c>; - assigned-clock-rates = <0x11b3dc40 0x11b3dc40 0x11b3dc40 0x11b3dc40>; - reset-names = "video_a\0video_h\0video_cabac\0video_core\0video_hevc_cabac"; - power-domains = <0x25 0x0d>; - operating-points-v2 = <0x82>; - vdec-supply = <0x75>; - iommus = <0x83>; - rockchip,srv = <0x7b>; - rockchip,taskqueue-node = <0x04>; - rockchip,resetgroup-node = <0x04>; - rockchip,sram = <0x84>; - rockchip,rcb-iova = <0x10000000 0x10000>; - rockchip,rcb-min-width = <0x200>; - rockchip,task-capacity = <0x10>; - status = "okay"; - }; - - rkvdec-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x85 0x07>; - nvmem-cell-names = "leakage\0pvtm"; - rockchip,leakage-voltage-sel = <0x01 0x50 0x00 0x51 0xfe 0x01>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x186a0 0x01>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0x82>; - - opp-297000000 { - opp-hz = <0x00 0x11b3dc40>; - opp-microvolt = <0xdbba0>; - opp-microvolt-L0 = <0xdbba0>; - opp-microvolt-L1 = <0xd59f8>; - }; - - opp-400000000 { - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xdbba0>; - }; - }; - - iommu@fdf80800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdf80800 0x00 0x40 0x00 0xfdf80840 0x00 0x40>; - interrupts = <0x00 0x5c 0x04>; - interrupt-names = "rkvdec_mmu"; - clocks = <0x23 0x108 0x23 0x109>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x0d>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x83>; - }; - - mipi-csi2-hw@fdfb0000 { - compatible = "rockchip,rk3568-mipi-csi2-hw"; - reg = <0x00 0xfdfb0000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x08 0x04 0x00 0x09 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x23 0xd5>; - clock-names = "pclk_csi2host"; - resets = <0x23 0xff>; - reset-names = "srst_csihost_p"; - status = "okay"; - phandle = <0x1e>; - }; - - rkcif@fdfe0000 { - compatible = "rockchip,rk3568-cif"; - reg = <0x00 0xfdfe0000 0x00 0x8000>; - reg-names = "cif_regs"; - interrupts = <0x00 0x92 0x04>; - interrupt-names = "cif-intr"; - clocks = <0x23 0xce 0x23 0xcf 0x23 0xd0 0x23 0xd1>; - clock-names = "aclk_cif\0hclk_cif\0dclk_cif\0iclk_cif_g"; - resets = <0x23 0xf7 0x23 0xf8 0x23 0xf9 0x23 0xfb 0x23 0xfa>; - reset-names = "rst_cif_a\0rst_cif_h\0rst_cif_d\0rst_cif_p\0rst_cif_i"; - assigned-clocks = <0x23 0xd0>; - assigned-clock-rates = <0x11e1a300>; - power-domains = <0x25 0x08>; - rockchip,grf = <0x3b>; - iommus = <0x86>; - status = "disabled"; - phandle = <0x87>; - }; - - iommu@fdfe0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdfe0800 0x00 0x100>; - interrupts = <0x00 0x92 0x04>; - interrupt-names = "cif_mmu"; - clocks = <0x23 0xce 0x23 0xcf>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x08>; - rockchip,disable-mmu-reset; - #iommu-cells = <0x00>; - status = "disabled"; - phandle = <0x86>; - }; - - rkcif_dvp { - compatible = "rockchip,rkcif-dvp"; - rockchip,hw = <0x87>; - status = "disabled"; - phandle = <0x88>; - }; - - rkcif_dvp_sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x88>; - status = "disabled"; - }; - - rkcif_mipi_lvds { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x87>; - status = "disabled"; - phandle = <0x89>; - }; - - rkcif_mipi_lvds_sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x89>; - status = "disabled"; - }; - - rkisp@fdff0000 { - compatible = "rockchip,rk3568-rkisp"; - reg = <0x00 0xfdff0000 0x00 0x10000>; - interrupts = <0x00 0x39 0x04 0x00 0x3a 0x04 0x00 0x3c 0x04>; - interrupt-names = "mipi_irq\0mi_irq\0isp_irq"; - clocks = <0x23 0xd2 0x23 0xd3 0x23 0xd4>; - clock-names = "aclk_isp\0hclk_isp\0clk_isp"; - resets = <0x23 0xfd 0x23 0xfc>; - reset-names = "isp\0isp-h"; - rockchip,grf = <0x3b>; - power-domains = <0x25 0x08>; - iommus = <0x8a>; - rockchip,iq-feature = <0x1bfb 0xfffe67ff>; - status = "okay"; - phandle = <0x8b>; - }; - - iommu@fdff1a00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdff1a00 0x00 0x100>; - interrupts = <0x00 0x3b 0x04>; - interrupt-names = "isp_mmu"; - clocks = <0x23 0xd2 0x23 0xd3>; - clock-names = "aclk\0iface"; - power-domains = <0x25 0x08>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "okay"; - phandle = <0x8a>; - }; - - rkisp-vir0 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x8b>; - status = "okay"; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x8c>; - phandle = <0x134>; - }; - }; - }; - - rkisp-vir1 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x8b>; - status = "disabled"; - }; - - uio@fe010000 { - compatible = "rockchip,uio-gmac"; - reg = <0x00 0xfe010000 0x00 0x10000>; - rockchip,ethernet = <0x8d>; - status = "disabled"; - }; - - ethernet@fe010000 { - local-mac-address = [5e 4f fd 70 05 c6]; - compatible = "rockchip,rk3568-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe010000 0x00 0x10000>; - interrupts = <0x00 0x20 0x04 0x00 0x1d 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0x3b>; - clocks = <0x23 0x186 0x23 0x189 0x23 0x189 0x23 0xc7 0x23 0xc3 0x23 0xc4 0x23 0x189 0x23 0xc8 0x23 0xac 0x23 0xab>; - clock-names = "stmmaceth\0mac_clk_rx\0mac_clk_tx\0clk_mac_refout\0aclk_mac\0pclk_mac\0clk_mac_speed\0ptp_ref\0pclk_xpcs\0clk_xpcs_eee"; - resets = <0x23 0xec>; - reset-names = "stmmaceth"; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0x8e>; - snps,mtl-rx-config = <0x8f>; - snps,mtl-tx-config = <0x90>; - status = "okay"; - phy-mode = "rgmii"; - clock_in_out = "input"; - snps,reset-gpio = <0x91 0x19 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - assigned-clocks = <0x23 0x189 0x23 0x186>; - assigned-clock-parents = <0x23 0x187 0x92>; - pinctrl-names = "default"; - pinctrl-0 = <0x93 0x94 0x95 0x96 0x97 0x98>; - tx_delay = <0x3e>; - rx_delay = <0x32>; - phy-handle = <0x99>; - phandle = <0x8d>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x00>; - led_status_value = <0x6940>; - phandle = <0x99>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0x8e>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x01>; - phandle = <0x8f>; - - queue0 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x01>; - phandle = <0x90>; - - queue0 { - }; - }; - }; - - vop@fe040000 { - compatible = "rockchip,rk3568-vop"; - reg = <0x00 0xfe040000 0x00 0x3000 0x00 0xfe044000 0x00 0x1000>; - reg-names = "regs\0gamma_lut"; - rockchip,grf = <0x3b>; - interrupts = <0x00 0x94 0x04>; - clocks = <0x23 0xdd 0x23 0xde 0x23 0xdf 0x23 0xe0 0x23 0xe1>; - clock-names = "aclk_vop\0hclk_vop\0dclk_vp0\0dclk_vp1\0dclk_vp2"; - iommus = <0x9a>; - power-domains = <0x25 0x09>; - status = "okay"; - assigned-clocks = <0x23 0xdf 0x23 0xe0>; - assigned-clock-parents = <0x3a 0x02 0x23 0x05>; - disable-win-move; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - phandle = <0x15>; - - port@0 { - rockchip,primary-plane = <0x04>; - rockchip,plane-mask = <0x15>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x9b>; - phandle = <0x17>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x9c>; - phandle = <0x18>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x9d>; - phandle = <0x19>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0x9e>; - phandle = <0x1a>; - }; - }; - - port@1 { - rockchip,primary-plane = <0x05>; - rockchip,plane-mask = <0x22>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x01>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x9f>; - phandle = <0xa8>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa0>; - phandle = <0xa9>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xa1>; - phandle = <0xaf>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0xa2>; - phandle = <0xad>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0xa3>; - phandle = <0x1b>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0xa4>; - phandle = <0x37>; - }; - }; - - port@2 { - rockchip,primary-plane = <0x03>; - rockchip,plane-mask = <0x08>; - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x02>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xa5>; - phandle = <0x35>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa6>; - phandle = <0x1c>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xa7>; - phandle = <0x38>; - }; - }; - }; - }; - - iommu@fe043e00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfe043e00 0x00 0x100 0x00 0xfe043f00 0x00 0x100>; - interrupts = <0x00 0x94 0x04>; - interrupt-names = "vop_mmu"; - clocks = <0x23 0xdd 0x23 0xde>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - rockchip,disable-device-link-resume; - status = "okay"; - phandle = <0x9a>; - }; - - dsi@fe060000 { - compatible = "rockchip,rk3568-mipi-dsi"; - reg = <0x00 0xfe060000 0x00 0x10000>; - interrupts = <0x00 0x44 0x04>; - clocks = <0x23 0xe8 0x23 0xda>; - clock-names = "pclk\0hclk"; - resets = <0x23 0x110>; - reset-names = "apb"; - phys = <0x34>; - phy-names = "dphy"; - power-domains = <0x25 0x09>; - rockchip,grf = <0x3b>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x17>; - status = "disabled"; - phandle = <0x9b>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa8>; - status = "disabled"; - phandle = <0x9f>; - }; - }; - }; - }; - - dsi@fe070000 { - compatible = "rockchip,rk3568-mipi-dsi"; - reg = <0x00 0xfe070000 0x00 0x10000>; - interrupts = <0x00 0x45 0x04>; - clocks = <0x23 0xe9 0x23 0xda>; - clock-names = "pclk\0hclk"; - resets = <0x23 0x111>; - reset-names = "apb"; - phys = <0x36>; - phy-names = "dphy"; - power-domains = <0x25 0x09>; - rockchip,grf = <0x3b>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x18>; - status = "disabled"; - phandle = <0x9c>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xa9>; - status = "disabled"; - phandle = <0xa0>; - }; - }; - }; - }; - - hdmi@fe0a0000 { - compatible = "rockchip,rk3568-dw-hdmi"; - reg = <0x00 0xfe0a0000 0x00 0x20000>; - interrupts = <0x00 0x2d 0x04>; - clocks = <0x23 0xe6 0x23 0xe7 0x23 0x193 0x3a 0x02 0x23 0xde>; - clock-names = "iahb\0isfr\0cec\0ref\0hclk"; - power-domains = <0x25 0x09>; - reg-io-width = <0x04>; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xaa 0xab 0xac>; - status = "okay"; - phandle = <0x147>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x1a>; - status = "okay"; - phandle = <0x9e>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xad>; - status = "disabled"; - phandle = <0xa2>; - }; - }; - }; - }; - - edp@fe0c0000 { - compatible = "rockchip,rk3568-edp"; - reg = <0x00 0xfe0c0000 0x00 0x10000>; - interrupts = <0x00 0x12 0x04>; - clocks = <0x3a 0x29 0x23 0xea 0x23 0xeb 0x23 0xda>; - clock-names = "dp\0pclk\0spdif\0hclk"; - resets = <0x23 0x113 0x23 0x112>; - reset-names = "dp\0apb"; - phys = <0xae>; - phy-names = "dp"; - power-domains = <0x25 0x09>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x19>; - status = "disabled"; - phandle = <0x9d>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xaf>; - status = "disabled"; - phandle = <0xa1>; - }; - }; - }; - }; - - nocp-cpu@fe102000 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102000 0x00 0x400>; - phandle = <0xb5>; - }; - - nocp-gpu-vpu-rga-venc@fe102400 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102400 0x00 0x400>; - }; - - nocp-vdec@fe102800 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102800 0x00 0x400>; - }; - - nocp-vi-usb-peri-pipe@fe102c00 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe102c00 0x00 0x400>; - }; - - nocp-vo@fe103000 { - compatible = "rockchip,rk3568-nocp"; - reg = <0x00 0xfe103000 0x00 0x400>; - }; - - qos@fe128000 { - compatible = "syscon"; - reg = <0x00 0xfe128000 0x00 0x20>; - phandle = <0x55>; - }; - - qos@fe138080 { - compatible = "syscon"; - reg = <0x00 0xfe138080 0x00 0x20>; - phandle = <0x64>; - }; - - qos@fe138100 { - compatible = "syscon"; - reg = <0x00 0xfe138100 0x00 0x20>; - phandle = <0x65>; - }; - - qos@fe138180 { - compatible = "syscon"; - reg = <0x00 0xfe138180 0x00 0x20>; - phandle = <0x66>; - }; - - qos@fe148000 { - compatible = "syscon"; - reg = <0x00 0xfe148000 0x00 0x20>; - phandle = <0x56>; - }; - - qos@fe148080 { - compatible = "syscon"; - reg = <0x00 0xfe148080 0x00 0x20>; - phandle = <0x57>; - }; - - qos@fe148100 { - compatible = "syscon"; - reg = <0x00 0xfe148100 0x00 0x20>; - phandle = <0x58>; - }; - - qos@fe150000 { - compatible = "syscon"; - reg = <0x00 0xfe150000 0x00 0x20>; - phandle = <0x62>; - }; - - qos@fe158000 { - compatible = "syscon"; - reg = <0x00 0xfe158000 0x00 0x20>; - phandle = <0x5c>; - }; - - qos@fe158100 { - compatible = "syscon"; - reg = <0x00 0xfe158100 0x00 0x20>; - phandle = <0x5d>; - }; - - qos@fe158180 { - compatible = "syscon"; - reg = <0x00 0xfe158180 0x00 0x20>; - phandle = <0x5e>; - }; - - qos@fe158200 { - compatible = "syscon"; - reg = <0x00 0xfe158200 0x00 0x20>; - phandle = <0x5f>; - }; - - qos@fe158280 { - compatible = "syscon"; - reg = <0x00 0xfe158280 0x00 0x20>; - phandle = <0x60>; - }; - - qos@fe158300 { - compatible = "syscon"; - reg = <0x00 0xfe158300 0x00 0x20>; - phandle = <0x61>; - }; - - qos@fe180000 { - compatible = "syscon"; - reg = <0x00 0xfe180000 0x00 0x20>; - phandle = <0x54>; - }; - - qos@fe190000 { - compatible = "syscon"; - reg = <0x00 0xfe190000 0x00 0x20>; - phandle = <0x67>; - }; - - qos@fe190080 { - compatible = "syscon"; - reg = <0x00 0xfe190080 0x00 0x20>; - phandle = <0x68>; - }; - - qos@fe190100 { - compatible = "syscon"; - reg = <0x00 0xfe190100 0x00 0x20>; - phandle = <0x69>; - }; - - qos@fe190200 { - compatible = "syscon"; - reg = <0x00 0xfe190200 0x00 0x20>; - phandle = <0x6a>; - }; - - qos@fe190280 { - compatible = "syscon"; - reg = <0x00 0xfe190280 0x00 0x20>; - phandle = <0x6b>; - }; - - qos@fe190300 { - compatible = "syscon"; - reg = <0x00 0xfe190300 0x00 0x20>; - phandle = <0x6c>; - }; - - qos@fe190380 { - compatible = "syscon"; - reg = <0x00 0xfe190380 0x00 0x20>; - phandle = <0x6d>; - }; - - qos@fe190400 { - compatible = "syscon"; - reg = <0x00 0xfe190400 0x00 0x20>; - phandle = <0x6e>; - }; - - qos@fe198000 { - compatible = "syscon"; - reg = <0x00 0xfe198000 0x00 0x20>; - phandle = <0x63>; - }; - - qos@fe1a8000 { - compatible = "syscon"; - reg = <0x00 0xfe1a8000 0x00 0x20>; - phandle = <0x59>; - }; - - qos@fe1a8080 { - compatible = "syscon"; - reg = <0x00 0xfe1a8080 0x00 0x20>; - phandle = <0x5a>; - }; - - qos@fe1a8100 { - compatible = "syscon"; - reg = <0x00 0xfe1a8100 0x00 0x20>; - phandle = <0x5b>; - }; - - dwmmc@fe000000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe000000 0x00 0x4000>; - interrupts = <0x00 0x64 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xc1 0x23 0xc2 0x23 0x18e 0x23 0x18f>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xeb>; - reset-names = "reset"; - status = "okay"; - no-sd; - no-mmc; - bus-width = <0x04>; - disable-wp; - cap-sd-highspeed; - cap-sdio-irq; - keep-power-in-suspend; - pinctrl-names = "default"; - pinctrl-0 = <0xb0 0xb1 0xb2>; - sd-uhs-sdr104; - mmc-pwrseq = <0xb3>; - non-removable; - }; - - dfi@fe230000 { - reg = <0x00 0xfe230000 0x00 0x400>; - compatible = "rockchip,rk3568-dfi"; - rockchip,pmugrf = <0x3c>; - status = "okay"; - phandle = <0xb4>; - }; - - dmc { - compatible = "rockchip,rk3568-dmc"; - interrupts = <0x00 0x0a 0x04>; - interrupt-names = "complete"; - devfreq-events = <0xb4 0xb5>; - clocks = <0x02 0x03>; - clock-names = "dmc_clk"; - operating-points-v2 = <0xb6>; - vop-bw-dmc-freq = <0x00 0x11e 0x4f1a0 0x11f 0x1869f 0x80e80>; - vop-frame-bw-dmc-freq = <0x00 0x26c 0x4f1a0 0x26d 0x1869f 0xbe6e0>; - cpu-bw-dmc-freq = <0x00 0x15e 0x4f1a0 0x15f 0x190 0x80e80 0x191 0x1869f 0xbe6e0>; - upthreshold = <0x28>; - downdifferential = <0x14>; - system-status-level = <0x01 0x04 0x08 0x08 0x02 0x01 0x10 0x04 0x10000 0x04 0x1000 0x08 0x4000 0x08 0x2000 0x08 0xc00 0x08>; - auto-min-freq = <0x4f1a0>; - auto-freq-en = <0x01>; - #cooling-cells = <0x02>; - status = "okay"; - center-supply = <0x75>; - phandle = <0x16>; - }; - - dmc-fsp { - compatible = "rockchip,rk3568-dmc-fsp"; - debug_print_level = <0x00>; - ddr3_params = <0xb7>; - ddr4_params = <0xb8>; - lpddr3_params = <0xb9>; - lpddr4_params = <0xba>; - lpddr4x_params = <0xbb>; - status = "okay"; - }; - - dmc-opp-table { - compatible = "operating-points-v2"; - mbist-vmin = <0xc96a8 0xdbba0 0xe7ef0>; - nvmem-cells = <0x85 0x07 0x08 0xbc 0x0a 0x0b>; - nvmem-cell-names = "leakage\0pvtm\0mbist-vmin\0opp-info\0specification_serial_number\0remark_spec_serial_number"; - rockchip,supported-hw; - rockchip,max-volt = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x00>; - rockchip,low-temp-adjust-volt = <0x00 0x618 0x124f8>; - rockchip,leakage-voltage-sel = <0x01 0x50 0x00 0x51 0xfe 0x01>; - rockchip,pvtm-voltage-sel = <0x00 0x14820 0x00 0x14821 0x186a0 0x01>; - rockchip,pvtm-ch = <0x00 0x05>; - phandle = <0xb6>; - - opp-1560000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x5cfbb600>; - opp-microvolt = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L0 = <0xdbba0 0xdbba0 0xf4240>; - opp-microvolt-L1 = <0xd59f8 0xd59f8 0xf4240>; - }; - - opp-j-m-1560000000 { - opp-supported-hw = <0x06 0xffff>; - opp-hz = <0x00 0x5cfbb600>; - opp-microvolt = <0xd59f8 0xd59f8 0xf4240>; - }; - }; - - pcie@fe260000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x00 0x0f>; - clocks = <0x23 0x81 0x23 0x82 0x23 0x83 0x23 0x84 0x23 0x85>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0x4b 0x04 0x00 0x4a 0x04 0x00 0x49 0x04 0x00 0x48 0x04 0x00 0x47 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xbd 0x00 0x00 0x00 0x00 0x02 0xbd 0x01 0x00 0x00 0x00 0x03 0xbd 0x02 0x00 0x00 0x00 0x04 0xbd 0x03>; - linux,pci-domain = <0x00>; - num-ib-windows = <0x06>; - num-viewport = <0x08>; - num-ob-windows = <0x02>; - max-link-speed = <0x02>; - msi-map = <0x00 0xbe 0x00 0x1000>; - num-lanes = <0x01>; - phys = <0x27 0x02>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf4000000 0x00 0xf4000000 0x00 0x100000 0x81000000 0x00 0xf4100000 0x00 0xf4100000 0x00 0x100000 0x82000000 0x00 0xf4200000 0x00 0xf4200000 0x00 0x1e00000 0xc3000000 0x03 0x00 0x03 0x00 0x00 0x40000000>; - reg = <0x03 0xc0000000 0x00 0x400000 0x00 0xfe260000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xa1>; - reset-names = "pipe"; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x48 0x01>; - phandle = <0xbd>; - }; - }; - - pcie@fe270000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x10 0x1f>; - clocks = <0x23 0x88 0x23 0x89 0x23 0x8a 0x23 0x8b 0x23 0x8c>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0xa0 0x04 0x00 0x9f 0x04 0x00 0x9e 0x04 0x00 0x9d 0x04 0x00 0x9c 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xbf 0x00 0x00 0x00 0x00 0x02 0xbf 0x01 0x00 0x00 0x00 0x03 0xbf 0x02 0x00 0x00 0x00 0x04 0xbf 0x03>; - linux,pci-domain = <0x01>; - num-ib-windows = <0x06>; - num-ob-windows = <0x02>; - num-viewport = <0x08>; - max-link-speed = <0x03>; - msi-map = <0x1000 0xbe 0x1000 0x1000>; - num-lanes = <0x01>; - phys = <0xc0>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf2000000 0x00 0xf2000000 0x00 0x100000 0x81000000 0x00 0xf2100000 0x00 0xf2100000 0x00 0x100000 0x82000000 0x00 0xf2200000 0x00 0xf2200000 0x00 0x1e00000 0xc3000000 0x03 0x40000000 0x03 0x40000000 0x00 0x40000000>; - reg = <0x03 0xc0400000 0x00 0x400000 0x00 0xfe270000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xb1>; - reset-names = "pipe"; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x9d 0x01>; - phandle = <0xbf>; - }; - }; - - pcie@fe280000 { - compatible = "rockchip,rk3568-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x20 0x2f>; - clocks = <0x23 0x8f 0x23 0x90 0x23 0x91 0x23 0x92 0x23 0x93>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux"; - device_type = "pci"; - interrupts = <0x00 0xa5 0x04 0x00 0xa4 0x04 0x00 0xa3 0x04 0x00 0xa2 0x04 0x00 0xa1 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xc1 0x00 0x00 0x00 0x00 0x02 0xc1 0x01 0x00 0x00 0x00 0x03 0xc1 0x02 0x00 0x00 0x00 0x04 0xc1 0x03>; - linux,pci-domain = <0x02>; - num-ib-windows = <0x06>; - num-viewport = <0x08>; - num-ob-windows = <0x02>; - max-link-speed = <0x03>; - msi-map = <0x2000 0xbe 0x2000 0x1000>; - num-lanes = <0x02>; - phys = <0xc0>; - phy-names = "pcie-phy"; - power-domains = <0x25 0x0f>; - ranges = <0x800 0x00 0xf0000000 0x00 0xf0000000 0x00 0x100000 0x81000000 0x00 0xf0100000 0x00 0xf0100000 0x00 0x100000 0x82000000 0x00 0xf0200000 0x00 0xf0200000 0x00 0x1e00000 0xc3000000 0x03 0x80000000 0x03 0x80000000 0x00 0x40000000>; - reg = <0x03 0xc0800000 0x00 0x400000 0x00 0xfe280000 0x00 0x10000>; - reg-names = "pcie-dbi\0pcie-apb"; - resets = <0x23 0xc1>; - reset-names = "pipe"; - status = "okay"; - reset-gpios = <0x91 0x1e 0x00>; - vpcie3v3-supply = <0xc2>; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xa2 0x01>; - phandle = <0xc1>; - }; - }; - - uio@fe2a0000 { - compatible = "rockchip,uio-gmac"; - reg = <0x00 0xfe2a0000 0x00 0x10000>; - rockchip,ethernet = <0xc3>; - status = "disabled"; - }; - - ethernet@fe2a0000 { - local-mac-address = [5a 4f fd 70 05 c6]; - compatible = "rockchip,rk3568-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe2a0000 0x00 0x10000>; - interrupts = <0x00 0x1b 0x04 0x00 0x18 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0x3b>; - clocks = <0x23 0x182 0x23 0x185 0x23 0x185 0x23 0xb8 0x23 0xb4 0x23 0xb5 0x23 0x185 0x23 0xb9 0x23 0xac 0x23 0xab>; - clock-names = "stmmaceth\0mac_clk_rx\0mac_clk_tx\0clk_mac_refout\0aclk_mac\0pclk_mac\0clk_mac_speed\0ptp_ref\0pclk_xpcs\0clk_xpcs_eee"; - resets = <0x23 0xd7>; - reset-names = "stmmaceth"; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0xc4>; - snps,mtl-rx-config = <0xc5>; - snps,mtl-tx-config = <0xc6>; - status = "okay"; - phy-mode = "rgmii"; - clock_in_out = "input"; - snps,reset-gpio = <0x91 0x1b 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - assigned-clocks = <0x23 0x185 0x23 0x182>; - assigned-clock-parents = <0x23 0x183 0xc7>; - pinctrl-names = "default"; - pinctrl-0 = <0xc8 0xc9 0xca 0xcb 0xcc 0xcd>; - tx_delay = <0x4a>; - rx_delay = <0x2e>; - phy-handle = <0xce>; - phandle = <0xc3>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@0 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x00>; - led_status_value = <0x6940>; - phandle = <0xce>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0xc4>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x01>; - phandle = <0xc5>; - - queue0 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x01>; - phandle = <0xc6>; - - queue0 { - }; - }; - }; - - dwmmc@fe2b0000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2b0000 0x00 0x4000>; - interrupts = <0x00 0x62 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xb0 0x23 0xb1 0x23 0x18a 0x23 0x18b>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xd4>; - reset-names = "reset"; - status = "okay"; - no-sdio; - no-mmc; - bus-width = <0x04>; - cap-mmc-highspeed; - cap-sd-highspeed; - disable-wp; - sd-uhs-sdr104; - vmmc-supply = <0xcf>; - vqmmc-supply = <0x31>; - pinctrl-names = "default"; - pinctrl-0 = <0xd0 0xd1 0xd2 0xd3>; - }; - - dwmmc@fe2c0000 { - compatible = "rockchip,rk3568-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2c0000 0x00 0x4000>; - interrupts = <0x00 0x63 0x04>; - max-frequency = <0x8f0d180>; - clocks = <0x23 0xb2 0x23 0xb3 0x23 0x18c 0x23 0x18d>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - resets = <0x23 0xd6>; - reset-names = "reset"; - status = "disabled"; - }; - - spi@fe300000 { - compatible = "rockchip,sfc"; - reg = <0x00 0xfe300000 0x00 0x4000>; - interrupts = <0x00 0x65 0x04>; - clocks = <0x23 0x78 0x23 0x76>; - clock-names = "clk_sfc\0hclk_sfc"; - assigned-clocks = <0x23 0x78>; - assigned-clock-rates = <0x2faf080>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <0xd4>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x00>; - spi-max-frequency = <0x2faf080>; - spi-rx-bus-width = <0x01>; - spi-tx-bus-width = <0x01>; - }; - }; - - sdhci@fe310000 { - compatible = "rockchip,rk3568-dwcmshc\0rockchip,dwcmshc-sdhci"; - reg = <0x00 0xfe310000 0x00 0x10000>; - interrupts = <0x00 0x13 0x04>; - assigned-clocks = <0x23 0x7b 0x23 0x7d 0x23 0x7c>; - assigned-clock-rates = <0xbebc200 0x16e3600 0xbebc200>; - clocks = <0x23 0x7c 0x23 0x7a 0x23 0x79 0x23 0x7b 0x23 0x7d>; - clock-names = "core\0bus\0axi\0block\0timer"; - resets = <0x23 0x78 0x23 0x76 0x23 0x75 0x23 0x77 0x23 0x79>; - reset-names = "core\0bus\0axi\0block\0timer"; - status = "okay"; - bus-width = <0x08>; - no-sdio; - no-sd; - non-removable; - max-frequency = <0xbebc200>; - full-pwr-cycle-in-suspend; - }; - - nandc@fe330000 { - compatible = "rockchip,rk-nandc-v9"; - reg = <0x00 0xfe330000 0x00 0x4000>; - interrupts = <0x00 0x46 0x04>; - nandc_id = <0x00>; - clocks = <0x23 0x75 0x23 0x74>; - clock-names = "clk_nandc\0hclk_nandc"; - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - nand@0 { - reg = <0x00>; - nand-bus-width = <0x08>; - nand-ecc-mode = "hw"; - nand-ecc-strength = <0x10>; - nand-ecc-step-size = <0x400>; - }; - }; - - crypto@fe380000 { - compatible = "rockchip,rk3568-crypto"; - reg = <0x00 0xfe380000 0x00 0x4000>; - interrupts = <0x00 0x04 0x04>; - clocks = <0x23 0x6a 0x23 0x6b 0x23 0x6c 0x23 0x6d>; - clock-names = "aclk\0hclk\0sclk\0apb_pclk"; - assigned-clocks = <0x23 0x6c>; - assigned-clock-rates = <0xbebc200>; - resets = <0x23 0x69>; - reset-names = "crypto-rst"; - status = "disabled"; - }; - - rng@fe388000 { - compatible = "rockchip,cryptov2-rng"; - reg = <0x00 0xfe388000 0x00 0x2000>; - clocks = <0x23 0x70 0x23 0x6f>; - clock-names = "clk_trng\0hclk_trng"; - resets = <0x23 0x6d>; - reset-names = "reset"; - status = "okay"; - }; - - otp@fe38c000 { - compatible = "rockchip,rk3568-otp"; - reg = <0x00 0xfe38c000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - clocks = <0x23 0x73 0x23 0x72 0x23 0x71 0x23 0x181>; - clock-names = "usr\0sbpi\0apb\0phy"; - resets = <0x23 0x1cf>; - reset-names = "otp_phy"; - - cpu-code@2 { - reg = <0x02 0x02>; - phandle = <0x12>; - }; - - specification-serial-number@7 { - reg = <0x07 0x01>; - bits = <0x00 0x05>; - phandle = <0x0a>; - }; - - cpu-version@8 { - reg = <0x08 0x01>; - bits = <0x03 0x03>; - phandle = <0x11>; - }; - - mbist-vmin@9 { - reg = <0x09 0x01>; - bits = <0x00 0x04>; - phandle = <0x08>; - }; - - id@a { - reg = <0x0a 0x10>; - phandle = <0x10>; - }; - - cpu-leakage@1a { - reg = <0x1a 0x01>; - phandle = <0x06>; - }; - - log-leakage@1b { - reg = <0x1b 0x01>; - phandle = <0x85>; - }; - - npu-leakage@1c { - reg = <0x1c 0x01>; - phandle = <0x72>; - }; - - gpu-leakage@1d { - reg = <0x1d 0x01>; - phandle = <0x78>; - }; - - core-pvtm@2a { - reg = <0x2a 0x02>; - phandle = <0x07>; - }; - - cpu-tsadc-trim-l@2e { - reg = <0x2e 0x01>; - phandle = <0x125>; - }; - - cpu-tsadc-trim-h@2f { - reg = <0x2f 0x01>; - bits = <0x00 0x04>; - phandle = <0x126>; - }; - - npu-tsadc-trim-l@30 { - reg = <0x30 0x01>; - phandle = <0x127>; - }; - - npu-tsadc-trim-h@31 { - reg = <0x31 0x01>; - bits = <0x00 0x04>; - phandle = <0x128>; - }; - - tsadc-trim-base-frac@31 { - reg = <0x31 0x01>; - bits = <0x04 0x04>; - phandle = <0x122>; - }; - - tsadc-trim-base@32 { - reg = <0x32 0x01>; - phandle = <0x121>; - }; - - cpu-opp-info@36 { - reg = <0x36 0x06>; - phandle = <0x09>; - }; - - gpu-opp-info@3c { - reg = <0x3c 0x06>; - phandle = <0x79>; - }; - - npu-opp-info@42 { - reg = <0x42 0x06>; - phandle = <0x73>; - }; - - dmc-opp-info@48 { - reg = <0x48 0x06>; - phandle = <0xbc>; - }; - - remark-spec-serial-number@56 { - reg = <0x56 0x01>; - bits = <0x00 0x05>; - phandle = <0x0b>; - }; - }; - - i2s@fe400000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe400000 0x00 0x1000>; - interrupts = <0x00 0x34 0x04>; - clocks = <0x23 0x3f 0x23 0x43 0x23 0x39>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x00>; - dma-names = "tx"; - resets = <0x23 0x50 0x23 0x51>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x146>; - }; - - i2s@fe410000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe410000 0x00 0x1000>; - interrupts = <0x00 0x35 0x04>; - clocks = <0x23 0x47 0x23 0x4b 0x23 0x3a>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x02 0xd5 0x03>; - dma-names = "tx\0rx"; - resets = <0x23 0x52 0x23 0x53>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xd6 0xd7 0xd8 0xd9>; - status = "okay"; - rockchip,clk-trcm = <0x01>; - phandle = <0xe8>; - }; - - i2s@fe420000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe420000 0x00 0x1000>; - interrupts = <0x00 0x36 0x04>; - clocks = <0x23 0x4f 0x23 0x4f 0x23 0x3b>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x04 0xd5 0x05>; - dma-names = "tx\0rx"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,clk-trcm = <0x01>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xda 0xdb 0xdc 0xdd>; - status = "disabled"; - }; - - i2s@fe430000 { - compatible = "rockchip,rk3568-i2s-tdm"; - reg = <0x00 0xfe430000 0x00 0x1000>; - interrupts = <0x00 0x37 0x04>; - clocks = <0x23 0x53 0x23 0x57 0x23 0x3c>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0xd5 0x06 0xd5 0x07>; - dma-names = "tx\0rx"; - resets = <0x23 0x55 0x23 0x56>; - reset-names = "tx-m\0rx-m"; - rockchip,cru = <0x23>; - rockchip,grf = <0x3b>; - rockchip,clk-trcm = <0x01>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xde 0xdf 0xe0 0xe1>; - status = "disabled"; - phandle = <0x144>; - }; - - pdm@fe440000 { - compatible = "rockchip,rk3568-pdm\0rockchip,pdm"; - reg = <0x00 0xfe440000 0x00 0x1000>; - clocks = <0x23 0x5a 0x23 0x59>; - clock-names = "pdm_clk\0pdm_hclk"; - dmas = <0xd5 0x09>; - dma-names = "rx"; - pinctrl-names = "default"; - pinctrl-0 = <0xe2 0xe3 0xe4 0xe5 0xe6 0xe7>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x149>; - }; - - vad@fe450000 { - compatible = "rockchip,rk3568-vad"; - reg = <0x00 0xfe450000 0x00 0x10000>; - reg-names = "vad"; - clocks = <0x23 0x5b>; - clock-names = "hclk"; - interrupts = <0x00 0x89 0x04>; - rockchip,audio-src = <0xe8>; - rockchip,det-channel = <0x00>; - rockchip,mode = <0x00>; - #sound-dai-cells = <0x00>; - status = "disabled"; - rockchip,buffer-time-ms = <0x80>; - phandle = <0x14e>; - }; - - spdif@fe460000 { - compatible = "rockchip,rk3568-spdif"; - reg = <0x00 0xfe460000 0x00 0x1000>; - interrupts = <0x00 0x66 0x04>; - dmas = <0xd5 0x01>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x23 0x5f 0x23 0x5c>; - #sound-dai-cells = <0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0xe9>; - status = "disabled"; - phandle = <0x14c>; - }; - - audpwm@fe470000 { - compatible = "rockchip,rk3568-audio-pwm\0rockchip,audio-pwm-v1"; - reg = <0x00 0xfe470000 0x00 0x1000>; - clocks = <0x23 0x63 0x23 0x60>; - clock-names = "clk\0hclk"; - dmas = <0xd5 0x08>; - dma-names = "tx"; - #sound-dai-cells = <0x00>; - rockchip,sample-width-bits = <0x0b>; - rockchip,interpolat-points = <0x01>; - status = "disabled"; - }; - - codec-digital@fe478000 { - compatible = "rockchip,rk3568-codec-digital\0rockchip,codec-digital-v1"; - reg = <0x00 0xfe478000 0x00 0x1000>; - clocks = <0x23 0x67 0x23 0x66 0x23 0x65 0x23 0x64>; - clock-names = "adc\0dac\0i2c\0pclk"; - pinctrl-names = "default"; - pinctrl-0 = <0xea>; - resets = <0x23 0x5f>; - reset-names = "reset"; - rockchip,grf = <0x3b>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x145>; - }; - - dmac@fe530000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfe530000 0x00 0x4000>; - interrupts = <0x00 0x0e 0x04 0x00 0x0d 0x04>; - clocks = <0x23 0x10d>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0x4e>; - }; - - dmac@fe550000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfe550000 0x00 0x4000>; - interrupts = <0x00 0x10 0x04 0x00 0x0f 0x04>; - clocks = <0x23 0x10d>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0xd5>; - }; - - rkscr@fe560000 { - compatible = "rockchip-scr"; - reg = <0x00 0xfe560000 0x00 0x10000>; - interrupts = <0x00 0x61 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xeb>; - clocks = <0x23 0x114>; - clock-names = "g_pclk_sim_card"; - status = "disabled"; - }; - - can@fe570000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe570000 0x00 0x1000>; - interrupts = <0x00 0x01 0x04>; - clocks = <0x23 0x141 0x23 0x140>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x155 0x23 0x154>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - }; - - can@fe580000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe580000 0x00 0x1000>; - interrupts = <0x00 0x02 0x04>; - clocks = <0x23 0x143 0x23 0x142>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x157 0x23 0x156>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "okay"; - assigned-clocks = <0x23 0x143>; - assigned-clock-rates = <0xbebc200>; - pinctrl-names = "default"; - pinctrl-0 = <0xec>; - }; - - can@fe590000 { - compatible = "rockchip,rk3568-can-2.0"; - reg = <0x00 0xfe590000 0x00 0x1000>; - interrupts = <0x00 0x03 0x04>; - clocks = <0x23 0x145 0x23 0x144>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x23 0x159 0x23 0x158>; - reset-names = "can\0can-apb"; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - assigned-clocks = <0x23 0x145>; - assigned-clock-rates = <0xbebc200>; - pinctrl-names = "default"; - pinctrl-0 = <0xed>; - }; - - i2c@fe5a0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5a0000 0x00 0x1000>; - clocks = <0x23 0x148 0x23 0x147>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x2f 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xee>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - clock-frequency = <0x186a0>; - - gpio@21 { - status = "disabled"; - compatible = "nxp,pca9555"; - reg = <0x21>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-group-num = <0xc8>; - phandle = <0x102>; - }; - - gt1x@14 { - status = "disabled"; - compatible = "goodix,gt1x"; - reg = <0x14>; - pinctrl-names = "default"; - pinctrl-0 = <0xef>; - goodix,rst-gpio = <0x3f 0x0e 0x00>; - goodix,irq-gpio = <0x3f 0x0d 0x08>; - }; - }; - - i2c@fe5b0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5b0000 0x00 0x1000>; - clocks = <0x23 0x14a 0x23 0x149>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x30 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf0>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@fe5c0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5c0000 0x00 0x1000>; - clocks = <0x23 0x14c 0x23 0x14b>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x31 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf1>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@fe5d0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5d0000 0x00 0x1000>; - clocks = <0x23 0x14e 0x23 0x14d>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x32 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xf2>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - clock-frequency = <0x61a80>; - - gc8034@37 { - compatible = "galaxycore,gc8034"; - status = "disabled"; - reg = <0x37>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x01>; - pwdn-gpios = <0xf4 0x0c 0x01>; - rockchip,grf = <0x3b>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "RK-CMK-8M-2-v1"; - rockchip,camera-module-lens-name = "CK8401"; - - port { - - endpoint { - remote-endpoint = <0xf5>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x130>; - }; - }; - }; - - os04a10@36 { - status = "disabled"; - compatible = "ovti,os04a10"; - reg = <0x36>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x01>; - pwdn-gpios = <0xf4 0x0c 0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT1607-FV1"; - rockchip,camera-module-lens-name = "M12-40IRC-4MP-F16"; - - port { - - endpoint { - remote-endpoint = <0xf6>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x12f>; - }; - }; - }; - - ov5695@36 { - status = "disabled"; - compatible = "ovti,ov5695"; - reg = <0x36>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x4a 0x0e 0x00>; - pwdn-gpios = <0xf4 0x0c 0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "TongJu"; - rockchip,camera-module-lens-name = "CHT842-MD"; - - port { - - endpoint { - remote-endpoint = <0xf7>; - data-lanes = <0x01 0x02>; - phandle = <0x131>; - }; - }; - }; - - XC7160b@1b { - status = "okay"; - compatible = "firefly,xc7160"; - reg = <0x1b>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x3f 0x1d 0x00>; - pwdn-gpios = <0xf4 0x0c 0x00>; - firefly,clkout-enabled-index = <0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "NC"; - rockchip,camera-module-lens-name = "NC"; - - port { - - endpoint { - remote-endpoint = <0xf8>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x132>; - }; - }; - }; - - imx415@37 { - status = "okay"; - compatible = "sony,imx415"; - reg = <0x37>; - clocks = <0x23 0xd6>; - clock-names = "xvclk"; - power-domains = <0x25 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0xf3>; - reset-gpios = <0x3f 0x1d 0x01>; - pwdn-gpios = <0xf4 0x0c 0x00>; - firefly,clkout-enabled-index = <0x00>; - rockchip,camera-module-index = <0x00>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT2022-PX1"; - rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; - - port { - - endpoint { - remote-endpoint = <0xf9>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0x133>; - }; - }; - }; - }; - - i2c@fe5e0000 { - compatible = "rockchip,rk3399-i2c"; - reg = <0x00 0xfe5e0000 0x00 0x1000>; - clocks = <0x23 0x150 0x23 0x14f>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x33 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0xfa>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - hym8563@51 { - status = "okay"; - compatible = "haoyu,hym8563"; - reg = <0x51>; - #clock-cells = <0x00>; - rtc-irq-gpio = <0x3f 0x1b 0x02>; - clock-frequency = <0x8000>; - }; - - mc3230sensor@4c { - compatible = "gs_mc3230"; - reg = <0x4c>; - type = <0x02>; - irq_enable = <0x00>; - poll_delay_ms = <0x1e>; - layout = <0x04>; - status = "okay"; - }; - - mxc6655xa@15 { - status = "disabled"; - compatible = "gs_mxc6655xa"; - pinctrl-names = "default"; - pinctrl-0 = <0xfb>; - reg = <0x15>; - irq-gpio = <0x4a 0x11 0x08>; - irq_enable = <0x00>; - poll_delay_ms = <0x1e>; - type = <0x02>; - power-off-in-suspend = <0x01>; - layout = <0x01>; - }; - }; - - timer@fe5f0000 { - compatible = "rockchip,rk3568-timer\0rockchip,rk3288-timer"; - reg = <0x00 0xfe5f0000 0x00 0x1000>; - interrupts = <0x00 0x6d 0x04>; - clocks = <0x23 0x16c 0x23 0x16d>; - clock-names = "pclk\0timer"; - }; - - watchdog@fe600000 { - compatible = "snps,dw-wdt"; - reg = <0x00 0xfe600000 0x00 0x100>; - clocks = <0x23 0x116 0x23 0x115>; - clock-names = "tclk\0pclk"; - interrupts = <0x00 0x95 0x04>; - status = "okay"; - }; - - spi@fe610000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe610000 0x00 0x1000>; - interrupts = <0x00 0x67 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x152 0x23 0x151>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x14 0x4e 0x15>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0xfc 0xfd 0xfe>; - pinctrl-1 = <0xfc 0xfd 0xff>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@fe620000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe620000 0x00 0x1000>; - interrupts = <0x00 0x68 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x154 0x23 0x153>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x16 0x4e 0x17>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x100>; - pinctrl-1 = <0x101>; - num-cs = <0x02>; - status = "disabled"; - max-freq = <0x2dc6c00>; - dev-port = <0x00>; - - spi_wk2xxx@0 { - status = "disabled"; - compatible = "firefly,spi-wk2xxx"; - reg = <0x00>; - spi-max-frequency = <0x989680>; - power-gpio = <0x102 0x0f 0x00>; - reset-gpio = <0x102 0x09 0x00>; - irq-gpio = <0x3f 0x06 0x02>; - cs-gpio = <0x4a 0x01 0x00>; - }; - }; - - spi@fe630000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe630000 0x00 0x1000>; - interrupts = <0x00 0x69 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x156 0x23 0x155>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x18 0x4e 0x19>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x103 0x104 0x105>; - pinctrl-1 = <0x103 0x104 0x106>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@fe640000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfe640000 0x00 0x1000>; - interrupts = <0x00 0x6a 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x23 0x158 0x23 0x157>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x4e 0x1a 0x4e 0x1b>; - dma-names = "tx\0rx"; - pinctrl-names = "default\0high_speed"; - pinctrl-0 = <0x107 0x108 0x109>; - pinctrl-1 = <0x107 0x108 0x10a>; - num-cs = <0x02>; - status = "disabled"; - }; - - serial@fe650000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe650000 0x00 0x100>; - interrupts = <0x00 0x75 0x04>; - clocks = <0x23 0x11f 0x23 0x11c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x02 0x4e 0x03>; - pinctrl-names = "default"; - pinctrl-0 = <0x10b>; - status = "disabled"; - }; - - serial@fe660000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe660000 0x00 0x100>; - interrupts = <0x00 0x76 0x04>; - clocks = <0x23 0x123 0x23 0x120>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x04 0x4e 0x05>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "disabled"; - }; - - serial@fe670000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe670000 0x00 0x100>; - interrupts = <0x00 0x77 0x04>; - clocks = <0x23 0x127 0x23 0x124>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x06 0x4e 0x07>; - pinctrl-names = "default"; - pinctrl-0 = <0x10d>; - status = "okay"; - }; - - serial@fe680000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe680000 0x00 0x100>; - interrupts = <0x00 0x78 0x04>; - clocks = <0x23 0x12b 0x23 0x128>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x08 0x4e 0x09>; - pinctrl-names = "default"; - pinctrl-0 = <0x10e>; - status = "okay"; - }; - - serial@fe690000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe690000 0x00 0x100>; - interrupts = <0x00 0x79 0x04>; - clocks = <0x23 0x12f 0x23 0x12c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0a 0x4e 0x0b>; - pinctrl-names = "default"; - pinctrl-0 = <0x10f>; - status = "disabled"; - }; - - serial@fe6a0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6a0000 0x00 0x100>; - interrupts = <0x00 0x7a 0x04>; - clocks = <0x23 0x133 0x23 0x130>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0c 0x4e 0x0d>; - pinctrl-names = "default"; - pinctrl-0 = <0x110>; - status = "disabled"; - }; - - serial@fe6b0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6b0000 0x00 0x100>; - interrupts = <0x00 0x7b 0x04>; - clocks = <0x23 0x137 0x23 0x134>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x0e 0x4e 0x0f>; - pinctrl-names = "default"; - pinctrl-0 = <0x111>; - status = "okay"; - }; - - serial@fe6c0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6c0000 0x00 0x100>; - interrupts = <0x00 0x7c 0x04>; - clocks = <0x23 0x13b 0x23 0x138>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x10 0x4e 0x11>; - pinctrl-names = "default"; - pinctrl-0 = <0x112 0x113>; - status = "okay"; - }; - - serial@fe6d0000 { - compatible = "rockchip,rk3568-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfe6d0000 0x00 0x100>; - interrupts = <0x00 0x7d 0x04>; - clocks = <0x23 0x13f 0x23 0x13c>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x4e 0x12 0x4e 0x13>; - pinctrl-names = "default"; - pinctrl-0 = <0x114>; - status = "okay"; - }; - - pwm@fe6e0000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0000 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x115>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - }; - - pwm@fe6e0010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0010 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x116>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - }; - - pwm@fe6e0020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6e0020 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x117>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6e0030 { - compatible = "rockchip,remotectl-pwm"; - reg = <0x00 0xfe6e0030 0x00 0x10>; - interrupts = <0x00 0x53 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "default"; - pinctrl-0 = <0x118>; - clocks = <0x23 0x15a 0x23 0x159>; - clock-names = "pwm\0pclk"; - status = "okay"; - remote_pwm_id = <0x03>; - handle_cpu_id = <0x01>; - remote_support_psci = <0x00>; - - ir_key_firefly { - rockchip,usercode = <0xff00>; - rockchip,key_table = <0xeb 0x74 0xec 0x8b 0xfe 0x9e 0xb7 0x66 0xa3 0x96 0xf4 0x73 0xa7 0x72 0xf8 0xe8 0xfc 0x67 0xfd 0x6c 0xf1 0x69 0xe5 0x6a>; - }; - }; - - pwm@fe6f0000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0000 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x119>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0010 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11a>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0020 0x00 0x10>; - interrupts = <0x00 0x54 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11b>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe6f0030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe6f0030 0x00 0x10>; - interrupts = <0x00 0x54 0x04 0x00 0x58 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11c>; - clocks = <0x23 0x15d 0x23 0x15c>; - clock-names = "pwm\0pclk"; - status = "disabled"; - phandle = <0x157>; - }; - - pwm@fe700000 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700000 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11d>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700010 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700010 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11e>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700020 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700020 0x00 0x10>; - interrupts = <0x00 0x55 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x11f>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fe700030 { - compatible = "rockchip,rk3568-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfe700030 0x00 0x10>; - interrupts = <0x00 0x55 0x04 0x00 0x59 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x120>; - clocks = <0x23 0x160 0x23 0x15f>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - tsadc@fe710000 { - compatible = "rockchip,rk3568-tsadc"; - reg = <0x00 0xfe710000 0x00 0x100>; - interrupts = <0x00 0x73 0x04>; - rockchip,grf = <0x3b>; - clocks = <0x23 0x111 0x23 0x10f>; - clock-names = "tsadc\0apb_pclk"; - assigned-clocks = <0x23 0x110 0x23 0x111>; - assigned-clock-rates = <0x1036640 0xaae60>; - resets = <0x23 0x182 0x23 0x181 0x23 0x1d7>; - reset-names = "tsadc\0tsadc-apb\0tsadc-phy"; - #thermal-sensor-cells = <0x01>; - nvmem-cells = <0x121 0x122>; - nvmem-cell-names = "trim_base\0trim_base_frac"; - rockchip,hw-tshut-temp = <0x1d4c0>; - rockchip,hw-tshut-mode = <0x00>; - rockchip,hw-tshut-polarity = <0x00>; - pinctrl-names = "gpio\0otpout"; - pinctrl-0 = <0x123>; - pinctrl-1 = <0x124>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x1f>; - - tsadc@0 { - reg = <0x00>; - nvmem-cells = <0x125 0x126>; - nvmem-cell-names = "trim_l\0trim_h"; - }; - - tsadc@1 { - reg = <0x01>; - nvmem-cells = <0x127 0x128>; - nvmem-cell-names = "trim_l\0trim_h"; - }; - }; - - saradc@fe720000 { - compatible = "rockchip,rk3568-saradc\0rockchip,rk3399-saradc"; - reg = <0x00 0xfe720000 0x00 0x100>; - interrupts = <0x00 0x5d 0x04>; - #io-channel-cells = <0x01>; - clocks = <0x23 0x113 0x23 0x112>; - clock-names = "saradc\0apb_pclk"; - resets = <0x23 0x180>; - reset-names = "saradc-apb"; - status = "okay"; - vref-supply = <0x129>; - phandle = <0x49>; - }; - - mailbox@fe780000 { - compatible = "rockchip,rk3568-mailbox\0rockchip,rk3368-mailbox"; - reg = <0x00 0xfe780000 0x00 0x1000>; - interrupts = <0x00 0xb7 0x04 0x00 0xb8 0x04 0x00 0xb9 0x04 0x00 0xba 0x04>; - clocks = <0x23 0x11b>; - clock-names = "pclk_mailbox"; - #mbox-cells = <0x01>; - status = "disabled"; - }; - - phy@fe820000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe820000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x1f 0x23 0x17c 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x1f>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c4 0x23 0x1c5>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12b>; - status = "okay"; - phandle = <0x24>; - }; - - phy@fe830000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe830000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x22 0x23 0x17d 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x22>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c6 0x23 0x1c7>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12c>; - status = "okay"; - phandle = <0x26>; - }; - - phy@fe840000 { - compatible = "rockchip,rk3568-naneng-combphy"; - reg = <0x00 0xfe840000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x3a 0x25 0x23 0x17e 0x23 0x7f>; - clock-names = "refclk\0apbclk\0pipe_clk"; - assigned-clocks = <0x3a 0x25>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x23 0x1c8 0x23 0x1c9>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x12a>; - rockchip,pipe-phy-grf = <0x12d>; - status = "okay"; - phandle = <0x27>; - }; - - phy@fe850000 { - compatible = "rockchip,rk3568-dsi-dphy\0rockchip,rk3568-video-phy"; - reg = <0x00 0xfe850000 0x00 0x10000 0x00 0xfe060000 0x00 0x10000>; - reg-names = "phy\0host"; - clocks = <0x3a 0x17 0x23 0x17a 0x23 0xe8>; - clock-names = "ref\0pclk\0pclk_host"; - #clock-cells = <0x00>; - resets = <0x23 0x1bb>; - reset-names = "apb"; - power-domains = <0x25 0x09>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x34>; - }; - - phy@fe860000 { - compatible = "rockchip,rk3568-dsi-dphy\0rockchip,rk3568-video-phy"; - reg = <0x00 0xfe860000 0x00 0x10000 0x00 0xfe070000 0x00 0x10000>; - reg-names = "phy\0host"; - clocks = <0x3a 0x19 0x23 0x17b 0x23 0xe9>; - clock-names = "ref\0pclk\0pclk_host"; - #clock-cells = <0x00>; - resets = <0x23 0x1bc>; - reset-names = "apb"; - power-domains = <0x25 0x09>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x36>; - }; - - csi2-dphy-hw@fe870000 { - compatible = "rockchip,rk3568-csi2-dphy-hw"; - reg = <0x00 0xfe870000 0x00 0x1000>; - clocks = <0x23 0x179>; - clock-names = "pclk"; - rockchip,grf = <0x3b>; - status = "okay"; - phandle = <0x12e>; - }; - - csi2-dphy0 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x12f>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf6>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x130>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf5>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0x131>; - data-lanes = <0x01 0x02>; - phandle = <0xf7>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0x132>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf8>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0x133>; - data-lanes = <0x01 0x02 0x03 0x04>; - phandle = <0xf9>; - }; - }; - - port@1 { - reg = <0x01>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x134>; - phandle = <0x8c>; - }; - }; - }; - }; - - csi2-dphy1 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "disabled"; - }; - - csi2-dphy2 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x12e>; - status = "disabled"; - }; - - usb2-phy@fe8a0000 { - compatible = "rockchip,rk3568-usb2phy"; - reg = <0x00 0xfe8a0000 0x00 0x10000>; - interrupts = <0x00 0x87 0x04>; - clocks = <0x3a 0x13>; - clock-names = "phyclk"; - #clock-cells = <0x00>; - assigned-clocks = <0x23 0x0b>; - assigned-clock-parents = <0x29>; - clock-output-names = "usb480m_phy"; - rockchip,usbgrf = <0x135>; - status = "okay"; - phandle = <0x29>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2b>; - }; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0x28>; - }; - }; - - usb2-phy@fe8b0000 { - compatible = "rockchip,rk3568-usb2phy"; - reg = <0x00 0xfe8b0000 0x00 0x10000>; - interrupts = <0x00 0x88 0x04>; - clocks = <0x3a 0x15>; - clock-names = "phyclk"; - #clock-cells = <0x00>; - rockchip,usbgrf = <0x137>; - status = "okay"; - phandle = <0x2c>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2e>; - }; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x136>; - phandle = <0x2d>; - }; - }; - - phy@fe8c0000 { - compatible = "rockchip,rk3568-pcie3-phy"; - reg = <0x00 0xfe8c0000 0x00 0x20000>; - #phy-cells = <0x00>; - clocks = <0x3a 0x26 0x3a 0x27 0x23 0x177>; - clock-names = "refclk_m\0refclk_n\0pclk"; - resets = <0x23 0x1be>; - reset-names = "phy"; - rockchip,phy-grf = <0x138>; - status = "okay"; - phandle = <0xc0>; - }; - - pinctrl { - compatible = "rockchip,rk3568-pinctrl"; - rockchip,grf = <0x3b>; - rockchip,pmu = <0x3c>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - gpio0@fdd60000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfdd60000 0x00 0x100>; - interrupts = <0x00 0x21 0x04>; - clocks = <0x3a 0x2e 0x3a 0x0c>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x3f>; - }; - - gpio1@fe740000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe740000 0x00 0x100>; - interrupts = <0x00 0x22 0x04>; - clocks = <0x23 0x163 0x23 0x164>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x159>; - }; - - gpio2@fe750000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe750000 0x00 0x100>; - interrupts = <0x00 0x23 0x04>; - clocks = <0x23 0x165 0x23 0x166>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x91>; - }; - - gpio3@fe760000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe760000 0x00 0x100>; - interrupts = <0x00 0x24 0x04>; - clocks = <0x23 0x167 0x23 0x168>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x4a>; - }; - - gpio4@fe770000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfe770000 0x00 0x100>; - interrupts = <0x00 0x25 0x04>; - clocks = <0x23 0x169 0x23 0x16a>; - gpio-controller; - #gpio-cells = <0x02>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0xf4>; - }; - - pcfg-pull-up { - bias-pull-up; - phandle = <0x13b>; - }; - - pcfg-pull-down { - bias-pull-down; - phandle = <0x142>; - }; - - pcfg-pull-none { - bias-disable; - phandle = <0x139>; - }; - - pcfg-pull-none-drv-level-1 { - bias-disable; - drive-strength = <0x01>; - phandle = <0x13d>; - }; - - pcfg-pull-none-drv-level-2 { - bias-disable; - drive-strength = <0x02>; - phandle = <0x13c>; - }; - - pcfg-pull-none-drv-level-3 { - bias-disable; - drive-strength = <0x03>; - phandle = <0x141>; - }; - - pcfg-pull-up-drv-level-1 { - bias-pull-up; - drive-strength = <0x01>; - phandle = <0x140>; - }; - - pcfg-pull-up-drv-level-2 { - bias-pull-up; - drive-strength = <0x02>; - phandle = <0x13a>; - }; - - pcfg-pull-none-smt { - bias-disable; - input-schmitt-enable; - phandle = <0x13e>; - }; - - pcfg-output-low-pull-down { - output-low; - bias-pull-down; - phandle = <0x13f>; - }; - - acodec { - - acodec-pins { - rockchip,pins = <0x01 0x09 0x05 0x139 0x01 0x01 0x05 0x139 0x01 0x00 0x05 0x139 0x01 0x07 0x05 0x139 0x01 0x08 0x05 0x139 0x01 0x03 0x05 0x139 0x01 0x05 0x05 0x139>; - phandle = <0xea>; - }; - }; - - cam { - - vcc-cam { - rockchip,pins = <0x00 0x11 0x00 0x139>; - phandle = <0x158>; - }; - }; - - can1 { - - can1m1-pins { - rockchip,pins = <0x04 0x12 0x03 0x139 0x04 0x13 0x03 0x139>; - phandle = <0xec>; - }; - }; - - can2 { - - can2m0-pins { - rockchip,pins = <0x04 0x0c 0x03 0x139 0x04 0x0d 0x03 0x139>; - phandle = <0xed>; - }; - }; - - cif { - - cif-clk { - rockchip,pins = <0x04 0x10 0x01 0x139>; - phandle = <0xf3>; - }; - }; - - clk32k { - - clk32k-out0 { - rockchip,pins = <0x00 0x08 0x02 0x139>; - phandle = <0x22>; - }; - }; - - ebc { - - ebc-pins { - rockchip,pins = <0x04 0x10 0x02 0x139 0x04 0x0b 0x02 0x139 0x04 0x0c 0x02 0x139 0x04 0x06 0x02 0x139 0x04 0x11 0x02 0x139 0x03 0x16 0x02 0x139 0x03 0x17 0x02 0x139 0x03 0x18 0x02 0x139 0x03 0x19 0x02 0x139 0x03 0x1a 0x02 0x139 0x03 0x1b 0x02 0x139 0x03 0x1c 0x02 0x139 0x03 0x1d 0x02 0x139 0x03 0x1e 0x02 0x139 0x03 0x1f 0x02 0x139 0x04 0x00 0x02 0x139 0x04 0x01 0x02 0x139 0x04 0x02 0x02 0x139 0x04 0x03 0x02 0x139 0x04 0x04 0x02 0x139 0x04 0x05 0x02 0x139 0x04 0x0e 0x02 0x139 0x04 0x0f 0x02 0x139>; - phandle = <0x7c>; - }; - }; - - fspi { - - fspi-pins { - rockchip,pins = <0x01 0x18 0x01 0x139 0x01 0x1b 0x01 0x139 0x01 0x19 0x01 0x139 0x01 0x1a 0x01 0x139 0x01 0x17 0x02 0x139 0x01 0x1c 0x01 0x139>; - phandle = <0xd4>; - }; - }; - - gmac0 { - - gmac0-miim { - rockchip,pins = <0x02 0x13 0x02 0x139 0x02 0x14 0x02 0x139>; - phandle = <0xc8>; - }; - - gmac0-clkinout { - rockchip,pins = <0x02 0x12 0x02 0x139>; - phandle = <0xcd>; - }; - - gmac0-rx-bus2 { - rockchip,pins = <0x02 0x0e 0x01 0x139 0x02 0x0f 0x02 0x139 0x02 0x10 0x02 0x139>; - phandle = <0xca>; - }; - - gmac0-tx-bus2 { - rockchip,pins = <0x02 0x0b 0x01 0x13c 0x02 0x0c 0x01 0x13c 0x02 0x0d 0x01 0x139>; - phandle = <0xc9>; - }; - - gmac0-rgmii-clk { - rockchip,pins = <0x02 0x05 0x02 0x139 0x02 0x08 0x02 0x13d>; - phandle = <0xcb>; - }; - - gmac0-rgmii-bus { - rockchip,pins = <0x02 0x03 0x02 0x139 0x02 0x04 0x02 0x139 0x02 0x06 0x02 0x13c 0x02 0x07 0x02 0x13c>; - phandle = <0xcc>; - }; - }; - - gmac1 { - - gmac1m1-miim { - rockchip,pins = <0x04 0x0e 0x03 0x139 0x04 0x0f 0x03 0x139>; - phandle = <0x93>; - }; - - gmac1m1-clkinout { - rockchip,pins = <0x04 0x11 0x03 0x139>; - phandle = <0x98>; - }; - - gmac1m1-rx-bus2 { - rockchip,pins = <0x04 0x07 0x03 0x139 0x04 0x08 0x03 0x139 0x04 0x09 0x03 0x139>; - phandle = <0x95>; - }; - - gmac1m1-tx-bus2 { - rockchip,pins = <0x04 0x04 0x03 0x13c 0x04 0x05 0x03 0x13c 0x04 0x06 0x03 0x139>; - phandle = <0x94>; - }; - - gmac1m1-rgmii-clk { - rockchip,pins = <0x04 0x03 0x03 0x139 0x04 0x00 0x03 0x13d>; - phandle = <0x96>; - }; - - gmac1m1-rgmii-bus { - rockchip,pins = <0x04 0x01 0x03 0x139 0x04 0x02 0x03 0x139 0x03 0x1e 0x03 0x13c 0x03 0x1f 0x03 0x13c>; - phandle = <0x97>; - }; - }; - - hdmitx { - - hdmitxm0-cec { - rockchip,pins = <0x04 0x19 0x01 0x139>; - phandle = <0xac>; - }; - - hdmitx-scl { - rockchip,pins = <0x04 0x17 0x01 0x139>; - phandle = <0xaa>; - }; - - hdmitx-sda { - rockchip,pins = <0x04 0x18 0x01 0x139>; - phandle = <0xab>; - }; - }; - - i2c0 { - - i2c0-xfer { - rockchip,pins = <0x00 0x09 0x01 0x13e 0x00 0x0a 0x01 0x13e>; - phandle = <0x3d>; - }; - }; - - i2c1 { - - i2c1-xfer { - rockchip,pins = <0x00 0x0b 0x01 0x13e 0x00 0x0c 0x01 0x13e>; - phandle = <0xee>; - }; - }; - - i2c2 { - - i2c2m0-xfer { - rockchip,pins = <0x00 0x0d 0x01 0x13e 0x00 0x0e 0x01 0x13e>; - phandle = <0xf0>; - }; - }; - - i2c3 { - - i2c3m0-xfer { - rockchip,pins = <0x01 0x01 0x01 0x13e 0x01 0x00 0x01 0x13e>; - phandle = <0xf1>; - }; - }; - - i2c4 { - - i2c4m0-xfer { - rockchip,pins = <0x04 0x0b 0x01 0x13e 0x04 0x0a 0x01 0x13e>; - phandle = <0xf2>; - }; - }; - - i2c5 { - - i2c5m0-xfer { - rockchip,pins = <0x03 0x0b 0x04 0x13e 0x03 0x0c 0x04 0x13e>; - phandle = <0xfa>; - }; - }; - - i2s1 { - - i2s1m0-lrcktx { - rockchip,pins = <0x01 0x05 0x01 0x13e>; - phandle = <0xd7>; - }; - - i2s1m0-mclk { - rockchip,pins = <0x01 0x02 0x01 0x13e>; - phandle = <0x47>; - }; - - i2s1m0-sclktx { - rockchip,pins = <0x01 0x03 0x01 0x13e>; - phandle = <0xd6>; - }; - - i2s1m0-sdi0 { - rockchip,pins = <0x01 0x0b 0x01 0x139>; - phandle = <0xd8>; - }; - - i2s1m0-sdo0 { - rockchip,pins = <0x01 0x07 0x01 0x139>; - phandle = <0xd9>; - }; - }; - - i2s2 { - - i2s2m0-lrcktx { - rockchip,pins = <0x02 0x13 0x01 0x13e>; - phandle = <0xdb>; - }; - - i2s2m0-sclktx { - rockchip,pins = <0x02 0x12 0x01 0x13e>; - phandle = <0xda>; - }; - - i2s2m0-sdi { - rockchip,pins = <0x02 0x15 0x01 0x139>; - phandle = <0xdc>; - }; - - i2s2m0-sdo { - rockchip,pins = <0x02 0x14 0x01 0x139>; - phandle = <0xdd>; - }; - }; - - i2s3 { - - i2s3m0-lrck { - rockchip,pins = <0x03 0x04 0x04 0x13e>; - phandle = <0xdf>; - }; - - i2s3m0-sclk { - rockchip,pins = <0x03 0x03 0x04 0x13e>; - phandle = <0xde>; - }; - - i2s3m0-sdi { - rockchip,pins = <0x03 0x06 0x04 0x139>; - phandle = <0xe0>; - }; - - i2s3m0-sdo { - rockchip,pins = <0x03 0x05 0x04 0x139>; - phandle = <0xe1>; - }; - }; - - lcdc { - - lcdc-ctl { - rockchip,pins = <0x03 0x00 0x01 0x139 0x02 0x18 0x01 0x139 0x02 0x19 0x01 0x139 0x02 0x1a 0x01 0x139 0x02 0x1b 0x01 0x139 0x02 0x1c 0x01 0x139 0x02 0x1d 0x01 0x139 0x02 0x1e 0x01 0x139 0x02 0x1f 0x01 0x139 0x03 0x01 0x01 0x139 0x03 0x02 0x01 0x139 0x03 0x03 0x01 0x139 0x03 0x04 0x01 0x139 0x03 0x05 0x01 0x139 0x03 0x06 0x01 0x139 0x03 0x07 0x01 0x139 0x03 0x08 0x01 0x139 0x03 0x09 0x01 0x139 0x03 0x0a 0x01 0x139 0x03 0x0b 0x01 0x139 0x03 0x0c 0x01 0x139 0x03 0x0d 0x01 0x139 0x03 0x0e 0x01 0x139 0x03 0x0f 0x01 0x139 0x03 0x10 0x01 0x139 0x03 0x13 0x01 0x139 0x03 0x11 0x01 0x139 0x03 0x12 0x01 0x139>; - phandle = <0x39>; - }; - }; - - pdm { - - pdmm0-clk { - rockchip,pins = <0x01 0x06 0x03 0x139>; - phandle = <0xe2>; - }; - - pdmm0-clk1 { - rockchip,pins = <0x01 0x04 0x03 0x139>; - phandle = <0xe3>; - }; - - pdmm0-sdi0 { - rockchip,pins = <0x01 0x0b 0x02 0x139>; - phandle = <0xe4>; - }; - - pdmm0-sdi1 { - rockchip,pins = <0x01 0x0a 0x03 0x139>; - phandle = <0xe5>; - }; - - pdmm0-sdi2 { - rockchip,pins = <0x01 0x09 0x03 0x139>; - phandle = <0xe6>; - }; - - pdmm0-sdi3 { - rockchip,pins = <0x01 0x08 0x03 0x139>; - phandle = <0xe7>; - }; - }; - - pmic { - - pmic_int { - rockchip,pins = <0x00 0x03 0x00 0x13b>; - phandle = <0x40>; - }; - - soc_slppin_gpio { - rockchip,pins = <0x00 0x02 0x00 0x13f>; - phandle = <0x43>; - }; - - soc_slppin_slp { - rockchip,pins = <0x00 0x02 0x01 0x13b>; - phandle = <0x41>; - }; - - soc_slppin_rst { - rockchip,pins = <0x00 0x02 0x02 0x139>; - }; - - spk_ctl_gpio { - rockchip,pins = <0x03 0x15 0x00 0x13b>; - phandle = <0x48>; - }; - }; - - pwm0 { - - pwm0m0-pins { - rockchip,pins = <0x00 0x0f 0x01 0x139>; - phandle = <0x50>; - }; - }; - - pwm1 { - - pwm1m0-pins { - rockchip,pins = <0x00 0x10 0x01 0x139>; - phandle = <0x51>; - }; - }; - - pwm2 { - - pwm2m0-pins { - rockchip,pins = <0x00 0x11 0x01 0x139>; - phandle = <0x52>; - }; - }; - - pwm3 { - - pwm3-pins { - rockchip,pins = <0x00 0x12 0x01 0x139>; - phandle = <0x53>; - }; - }; - - pwm4 { - - pwm4-pins { - rockchip,pins = <0x00 0x13 0x01 0x139>; - phandle = <0x115>; - }; - }; - - pwm5 { - - pwm5-pins { - rockchip,pins = <0x00 0x14 0x01 0x139>; - phandle = <0x116>; - }; - }; - - pwm6 { - - pwm6-pins { - rockchip,pins = <0x00 0x15 0x01 0x139>; - phandle = <0x117>; - }; - }; - - pwm7 { - - pwm7-pins { - rockchip,pins = <0x00 0x16 0x01 0x139>; - phandle = <0x118>; - }; - }; - - pwm8 { - - pwm8m0-pins { - rockchip,pins = <0x03 0x09 0x05 0x139>; - phandle = <0x119>; - }; - }; - - pwm9 { - - pwm9m0-pins { - rockchip,pins = <0x03 0x0a 0x05 0x139>; - phandle = <0x11a>; - }; - }; - - pwm10 { - - pwm10m0-pins { - rockchip,pins = <0x03 0x0d 0x05 0x139>; - phandle = <0x11b>; - }; - }; - - pwm11 { - - pwm11m0-pins { - rockchip,pins = <0x03 0x0e 0x05 0x139>; - phandle = <0x11c>; - }; - }; - - pwm12 { - - pwm12m0-pins { - rockchip,pins = <0x03 0x0f 0x02 0x139>; - phandle = <0x11d>; - }; - }; - - pwm13 { - - pwm13m0-pins { - rockchip,pins = <0x03 0x10 0x02 0x139>; - phandle = <0x11e>; - }; - }; - - pwm14 { - - pwm14m0-pins { - rockchip,pins = <0x03 0x14 0x01 0x139>; - phandle = <0x11f>; - }; - }; - - pwm15 { - - pwm15m0-pins { - rockchip,pins = <0x03 0x15 0x01 0x139>; - phandle = <0x120>; - }; - }; - - scr { - - scr-pins { - rockchip,pins = <0x01 0x02 0x03 0x139 0x01 0x07 0x03 0x13b 0x01 0x03 0x03 0x13b 0x01 0x05 0x03 0x139>; - phandle = <0xeb>; - }; - }; - - sdmmc0 { - - sdmmc0-bus4 { - rockchip,pins = <0x01 0x1d 0x01 0x13a 0x01 0x1e 0x01 0x13a 0x01 0x1f 0x01 0x13a 0x02 0x00 0x01 0x13a>; - phandle = <0xd0>; - }; - - sdmmc0-clk { - rockchip,pins = <0x02 0x02 0x01 0x13a>; - phandle = <0xd1>; - }; - - sdmmc0-cmd { - rockchip,pins = <0x02 0x01 0x01 0x13a>; - phandle = <0xd2>; - }; - - sdmmc0-det { - rockchip,pins = <0x00 0x04 0x01 0x13b>; - phandle = <0xd3>; - }; - }; - - sdmmc2 { - - sdmmc2m0-bus4 { - rockchip,pins = <0x03 0x16 0x03 0x13a 0x03 0x17 0x03 0x13a 0x03 0x18 0x03 0x13a 0x03 0x19 0x03 0x13a>; - phandle = <0xb0>; - }; - - sdmmc2m0-clk { - rockchip,pins = <0x03 0x1b 0x03 0x13a>; - phandle = <0xb2>; - }; - - sdmmc2m0-cmd { - rockchip,pins = <0x03 0x1a 0x03 0x13a>; - phandle = <0xb1>; - }; - }; - - spdif { - - spdifm1-tx { - rockchip,pins = <0x03 0x15 0x02 0x139>; - phandle = <0xe9>; - }; - }; - - spi0 { - - spi0m0-pins { - rockchip,pins = <0x00 0x0d 0x02 0x139 0x00 0x15 0x02 0x139 0x00 0x0e 0x02 0x139>; - phandle = <0xfe>; - }; - - spi0m0-cs0 { - rockchip,pins = <0x00 0x16 0x02 0x139>; - phandle = <0xfc>; - }; - - spi0m0-cs1 { - rockchip,pins = <0x00 0x14 0x02 0x139>; - phandle = <0xfd>; - }; - }; - - spi1 { - - spi1m1-pins { - rockchip,pins = <0x03 0x13 0x03 0x139 0x03 0x12 0x03 0x139 0x03 0x11 0x03 0x139>; - phandle = <0x100>; - }; - }; - - spi2 { - - spi2m0-pins { - rockchip,pins = <0x02 0x11 0x04 0x139 0x02 0x12 0x04 0x139 0x02 0x13 0x04 0x139>; - phandle = <0x105>; - }; - - spi2m0-cs0 { - rockchip,pins = <0x02 0x14 0x04 0x139>; - phandle = <0x103>; - }; - - spi2m0-cs1 { - rockchip,pins = <0x02 0x15 0x04 0x139>; - phandle = <0x104>; - }; - }; - - spi3 { - - spi3m0-pins { - rockchip,pins = <0x04 0x0b 0x04 0x139 0x04 0x08 0x04 0x139 0x04 0x0a 0x04 0x139>; - phandle = <0x109>; - }; - - spi3m0-cs0 { - rockchip,pins = <0x04 0x06 0x04 0x139>; - phandle = <0x107>; - }; - - spi3m0-cs1 { - rockchip,pins = <0x04 0x07 0x04 0x139>; - phandle = <0x108>; - }; - }; - - tsadc { - - tsadc-shutorg { - rockchip,pins = <0x00 0x01 0x02 0x139>; - phandle = <0x124>; - }; - }; - - uart0 { - - uart0-xfer { - rockchip,pins = <0x00 0x10 0x03 0x13b 0x00 0x11 0x03 0x13b>; - phandle = <0x4f>; - }; - }; - - uart1 { - - uart1m0-xfer { - rockchip,pins = <0x02 0x0b 0x02 0x13b 0x02 0x0c 0x02 0x13b>; - phandle = <0x10b>; - }; - }; - - uart2 { - - uart2m0-xfer { - rockchip,pins = <0x00 0x18 0x01 0x13b 0x00 0x19 0x01 0x13b>; - phandle = <0x10c>; - }; - }; - - uart3 { - - uart3m1-xfer { - rockchip,pins = <0x03 0x10 0x04 0x13b 0x03 0x0f 0x04 0x13b>; - phandle = <0x10d>; - }; - }; - - uart4 { - - uart4m1-xfer { - rockchip,pins = <0x03 0x09 0x04 0x13b 0x03 0x0a 0x04 0x13b>; - phandle = <0x10e>; - }; - }; - - uart5 { - - uart5m0-xfer { - rockchip,pins = <0x02 0x01 0x03 0x13b 0x02 0x02 0x03 0x13b>; - phandle = <0x10f>; - }; - }; - - uart6 { - - uart6m0-xfer { - rockchip,pins = <0x02 0x03 0x03 0x13b 0x02 0x04 0x03 0x13b>; - phandle = <0x110>; - }; - }; - - uart7 { - - uart7m1-xfer { - rockchip,pins = <0x03 0x15 0x04 0x13b 0x03 0x14 0x04 0x13b>; - phandle = <0x111>; - }; - }; - - uart8 { - - uart8m0-xfer { - rockchip,pins = <0x02 0x16 0x02 0x13b 0x02 0x15 0x03 0x13b>; - phandle = <0x112>; - }; - - uart8m0-ctsn { - rockchip,pins = <0x02 0x0a 0x03 0x139>; - phandle = <0x113>; - }; - - uart8m0-rtsn { - rockchip,pins = <0x02 0x09 0x03 0x139>; - phandle = <0x155>; - }; - }; - - uart9 { - - uart9m1-xfer { - rockchip,pins = <0x04 0x16 0x04 0x13b 0x04 0x15 0x04 0x13b>; - phandle = <0x114>; - }; - }; - - spi0-hs { - - spi0m0-pins { - rockchip,pins = <0x00 0x0d 0x02 0x140 0x00 0x15 0x02 0x140 0x00 0x0e 0x02 0x140>; - phandle = <0xff>; - }; - }; - - spi1-hs { - - spi1m1-pins { - rockchip,pins = <0x03 0x13 0x03 0x140 0x03 0x12 0x03 0x140 0x03 0x11 0x03 0x140>; - phandle = <0x101>; - }; - }; - - spi2-hs { - - spi2m0-pins { - rockchip,pins = <0x02 0x11 0x04 0x140 0x02 0x12 0x04 0x140 0x02 0x13 0x04 0x140>; - phandle = <0x106>; - }; - }; - - spi3-hs { - - spi3m0-pins { - rockchip,pins = <0x04 0x0b 0x04 0x140 0x04 0x08 0x04 0x140 0x04 0x0a 0x04 0x140>; - phandle = <0x10a>; - }; - }; - - gpio-func { - - tsadc-gpio-func { - rockchip,pins = <0x00 0x01 0x00 0x139>; - phandle = <0x123>; - }; - }; - - usb { - - vcc5v0-host-en { - rockchip,pins = <0x00 0x06 0x00 0x139>; - phandle = <0x150>; - }; - - vcc5v0-otg-en { - rockchip,pins = <0x00 0x05 0x00 0x139>; - phandle = <0x151>; - }; - - vcc-hub-reset-en { - rockchip,pins = <0x01 0x04 0x00 0x139>; - phandle = <0x15a>; - }; - }; - - headphone { - - hp-det { - rockchip,pins = <0x03 0x12 0x00 0x142>; - phandle = <0x148>; - }; - }; - - sdio-pwrseq { - - wifi-enable-h { - rockchip,pins = <0x03 0x1d 0x00 0x139>; - phandle = <0x153>; - }; - }; - - wireless-wlan { - - wifi-host-wake-irq { - rockchip,pins = <0x03 0x1c 0x00 0x142>; - phandle = <0x154>; - }; - }; - - wireless-bluetooth { - - uart8-gpios { - rockchip,pins = <0x02 0x09 0x00 0x139>; - phandle = <0x156>; - }; - }; - - touch { - - touch-gpio { - rockchip,pins = <0x00 0x0d 0x00 0x13b 0x00 0x0e 0x00 0x139>; - phandle = <0xef>; - }; - }; - - mxc6655xa { - - mxc6655xa_irq_gpio { - rockchip,pins = <0x03 0x11 0x00 0x139>; - phandle = <0xfb>; - }; - }; - - pcie { - - pcie-pi6c-oe-en { - rockchip,pins = <0x03 0x07 0x00 0x139>; - phandle = <0x15b>; - }; - }; - - leds { - - leds-gpio { - rockchip,pins = <0x01 0x0a 0x00 0x139 0x01 0x09 0x00 0x139 0x01 0x08 0x00 0x139 0x02 0x11 0x00 0x139>; - phandle = <0x15d>; - }; - }; - - 4g { - - vcc-4g-power-en { - rockchip,pins = <0x03 0x03 0x00 0x139>; - phandle = <0x15c>; - }; - }; - - usb-typec { - - usbc0-int { - rockchip,pins = <0x00 0x11 0x00 0x13b>; - phandle = <0x4b>; - }; - - vcc5v0-typec0-en { - rockchip,pins = <0x00 0x05 0x00 0x139>; - }; - }; - }; - - audiopwmout-diff { - status = "disabled"; - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "rockchip,audiopwmout-diff"; - simple-audio-card,mclk-fs = <0x100>; - simple-audio-card,bitclock-master = <0x143>; - simple-audio-card,frame-master = <0x143>; - - simple-audio-card,cpu { - sound-dai = <0x144>; - }; - - simple-audio-card,codec { - sound-dai = <0x145>; - phandle = <0x143>; - }; - }; - - dc-12v { - compatible = "regulator-fixed"; - regulator-name = "dc_12v"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b00>; - regulator-max-microvolt = <0xb71b00>; - phandle = <0x14f>; - }; - - hdmi-sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,mclk-fs = <0x80>; - simple-audio-card,name = "rockchip,hdmi"; - status = "okay"; - - simple-audio-card,cpu { - sound-dai = <0x146>; - }; - - simple-audio-card,codec { - sound-dai = <0x147>; - }; - }; - - rk-headset { - status = "disabled"; - compatible = "rockchip_headset"; - headset_gpio = <0x4a 0x12 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x148>; - }; - - dummy-codec { - status = "disabled"; - compatible = "rockchip,dummy-codec"; - #sound-dai-cells = <0x00>; - phandle = <0x14a>; - }; - - pdm-mic-array { - status = "disabled"; - compatible = "simple-audio-card"; - simple-audio-card,name = "rockchip,pdm-mic-array"; - - simple-audio-card,cpu { - sound-dai = <0x149>; - }; - - simple-audio-card,codec { - sound-dai = <0x14a>; - }; - }; - - rk809-sound { - status = "okay"; - compatible = "rockchip,multicodecs-card"; - rockchip,card-name = "rockchip-rk809"; - rockchip,format = "i2s"; - rockchip,mclk-fs = <0x100>; - rockchip,cpu = <0xe8>; - rockchip,codec = <0x14b>; - }; - - spdif-sound { - status = "okay"; - compatible = "simple-audio-card"; - simple-audio-card,name = "ROCKCHIP,SPDIF"; - - simple-audio-card,cpu { - sound-dai = <0x14c>; - }; - - simple-audio-card,codec { - sound-dai = <0x14d>; - }; - }; - - spdif-out { - status = "okay"; - compatible = "linux,spdif-dit"; - #sound-dai-cells = <0x00>; - phandle = <0x14d>; - }; - - vad-sound { - status = "disabled"; - compatible = "rockchip,multicodecs-card"; - rockchip,card-name = "rockchip,rk3568-vad"; - rockchip,cpu = <0xe8>; - rockchip,codec = <0x14b 0x14e>; - }; - - vcc3v3-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - vin-supply = <0x14f>; - phandle = <0x46>; - }; - - vcc5v0-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - vin-supply = <0x14f>; - phandle = <0x3e>; - }; - - vcc5v0-host-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x3f 0x06 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x150>; - regulator-name = "vcc5v0_host"; - regulator-always-on; - regulator-boot-on; - phandle = <0x136>; - }; - - vcc5v0-otg-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x3f 0x05 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x151>; - regulator-name = "vcc5v0_otg"; - phandle = <0x4c>; - }; - - vcc3v3-lcd0-n { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_lcd0_n"; - regulator-boot-on; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - vcc3v3-lcd1-n { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_lcd1_n"; - regulator-boot-on; - status = "disabled"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - test-power { - status = "okay"; - }; - - chosen { - // linux,initrd-end = <0x00 0xaacf15d>; - // linux,initrd-start = <0x00 0xa200000>; - bootargs = "storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal androidboot.verifiedbootstate=orange rw rootwait earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 root=PARTLABEL=rootfs rootfstype=ext4 overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 swiotlb=0x10000 net.ifnames=0 comm-05/20/2025 androidboot.fwver=ddr-v1.21-2d653b3476,spl-v1.14,bl31-v1.44,bl32-v2.12,uboot--boot"; - }; - - fiq-debugger { - compatible = "rockchip,fiq-debugger"; - rockchip,serial-id = <0x02>; - rockchip,wake-irq = <0x00>; - rockchip,irq-mode-enable = <0x01>; - rockchip,baudrate = <0x16e360>; - interrupts = <0x00 0xfc 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0x10c>; - status = "okay"; - }; - - debug@fd904000 { - compatible = "rockchip,debug"; - reg = <0x00 0xfd904000 0x00 0x1000 0x00 0xfd905000 0x00 0x1000 0x00 0xfd906000 0x00 0x1000 0x00 0xfd907000 0x00 0x1000>; - }; - - cspmu@fd90c000 { - compatible = "rockchip,cspmu"; - reg = <0x00 0xfd90c000 0x00 0x1000 0x00 0xfd90d000 0x00 0x1000 0x00 0xfd90e000 0x00 0x1000 0x00 0xfd90f000 0x00 0x1000>; - }; - - adc-keys { - compatible = "adc-keys"; - io-channels = <0x49 0x00>; - io-channel-names = "buttons"; - keyup-threshold-microvolt = <0x1b7740>; - poll-interval = <0x64>; - - recovery-key { - label = "F12"; - linux,code = <0x58>; - press-threshold-microvolt = <0x6d6>; - }; - - vol-down-key { - label = "volume down"; - linux,code = <0x72>; - press-threshold-microvolt = <0x48a1c>; - }; - - menu-key { - label = "menu"; - linux,code = <0x8b>; - press-threshold-microvolt = <0xef420>; - }; - - back-key { - label = "back"; - linux,code = <0x9e>; - press-threshold-microvolt = <0x13eb9c>; - }; - }; - - vcc2v5-ddr { - compatible = "regulator-fixed"; - regulator-name = "vcc2v5-sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x2625a0>; - regulator-max-microvolt = <0x2625a0>; - vin-supply = <0x46>; - }; - - pcie30-avdd0v9 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd0v9"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xdbba0>; - regulator-max-microvolt = <0xdbba0>; - vin-supply = <0x46>; - }; - - pcie30-avdd1v8 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd1v8"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - vin-supply = <0x46>; - }; - - gpio-regulator { - compatible = "regulator-gpio"; - regulator-name = "pcie30_3v3"; - regulator-min-microvolt = <0x186a0>; - regulator-max-microvolt = <0x325aa0>; - gpios = <0x3f 0x1c 0x00>; - gpios-states = <0x01>; - states = <0x186a0 0x00 0x325aa0 0x01>; - phandle = <0xc2>; - }; - - vcc3v3-bu { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_bu"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - vin-supply = <0x3e>; - }; - - sdio-pwrseq { - compatible = "mmc-pwrseq-simple"; - clocks = <0x152 0x01>; - clock-names = "ext_clock"; - pinctrl-names = "default"; - pinctrl-0 = <0x153>; - post-power-on-delay-ms = <0x64>; - reset-gpios = <0x4a 0x1d 0x01>; - status = "okay"; - phandle = <0xb3>; - }; - - wireless-wlan { - compatible = "wlan-platdata"; - rockchip,grf = <0x3b>; - wifi_chip_type = "ap6256"; - pinctrl-names = "default"; - pinctrl-0 = <0x154>; - WIFI,host_wake_irq = <0x4a 0x1c 0x00>; - status = "okay"; - }; - - wireless-bluetooth { - compatible = "bluetooth-platdata"; - clocks = <0x152 0x01>; - clock-names = "ext_clock"; - uart_rts_gpios = <0x91 0x09 0x01>; - pinctrl-names = "default\0rts_gpio"; - pinctrl-0 = <0x155>; - pinctrl-1 = <0x156>; - BT,reset_gpio = <0x4a 0x00 0x00>; - BT,wake_gpio = <0x4a 0x02 0x00>; - BT,wake_host_irq = <0x4a 0x01 0x00>; - status = "okay"; - }; - - flash-led { - compatible = "led,rgb13h"; - label = "pwm-flash-led"; - led-max-microamp = <0x4e20>; - flash-max-microamp = <0x4e20>; - flash-max-timeout-us = <0xf4240>; - pwms = <0x157 0x00 0x61a8 0x00>; - rockchip,camera-module-index = <0x01>; - rockchip,camera-module-facing = "front"; - status = "disabled"; - }; - - vcc-camera-regulator { - compatible = "regulator-fixed"; - gpio = <0x102 0x03 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x158>; - regulator-name = "vcc_camera"; - enable-active-high; - status = "disabled"; - }; - - vcc-hub-reset-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x159 0x04 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x15a>; - regulator-name = "vcc_hub_reset_en"; - regulator-always-on; - }; - - pcie-pi6c-oe-regulator { - compatible = "regulator-fixed"; - gpio = <0x4a 0x07 0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x15b>; - regulator-name = "pcie_pi6c_oe_en"; - regulator-always-on; - }; - - vcc-4g-power-regulator { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <0x4a 0x03 0x00>; - pinctrl-names = "default"; - pinctrl-0 = <0x15c>; - regulator-name = "vcc_4g_power_en"; - regulator-always-on; - }; - - leds { - status = "okay"; - compatible = "gpio-leds"; - pinctrl-names = "default"; - pinctrl-0 = <0x15d>; - - power { - label = "firefly:blue:power"; - linux,default-trigger = "ir-power-click"; - default-state = "on"; - gpios = <0x159 0x0a 0x00>; - }; - - user { - label = "firefly:yellow:user"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x159 0x09 0x00>; - }; - - diy1 { - label = "firefly:green:diy"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x159 0x08 0x00>; - }; - - diy2 { - label = "firefly:yellow:diy"; - linux,default-trigger = "ir-user-click"; - default-state = "off"; - gpios = <0x91 0x11 0x00>; - }; - }; -}; \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.toml b/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.toml deleted file mode 100644 index 36313e6cd..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3568-smp2.toml +++ /dev/null @@ -1,62 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 2 -# The physical CPU ids. -phys_cpu_ids = [0x200, 0x300] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8008_0000 -# The location of image: "memory" | "fs". -# Load from memory. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x8008_0000 -## The file path of the kernel image. -kernel_path = "/userdata/rootfs_overlay/guest/linux/roc-rk3568-pc" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x8000_0000 -#dtb_path = "/code/axvisor/configs/vms/linux-aarch64-rk3568_smp2.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x6000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] -# -# Device specifications -# -[devices] -# The interrupt mode. -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.dts b/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.dts deleted file mode 100644 index 20bc1c3d4..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.dts +++ /dev/null @@ -1,8099 +0,0 @@ -/dts-v1/; - -/ { - compatible = "rockchip,rk3588-firefly-itx-3588j\0rockchip,rk3588"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Firefly ITX-3588J HDMI(Linux)"; - - aliases { - csi2dcphy0 = "/csi2-dcphy0"; - csi2dcphy1 = "/csi2-dcphy1"; - csi2dphy0 = "/csi2-dphy0"; - csi2dphy1 = "/csi2-dphy1"; - csi2dphy2 = "/csi2-dphy2"; - dsi0 = "/dsi@fde20000"; - dsi1 = "/dsi@fde30000"; - ethernet0 = "/ethernet@fe1b0000"; - ethernet1 = "/ethernet@fe1c0000"; - gpio0 = "/pinctrl/gpio@fd8a0000"; - gpio1 = "/pinctrl/gpio@fec20000"; - gpio2 = "/pinctrl/gpio@fec30000"; - gpio3 = "/pinctrl/gpio@fec40000"; - gpio4 = "/pinctrl/gpio@fec50000"; - i2c0 = "/i2c@fd880000"; - i2c1 = "/i2c@fea90000"; - i2c2 = "/i2c@feaa0000"; - i2c3 = "/i2c@feab0000"; - i2c4 = "/i2c@feac0000"; - i2c5 = "/i2c@fead0000"; - i2c6 = "/i2c@fec80000"; - i2c7 = "/i2c@fec90000"; - i2c8 = "/i2c@feca0000"; - rkcif_mipi_lvds0 = "/rkcif-mipi-lvds"; - rkcif_mipi_lvds1 = "/rkcif-mipi-lvds1"; - rkcif_mipi_lvds2 = "/rkcif-mipi-lvds2"; - rkcif_mipi_lvds3 = "/rkcif-mipi-lvds3"; - rkvenc0 = "/rkvenc-core@fdbd0000"; - rkvenc1 = "/rkvenc-core@fdbe0000"; - jpege0 = "/jpege-core@fdba0000"; - jpege1 = "/jpege-core@fdba4000"; - jpege2 = "/jpege-core@fdba8000"; - jpege3 = "/jpege-core@fdbac000"; - serial0 = "/serial@fd890000"; - serial1 = "/serial@feb40000"; - serial2 = "/serial@feb50000"; - serial3 = "/serial@feb60000"; - serial4 = "/serial@feb70000"; - serial5 = "/serial@feb80000"; - serial6 = "/serial@feb90000"; - serial7 = "/serial@feba0000"; - serial8 = "/serial@febb0000"; - serial9 = "/serial@febc0000"; - spi0 = "/spi@feb00000"; - spi1 = "/spi@feb10000"; - spi2 = "/spi@feb20000"; - spi3 = "/spi@feb30000"; - spi4 = "/spi@fecb0000"; - spi5 = "/spi@fe2b0000"; - hdcp0 = "/hdcp@fde40000"; - hdcp1 = "/hdcp@fde70000"; - pwm0 = "/pwm@fd8b0000"; - pwm1 = "/pwm@fd8b0010"; - pwm2 = "/pwm@fd8b0020"; - pwm3 = "/pwm@fd8b0030"; - pwm4 = "/pwm@febd0000"; - pwm5 = "/pwm@febd0010"; - pwm6 = "/pwm@febd0020"; - pwm7 = "/pwm@febd0030"; - pwm8 = "/pwm@febe0000"; - pwm9 = "/pwm@febe0010"; - pwm10 = "/pwm@febe0020"; - pwm11 = "/pwm@febe0030"; - pwm12 = "/pwm@febf0000"; - pwm13 = "/pwm@febf0010"; - pwm14 = "/pwm@febf0020"; - pwm15 = "/pwm@febf0030"; - csi2dphy3 = "/csi2-dphy3"; - csi2dphy4 = "/csi2-dphy4"; - csi2dphy5 = "/csi2-dphy5"; - dp0 = "/dp@fde50000"; - dp1 = "/dp@fde60000"; - edp0 = "/edp@fdec0000"; - edp1 = "/edp@fded0000"; - hdptx0 = "/phy@fed60000"; - hdptx1 = "/phy@fed70000"; - hdptxhdmi0 = "/hdmiphy@fed60000"; - hdptxhdmi1 = "/hdmiphy@fed70000"; - hdmi0 = "/hdmi@fde80000"; - hdmi1 = "/hdmi@fdea0000"; - hdmirx0 = "/hdmirx-controller@fdee0000"; - rkcif_mipi_lvds4 = "/rkcif-mipi-lvds4"; - rkcif_mipi_lvds5 = "/rkcif-mipi-lvds5"; - usbdp0 = "/phy@fed80000"; - usbdp1 = "/phy@fed90000"; - mmc0 = "/mmc@fe2e0000"; - mmc1 = "/mmc@fe2c0000"; - mmc2 = "/mmc@fe2d0000"; - }; - - clocks { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - spll { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x29d7ab80>; - clock-output-names = "spll"; - }; - - xin32k { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x8000>; - clock-output-names = "xin32k"; - }; - - xin24m { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x16e3600>; - clock-output-names = "xin24m"; - }; - - hclk_vo1@fd7c08ec { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08ec 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x264>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - phandle = <0x05>; - }; - - aclk_vdpu_low_pre@fd7c08b0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08b0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1bc>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_vo0@fd7c08dc { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08dc 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x26d>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - phandle = <0x04>; - }; - - hclk_usb@fd7c08a8 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a8 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x264>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_nvm@fd7c087c { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c087c 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x141>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - phandle = <0x03>; - }; - - aclk_usb@fd7c08a8 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a8 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x263>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_isp1_pre@fd7c0868 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c0868 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1e1>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_isp1_pre@fd7c0868 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c0868 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1e0>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_rkvdec0_pre@fd7c08a0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1bc>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_rkvdec0_pre@fd7c08a0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1be>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_rkvdec1_pre@fd7c08a4 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a4 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1bc>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_rkvdec1_pre@fd7c08a4 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08a4 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1be>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_jpeg_decoder_pre@fd7c08b0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08b0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1bc>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_rkvenc1_pre@fd7c08c0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08c0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1c5>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_rkvenc1_pre@fd7c08c0 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08c0 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1c4>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_hdcp0_pre@fd7c08dc { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08dc 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x26c>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_hdcp1_pre@fd7c08ec { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08ec 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x263>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - pclk_av1_pre@fd7c0910 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c0910 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1be>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - aclk_av1_pre@fd7c0910 { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c0910 0x00 0x10>; - clock-names = "link"; - clocks = <0x02 0x1bc>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - hclk_sdio_pre@fd7c092c { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c092c 0x00 0x10>; - clock-names = "link"; - clocks = <0x03>; - #power-domain-cells = <0x01>; - #clock-cells = <0x00>; - }; - - pclk_vo0_grf@fd7c08dc { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08dc 0x00 0x04>; - clocks = <0x04>; - clock-names = "link"; - #clock-cells = <0x00>; - phandle = <0x5d>; - }; - - pclk_vo1_grf@fd7c08ec { - compatible = "rockchip,rk3588-clock-gate-link"; - reg = <0x00 0xfd7c08ec 0x00 0x04>; - clocks = <0x05>; - clock-names = "link"; - #clock-cells = <0x00>; - phandle = <0x5e>; - }; - }; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - - cpu-map { - - cluster0 { - - core0 { - cpu = <0x06>; - }; - - core1 { - cpu = <0x07>; - }; - - core2 { - cpu = <0x08>; - }; - - core3 { - cpu = <0x09>; - }; - }; - - cluster1 { - - core0 { - cpu = <0x0a>; - }; - - core1 { - cpu = <0x0b>; - }; - }; - - cluster2 { - - core0 { - cpu = <0x0c>; - }; - - core1 { - cpu = <0x0d>; - }; - }; - }; - - cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a55"; - reg = <0x00>; - enable-method = "psci"; - capacity-dmips-mhz = <0x212>; - clocks = <0x0e 0x00>; - operating-points-v2 = <0x0f>; - cpu-idle-states = <0x10>; - i-cache-size = <0x8000>; - i-cache-line-size = <0x40>; - i-cache-sets = <0x80>; - d-cache-size = <0x8000>; - d-cache-line-size = <0x40>; - d-cache-sets = <0x80>; - next-level-cache = <0x11>; - #cooling-cells = <0x02>; - dynamic-power-coefficient = <0x64>; - cpu-supply = <0x12>; - mem-supply = <0x12>; - phandle = <0x06>; - }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x100>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x212>; - // clocks = <0x0e 0x00>; - // operating-points-v2 = <0x0f>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x8000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x80>; - // d-cache-size = <0x8000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x80>; - // next-level-cache = <0x13>; - // phandle = <0x07>; - // }; - - // cpu@200 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x200>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x212>; - // clocks = <0x0e 0x00>; - // operating-points-v2 = <0x0f>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x8000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x80>; - // d-cache-size = <0x8000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x80>; - // next-level-cache = <0x14>; - // phandle = <0x08>; - // }; - - // cpu@300 { - // device_type = "cpu"; - // compatible = "arm,cortex-a55"; - // reg = <0x300>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x212>; - // clocks = <0x0e 0x00>; - // operating-points-v2 = <0x0f>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x8000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x80>; - // d-cache-size = <0x8000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x80>; - // next-level-cache = <0x15>; - // phandle = <0x09>; - // }; - - // cpu@400 { - // device_type = "cpu"; - // compatible = "arm,cortex-a76"; - // reg = <0x400>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x400>; - // clocks = <0x0e 0x02>; - // operating-points-v2 = <0x16>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x10000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x100>; - // d-cache-size = <0x10000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x100>; - // next-level-cache = <0x17>; - // #cooling-cells = <0x02>; - // dynamic-power-coefficient = <0x12c>; - // cpu-supply = <0x18>; - // mem-supply = <0x18>; - // phandle = <0x0a>; - // }; - - // cpu@500 { - // device_type = "cpu"; - // compatible = "arm,cortex-a76"; - // reg = <0x500>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x400>; - // clocks = <0x0e 0x02>; - // operating-points-v2 = <0x16>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x10000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x100>; - // d-cache-size = <0x10000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x100>; - // next-level-cache = <0x19>; - // phandle = <0x0b>; - // }; - - // cpu@600 { - // device_type = "cpu"; - // compatible = "arm,cortex-a76"; - // reg = <0x600>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x400>; - // clocks = <0x0e 0x03>; - // operating-points-v2 = <0x1a>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x10000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x100>; - // d-cache-size = <0x10000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x100>; - // next-level-cache = <0x1b>; - // #cooling-cells = <0x02>; - // dynamic-power-coefficient = <0x12c>; - // cpu-supply = <0x1c>; - // mem-supply = <0x1c>; - // phandle = <0x0c>; - // }; - - // cpu@700 { - // device_type = "cpu"; - // compatible = "arm,cortex-a76"; - // reg = <0x700>; - // enable-method = "psci"; - // capacity-dmips-mhz = <0x400>; - // clocks = <0x0e 0x03>; - // operating-points-v2 = <0x1a>; - // cpu-idle-states = <0x10>; - // i-cache-size = <0x10000>; - // i-cache-line-size = <0x40>; - // i-cache-sets = <0x100>; - // d-cache-size = <0x10000>; - // d-cache-line-size = <0x40>; - // d-cache-sets = <0x100>; - // next-level-cache = <0x1d>; - // phandle = <0x0d>; - // }; - - l2-cache-l0 { - compatible = "cache"; - cache-size = <0x20000>; - cache-line-size = <0x40>; - cache-sets = <0x200>; - next-level-cache = <0x1e>; - phandle = <0x11>; - }; - - l2-cache-l1 { - compatible = "cache"; - cache-size = <0x20000>; - cache-line-size = <0x40>; - cache-sets = <0x200>; - next-level-cache = <0x1e>; - phandle = <0x13>; - }; - - l2-cache-l2 { - compatible = "cache"; - cache-size = <0x20000>; - cache-line-size = <0x40>; - cache-sets = <0x200>; - next-level-cache = <0x1e>; - phandle = <0x14>; - }; - - l2-cache-l3 { - compatible = "cache"; - cache-size = <0x20000>; - cache-line-size = <0x40>; - cache-sets = <0x200>; - next-level-cache = <0x1e>; - phandle = <0x15>; - }; - - l2-cache-b0 { - compatible = "cache"; - cache-size = <0x80000>; - cache-line-size = <0x40>; - cache-sets = <0x400>; - next-level-cache = <0x1e>; - phandle = <0x17>; - }; - - l2-cache-b1 { - compatible = "cache"; - cache-size = <0x80000>; - cache-line-size = <0x40>; - cache-sets = <0x400>; - next-level-cache = <0x1e>; - phandle = <0x19>; - }; - - l2-cache-b2 { - compatible = "cache"; - cache-size = <0x80000>; - cache-line-size = <0x40>; - cache-sets = <0x400>; - next-level-cache = <0x1e>; - phandle = <0x1b>; - }; - - l2-cache-b3 { - compatible = "cache"; - cache-size = <0x80000>; - cache-line-size = <0x40>; - cache-sets = <0x400>; - next-level-cache = <0x1e>; - phandle = <0x1d>; - }; - - l3-cache { - compatible = "cache"; - cache-size = <0x300000>; - cache-line-size = <0x40>; - cache-sets = <0x1000>; - phandle = <0x1e>; - }; - }; - - cluster0-opp-table { - compatible = "operating-points-v2"; - opp-shared; - nvmem-cells = <0x1f 0x20>; - nvmem-cell-names = "leakage\0specification_serial_number"; - rockchip,supported-hw; - rockchip,opp-shared-dsu; - rockchip,pvtm-voltage-sel = <0x00 0x582 0x00 0x583 0x59a 0x01 0x59b 0x5b2 0x02 0x5b3 0x5ca 0x03 0x5cb 0x5e2 0x04 0x5e3 0x5fa 0x05 0x5fb 0x270f 0x06>; - rockchip,pvtm-pvtpll; - rockchip,pvtm-offset = <0x64>; - rockchip,pvtm-sample-time = <0x44c>; - rockchip,pvtm-freq = <0x159b40>; - rockchip,pvtm-volt = <0xb71b0>; - rockchip,pvtm-ref-temp = <0x19>; - rockchip,pvtm-temp-prop = <0xf4 0xf4>; - rockchip,pvtm-thermal-zone = "soc-thermal"; - rockchip,grf = <0x21>; - rockchip,dsu-grf = <0x22>; - volt-mem-read-margin = <0xd0bd8 0x01 0xbac48 0x02 0xa4cb8 0x03 0x78d98 0x04>; - low-volt-mem-read-margin = <0x04>; - intermediate-threshold-freq = <0xf6180>; - rockchip,reboot-freq = <0x159b40>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x2710>; - rockchip,low-temp-min-volt = <0xb71b0>; - rockchip,high-temp = <0x14c08>; - rockchip,high-temp-max-freq = <0x188940>; - phandle = <0x0f>; - - opp-408000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x18519600>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-600000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-816000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x30a32c00>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-1008000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x3c14dc00>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-1200000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x47868c00>; - opp-microvolt = <0xadf34 0xadf34 0xe7ef0 0xadf34 0xadf34 0xe7ef0>; - opp-microvolt-L1 = <0xaae60 0xaae60 0xe7ef0 0xaae60 0xaae60 0xe7ef0>; - opp-microvolt-L2 = <0xaae60 0xaae60 0xe7ef0 0xaae60 0xaae60 0xe7ef0>; - opp-microvolt-L3 = <0xa7d8c 0xa7d8c 0xe7ef0 0xa7d8c 0xa7d8c 0xe7ef0>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - opp-microvolt-L6 = <0xa4cb8 0xa4cb8 0xe7ef0 0xa4cb8 0xa4cb8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-1416000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x54667200>; - opp-microvolt = <0xba284 0xba284 0xe7ef0 0xba284 0xba284 0xe7ef0>; - opp-microvolt-L1 = <0xb71b0 0xb71b0 0xe7ef0 0xb71b0 0xb71b0 0xe7ef0>; - opp-microvolt-L2 = <0xb40dc 0xb40dc 0xe7ef0 0xb40dc 0xb40dc 0xe7ef0>; - opp-microvolt-L3 = <0xb1008 0xb1008 0xe7ef0 0xb1008 0xb1008 0xe7ef0>; - opp-microvolt-L4 = <0xb1008 0xb1008 0xe7ef0 0xb1008 0xb1008 0xe7ef0>; - opp-microvolt-L5 = <0xadf34 0xadf34 0xe7ef0 0xadf34 0xadf34 0xe7ef0>; - opp-microvolt-L6 = <0xadf34 0xadf34 0xe7ef0 0xadf34 0xadf34 0xe7ef0>; - clock-latency-ns = <0x9c40>; - opp-suspend; - }; - - opp-1608000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x5fd82200>; - opp-microvolt = <0xcf850 0xcf850 0xe7ef0 0xcf850 0xcf850 0xe7ef0>; - opp-microvolt-L1 = <0xcc77c 0xcc77c 0xe7ef0 0xcc77c 0xcc77c 0xe7ef0>; - opp-microvolt-L2 = <0xc96a8 0xc96a8 0xe7ef0 0xc96a8 0xc96a8 0xe7ef0>; - opp-microvolt-L3 = <0xc65d4 0xc65d4 0xe7ef0 0xc65d4 0xc65d4 0xe7ef0>; - opp-microvolt-L4 = <0xc3500 0xc3500 0xe7ef0 0xc3500 0xc3500 0xe7ef0>; - opp-microvolt-L5 = <0xc3500 0xc3500 0xe7ef0 0xc3500 0xc3500 0xe7ef0>; - opp-microvolt-L6 = <0xc042c 0xc042c 0xe7ef0 0xc042c 0xc042c 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-1704000000 { - opp-supported-hw = <0x02 0xffff>; - opp-hz = <0x00 0x6590fa00>; - opp-microvolt = <0xdbba0 0xdbba0 0xe7ef0 0xdbba0 0xdbba0 0xe7ef0>; - opp-microvolt-L1 = <0xd8acc 0xd8acc 0xe7ef0 0xd8acc 0xd8acc 0xe7ef0>; - opp-microvolt-L2 = <0xd59f8 0xd59f8 0xe7ef0 0xd59f8 0xd59f8 0xe7ef0>; - opp-microvolt-L3 = <0xd2924 0xd2924 0xe7ef0 0xd2924 0xd2924 0xe7ef0>; - opp-microvolt-L4 = <0xcf850 0xcf850 0xe7ef0 0xcf850 0xcf850 0xe7ef0>; - opp-microvolt-L5 = <0xcc77c 0xcc77c 0xe7ef0 0xcc77c 0xcc77c 0xe7ef0>; - opp-microvolt-L6 = <0xc96a8 0xc96a8 0xe7ef0 0xc96a8 0xc96a8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - - opp-1800000000 { - opp-supported-hw = <0xf9 0xffff>; - opp-hz = <0x00 0x6b49d200>; - opp-microvolt = <0xe7ef0 0xe7ef0 0xe7ef0 0xe7ef0 0xe7ef0 0xe7ef0>; - opp-microvolt-L1 = <0xe4e1c 0xe4e1c 0xe7ef0 0xe4e1c 0xe4e1c 0xe7ef0>; - opp-microvolt-L2 = <0xe1d48 0xe1d48 0xe7ef0 0xe1d48 0xe1d48 0xe7ef0>; - opp-microvolt-L3 = <0xdec74 0xdec74 0xe7ef0 0xdec74 0xdec74 0xe7ef0>; - opp-microvolt-L4 = <0xdbba0 0xdbba0 0xe7ef0 0xdbba0 0xdbba0 0xe7ef0>; - opp-microvolt-L5 = <0xd8acc 0xd8acc 0xe7ef0 0xd8acc 0xd8acc 0xe7ef0>; - opp-microvolt-L6 = <0xd59f8 0xd59f8 0xe7ef0 0xd59f8 0xd59f8 0xe7ef0>; - clock-latency-ns = <0x9c40>; - }; - }; - - arm-pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0x01 0x07 0x08>; - interrupt-affinity = <0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d>; - }; - - cpuinfo { - compatible = "rockchip,cpuinfo"; - nvmem-cells = <0x27 0x28 0x29>; - nvmem-cell-names = "id\0cpu-version\0cpu-code"; - }; - - csi2-dcphy0 { - compatible = "rockchip,rk3588-csi2-dcphy"; - phys = <0x2a>; - phy-names = "dcphy"; - status = "disabled"; - }; - - csi2-dcphy1 { - compatible = "rockchip,rk3588-csi2-dcphy"; - phys = <0x2b>; - phy-names = "dcphy"; - status = "disabled"; - }; - - csi2-dphy0 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x2c>; - status = "disabled"; - }; - - csi2-dphy1 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x2c>; - status = "disabled"; - }; - - csi2-dphy2 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x2c>; - status = "disabled"; - }; - - display-subsystem { - compatible = "rockchip,display-subsystem"; - ports = <0x2d>; - clocks = <0x2e 0x2f>; - clock-names = "hdmi0_phy_pll\0hdmi1_phy_pll"; - memory-region = <0x30>; - memory-region-names = "drm-logo"; - - route { - - route-dp0 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x31>; - }; - - route-dsi0 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x32>; - }; - - route-dsi1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x33>; - }; - - route-edp0 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x34>; - }; - - route-edp1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - }; - - route-hdmi0 { - status = "okay"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x35>; - }; - - route-rgb { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x36>; - }; - - route-dp1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x37>; - }; - - route-hdmi1 { - status = "disabled"; - logo,uboot = "logo.bmp"; - logo,kernel = "logo_kernel.bmp"; - logo,mode = "center"; - charge_logo,mode = "center"; - connect = <0x38>; - }; - }; - }; - - dmc { - compatible = "rockchip,rk3588-dmc"; - interrupts = <0x00 0x49 0x04>; - interrupt-names = "complete"; - devfreq-events = <0x39>; - clocks = <0x0e 0x04>; - clock-names = "dmc_clk"; - operating-points-v2 = <0x3a>; - upthreshold = <0x28>; - downdifferential = <0x14>; - system-status-level = <0x01 0x04 0x08 0x08 0x02 0x01 0x10 0x04 0x10000 0x04 0x80000 0x04 0x1000 0x08 0x4000 0x08 0x2000 0x08 0xc00 0x08 0x40000 0x08>; - auto-freq-en = <0x01>; - status = "okay"; - center-supply = <0x3b>; - mem-supply = <0x3c>; - }; - - dmc-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x3d>; - nvmem-cell-names = "leakage"; - rockchip,leakage-voltage-sel = <0x01 0x1f 0x00 0x20 0x2c 0x01 0x2d 0x39 0x02 0x3a 0xfe 0x03>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x2710>; - rockchip,low-temp-min-volt = <0xb71b0>; - phandle = <0x3a>; - - opp-528000000 { - opp-hz = <0x00 0x1f78a400>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xd59f8 0xb1008 0xb1008 0xb71b0>; - opp-microvolt-L1 = <0xa4cb8 0xa4cb8 0xd59f8 0xaae60 0xaae60 0xb71b0>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xd59f8 0xa7d8c 0xa7d8c 0xb71b0>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xd59f8 0xa4cb8 0xa4cb8 0xb71b0>; - }; - - opp-1068000000 { - opp-hz = <0x00 0x3fa86300>; - opp-microvolt = <0xb1008 0xb1008 0xd59f8 0xb40dc 0xb40dc 0xb71b0>; - opp-microvolt-L1 = <0xaae60 0xaae60 0xd59f8 0xadf34 0xadf34 0xb71b0>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xd59f8 0xaae60 0xaae60 0xb71b0>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xd59f8 0xa7d8c 0xa7d8c 0xb71b0>; - }; - - opp-1560000000 { - opp-hz = <0x00 0x5cfbb600>; - opp-microvolt = <0xc3500 0xc3500 0xd59f8 0xb71b0 0xb71b0 0xb71b0>; - opp-microvolt-L1 = <0xbd358 0xbd358 0xd59f8 0xb1008 0xb1008 0xb71b0>; - opp-microvolt-L2 = <0xb71b0 0xb71b0 0xd59f8 0xadf34 0xadf34 0xb71b0>; - opp-microvolt-L3 = <0xb1008 0xb1008 0xd59f8 0xaae60 0xaae60 0xb71b0>; - }; - - opp-2750000000 { - opp-hz = <0x00 0xa3e9ab80>; - opp-microvolt = <0xd59f8 0xd59f8 0xd59f8 0xb71b0 0xb71b0 0xb71b0>; - opp-microvolt-L1 = <0xcf850 0xcf850 0xd59f8 0xb71b0 0xb71b0 0xb71b0>; - opp-microvolt-L2 = <0xcc77c 0xcc77c 0xd59f8 0xb1008 0xb1008 0xb71b0>; - opp-microvolt-L3 = <0xc96a8 0xc8320 0xd59f8 0xaae60 0xaae60 0xb71b0>; - }; - }; - - firmware { - - scmi { - compatible = "arm,scmi-smc"; - shmem = <0x3e>; - arm,smc-id = <0x82000010>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@14 { - reg = <0x14>; - #clock-cells = <0x01>; - assigned-clocks = <0x0e 0x00 0x0e 0x02 0x0e 0x03>; - assigned-clock-rates = <0x30a32c00 0x30a32c00 0x30a32c00>; - phandle = <0x0e>; - }; - - protocol@16 { - reg = <0x16>; - #reset-cells = <0x01>; - phandle = <0x104>; - }; - }; - - sdei { - compatible = "arm,sdei-1.0"; - method = "smc"; - }; - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - }; - }; - - jpege-ccu { - compatible = "rockchip,vpu-jpege-ccu"; - status = "okay"; - phandle = <0xa4>; - }; - - mpp-srv { - compatible = "rockchip,mpp-service"; - rockchip,taskqueue-count = <0x0c>; - rockchip,resetgroup-count = <0x01>; - status = "okay"; - phandle = <0x9f>; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - }; - - rkcif-dvp { - compatible = "rockchip,rkcif-dvp"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x41>; - }; - - rkcif-dvp-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x41>; - status = "disabled"; - }; - - rkcif-mipi-lvds { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x42>; - }; - - rkcif-mipi-lvds-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x42>; - status = "disabled"; - }; - - rkcif-mipi-lvds-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x42>; - status = "disabled"; - }; - - rkcif-mipi-lvds-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x42>; - status = "disabled"; - }; - - rkcif-mipi-lvds-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x42>; - status = "disabled"; - }; - - rkcif-mipi-lvds1 { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x43>; - }; - - rkcif-mipi-lvds1-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x43>; - status = "disabled"; - }; - - rkcif-mipi-lvds1-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x43>; - status = "disabled"; - }; - - rkcif-mipi-lvds1-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x43>; - status = "disabled"; - }; - - rkcif-mipi-lvds1-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x43>; - status = "disabled"; - }; - - rkcif-mipi-lvds2 { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x44>; - }; - - rkcif-mipi-lvds2-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x44>; - status = "disabled"; - }; - - rkcif-mipi-lvds2-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x44>; - status = "disabled"; - }; - - rkcif-mipi-lvds2-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x44>; - status = "disabled"; - }; - - rkcif-mipi-lvds2-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x44>; - status = "disabled"; - }; - - rkcif-mipi-lvds3 { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x45>; - }; - - rkcif-mipi-lvds3-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x45>; - status = "disabled"; - }; - - rkcif-mipi-lvds3-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x45>; - status = "disabled"; - }; - - rkcif-mipi-lvds3-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x45>; - status = "disabled"; - }; - - rkcif-mipi-lvds3-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x45>; - status = "disabled"; - }; - - rkisp0-vir0 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x46>; - status = "disabled"; - }; - - rkisp0-vir1 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x46>; - status = "disabled"; - }; - - rkisp0-vir2 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x46>; - status = "disabled"; - }; - - rkisp0-vir3 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x46>; - status = "disabled"; - }; - - rkisp1-vir0 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x47>; - status = "disabled"; - }; - - rkisp1-vir1 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x47>; - status = "disabled"; - }; - - rkisp1-vir2 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x47>; - status = "disabled"; - }; - - rkisp1-vir3 { - compatible = "rockchip,rkisp-vir"; - rockchip,hw = <0x47>; - status = "disabled"; - }; - - rkispp0-vir0 { - compatible = "rockchip,rk3588-rkispp-vir"; - rockchip,hw = <0x48>; - status = "disabled"; - }; - - rkispp1-vir0 { - compatible = "rockchip,rk3588-rkispp-vir"; - rockchip,hw = <0x49>; - status = "disabled"; - }; - - rkvenc-ccu { - compatible = "rockchip,rkv-encoder-v2-ccu"; - status = "okay"; - phandle = <0xaa>; - }; - - rockchip-suspend { - compatible = "rockchip,pm-rk3588"; - status = "okay"; - rockchip,sleep-debug-en = <0x01>; - rockchip,sleep-mode-config = <0x04>; - rockchip,wakeup-config = <0x900>; - }; - - rockchip-system-monitor { - compatible = "rockchip,system-monitor"; - rockchip,thermal-zone = "soc-thermal"; - }; - - thermal-zones { - - soc-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - sustainable-power = <0x834>; - thermal-sensors = <0x4a 0x00>; - - trips { - - trip-point-0 { - temperature = <0x124f8>; - hysteresis = <0x7d0>; - type = "passive"; - }; - - trip-point-1 { - temperature = <0x14c08>; - hysteresis = <0x7d0>; - type = "passive"; - phandle = <0x4b>; - }; - - soc-crit { - temperature = <0x1c138>; - hysteresis = <0x7d0>; - type = "critical"; - }; - }; - - cooling-maps { - - map0 { - trip = <0x4b>; - cooling-device = <0x06 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - - map3 { - trip = <0x4b>; - cooling-device = <0x4c 0xffffffff 0xffffffff>; - contribution = <0x400>; - }; - }; - }; - - bigcore0-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x01>; - }; - - bigcore1-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x02>; - }; - - littlecore-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x03>; - }; - - center-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x04>; - }; - - gpu-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x05>; - }; - - npu-thermal { - polling-delay-passive = <0x14>; - polling-delay = <0x3e8>; - thermal-sensors = <0x4a 0x06>; - }; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04>; - }; - - sram@10f000 { - compatible = "mmio-sram"; - reg = <0x00 0x10f000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x10f000 0x100>; - - sram@0 { - compatible = "arm,scmi-shmem"; - reg = <0x00 0x100>; - phandle = <0x3e>; - }; - }; - - gpu@fb000000 { - compatible = "arm,mali-bifrost"; - reg = <0x00 0xfb000000 0x00 0x200000>; - interrupts = <0x00 0x5e 0x04 0x00 0x5d 0x04 0x00 0x5c 0x04>; - interrupt-names = "GPU\0MMU\0JOB"; - clocks = <0x0e 0x05 0x02 0x115 0x02 0x116 0x02 0x114>; - clock-names = "clk_mali\0clk_gpu_coregroup\0clk_gpu_stacks\0clk_gpu"; - assigned-clocks = <0x0e 0x05>; - assigned-clock-rates = <0xbebc200>; - power-domains = <0x4d 0x0c>; - operating-points-v2 = <0x4e>; - #cooling-cells = <0x02>; - dynamic-power-coefficient = <0xba6>; - upthreshold = <0x1e>; - downdifferential = <0x0a>; - status = "okay"; - mali-supply = <0x4f>; - mem-supply = <0x4f>; - phandle = <0x4c>; - }; - - gpu-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x50 0x20>; - nvmem-cell-names = "leakage\0specification_serial_number"; - rockchip,supported-hw; - rockchip,pvtm-voltage-sel = <0x00 0x32f 0x00 0x330 0x343 0x01 0x344 0x35c 0x02 0x35d 0x375 0x03 0x376 0x38e 0x04 0x38f 0x270f 0x05>; - rockchip,pvtm-pvtpll; - rockchip,pvtm-offset = <0x1c>; - rockchip,pvtm-freq = <0xc3500>; - rockchip,pvtm-sample-time = <0x44c>; - rockchip,pvtm-volt = <0xb71b0>; - rockchip,pvtm-ref-temp = <0x19>; - rockchip,pvtm-temp-prop = <0xffffff79 0xffffff79>; - rockchip,pvtm-thermal-zone = "gpu-thermal"; - clocks = <0x02 0x114>; - clock-names = "clk"; - rockchip,grf = <0x51>; - volt-mem-read-margin = <0xd0bd8 0x01 0xbac48 0x02 0xa4cb8 0x03 0x78d98 0x04>; - low-volt-mem-read-margin = <0x04>; - intermediate-threshold-freq = <0x61a80>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x2710>; - rockchip,low-temp-min-volt = <0xb71b0>; - rockchip,high-temp = <0x14c08>; - rockchip,high-temp-max-freq = <0xc3500>; - phandle = <0x4e>; - - opp-300000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x11e1a300>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-400000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-500000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x1dcd6500>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-600000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-700000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L1 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-800000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xb71b0 0xb71b0 0xcf850 0xb71b0 0xb71b0 0xcf850>; - opp-microvolt-L1 = <0xb40dc 0xb40dc 0xcf850 0xb40dc 0xb40dc 0xcf850>; - opp-microvolt-L2 = <0xb1008 0xb1008 0xcf850 0xb1008 0xb1008 0xcf850>; - opp-microvolt-L3 = <0xadf34 0xadf34 0xcf850 0xadf34 0xadf34 0xcf850>; - opp-microvolt-L4 = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - }; - - opp-900000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xc3500 0xc3500 0xcf850 0xc3500 0xc3500 0xcf850>; - opp-microvolt-L1 = <0xc042c 0xc042c 0xcf850 0xc042c 0xc042c 0xcf850>; - opp-microvolt-L2 = <0xbd358 0xbd358 0xcf850 0xbd358 0xbd358 0xcf850>; - opp-microvolt-L3 = <0xba284 0xba284 0xcf850 0xba284 0xba284 0xcf850>; - opp-microvolt-L4 = <0xb71b0 0xb71b0 0xcf850 0xb71b0 0xb71b0 0xcf850>; - }; - - opp-1000000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xcf850 0xcf850 0xcf850 0xcf850 0xcf850 0xcf850>; - opp-microvolt-L1 = <0xcc77c 0xcc77c 0xcf850 0xcc77c 0xcc77c 0xcf850>; - opp-microvolt-L2 = <0xc96a8 0xc96a8 0xcf850 0xc96a8 0xc96a8 0xcf850>; - opp-microvolt-L3 = <0xc65d4 0xc65d4 0xcf850 0xc65d4 0xc65d4 0xcf850>; - opp-microvolt-L4 = <0xc3500 0xc3500 0xcf850 0xc3500 0xc3500 0xcf850>; - }; - }; - - usbdrd3_0 { - compatible = "rockchip,rk3588-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x02 0x1a3 0x02 0x1a2 0x02 0x1a1>; - clock-names = "ref\0suspend\0bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - usb@fc000000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfc000000 0x00 0x400000>; - interrupts = <0x00 0xdc 0x04>; - power-domains = <0x4d 0x1f>; - resets = <0x02 0x2a4>; - reset-names = "usb3-otg"; - dr_mode = "host"; - phys = <0x52 0x53>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - snps,dis_enblslpm_quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,parkmode-disable-ss-quirk; - status = "okay"; - usb-role-switch; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x54>; - phandle = <0x163>; - }; - }; - }; - }; - - usb@fc800000 { - compatible = "rockchip,rk3588-ehci\0generic-ehci"; - reg = <0x00 0xfc800000 0x00 0x40000>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x02 0x19d 0x02 0x19e 0x55>; - clock-names = "usbhost\0arbiter\0utmi"; - companion = <0x56>; - phys = <0x57>; - phy-names = "usb2-phy"; - power-domains = <0x4d 0x1f>; - status = "okay"; - }; - - usb@fc840000 { - compatible = "generic-ohci"; - reg = <0x00 0xfc840000 0x00 0x40000>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x02 0x19d 0x02 0x19e 0x55>; - clock-names = "usbhost\0arbiter\0utmi"; - phys = <0x57>; - phy-names = "usb2-phy"; - power-domains = <0x4d 0x1f>; - status = "okay"; - phandle = <0x56>; - }; - - usb@fc880000 { - compatible = "rockchip,rk3588-ehci\0generic-ehci"; - reg = <0x00 0xfc880000 0x00 0x40000>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x02 0x19f 0x02 0x1a0 0x58>; - clock-names = "usbhost\0arbiter\0utmi"; - companion = <0x59>; - phys = <0x5a>; - phy-names = "usb2-phy"; - power-domains = <0x4d 0x1f>; - status = "okay"; - }; - - usb@fc8c0000 { - compatible = "generic-ohci"; - reg = <0x00 0xfc8c0000 0x00 0x40000>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x02 0x19f 0x02 0x1a0 0x58>; - clock-names = "usbhost\0arbiter\0utmi"; - phys = <0x5a>; - phy-names = "usb2-phy"; - power-domains = <0x4d 0x1f>; - status = "okay"; - phandle = <0x59>; - }; - - iommu@fc900000 { - compatible = "arm,smmu-v3"; - reg = <0x00 0xfc900000 0x00 0x200000>; - interrupts = <0x00 0x171 0x04 0x00 0x173 0x04 0x00 0x176 0x04 0x00 0x16f 0x04>; - interrupt-names = "eventq\0gerror\0priq\0cmdq-sync"; - #iommu-cells = <0x01>; - status = "disabled"; - }; - - iommu@fcb00000 { - compatible = "arm,smmu-v3"; - reg = <0x00 0xfcb00000 0x00 0x200000>; - interrupts = <0x00 0x17d 0x04 0x00 0x17f 0x04 0x00 0x182 0x04 0x00 0x17b 0x04>; - interrupt-names = "eventq\0gerror\0priq\0cmdq-sync"; - #iommu-cells = <0x01>; - status = "disabled"; - }; - - usbhost3_0 { - compatible = "rockchip,rk3588-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x02 0x179 0x02 0x178 0x02 0x177 0x02 0x17a 0x02 0x166 0x02 0x181>; - clock-names = "ref\0suspend\0bus\0utmi\0php\0pipe"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "disabled"; - - usb@fcd00000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfcd00000 0x00 0x400000>; - interrupts = <0x00 0xde 0x04>; - resets = <0x02 0x237>; - reset-names = "usb3-host"; - dr_mode = "host"; - phys = <0x5b 0x04>; - phy-names = "usb3-phy"; - phy_type = "utmi_wide"; - snps,dis_enblslpm_quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,dis_rxdet_inp3_quirk; - snps,parkmode-disable-ss-quirk; - status = "disabled"; - }; - }; - - syscon@fd588000 { - compatible = "rockchip,rk3588-pmu0-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd588000 0x00 0x2000>; - - reboot-mode { - compatible = "syscon-reboot-mode"; - offset = <0x80>; - mode-bootloader = <0x5242c301>; - mode-charge = <0x5242c30b>; - mode-fastboot = <0x5242c309>; - mode-loader = <0x5242c301>; - mode-normal = <0x5242c300>; - mode-recovery = <0x5242c303>; - mode-ums = <0x5242c30c>; - mode-panic = <0x5242c307>; - mode-watchdog = <0x5242c308>; - }; - }; - - syscon@fd58a000 { - compatible = "rockchip,rk3588-pmu1-grf\0syscon"; - reg = <0x00 0xfd58a000 0x00 0x2000>; - phandle = <0xe6>; - }; - - syscon@fd58c000 { - compatible = "rockchip,rk3588-sys-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd58c000 0x00 0x1000>; - phandle = <0xb7>; - - rgb { - compatible = "rockchip,rk3588-rgb"; - pinctrl-names = "default"; - pinctrl-0 = <0x5c>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x36>; - status = "disabled"; - phandle = <0xd2>; - }; - }; - }; - }; - }; - - syscon@fd590000 { - compatible = "rockchip,rk3588-bigcore0-grf\0syscon"; - reg = <0x00 0xfd590000 0x00 0x100>; - phandle = <0x24>; - }; - - syscon@fd592000 { - compatible = "rockchip,rk3588-bigcore1-grf\0syscon"; - reg = <0x00 0xfd592000 0x00 0x100>; - phandle = <0x26>; - }; - - syscon@fd594000 { - compatible = "rockchip,rk3588-litcore-grf\0syscon"; - reg = <0x00 0xfd594000 0x00 0x100>; - phandle = <0x21>; - }; - - syscon@fd598000 { - compatible = "rockchip,rk3588-dsu-grf\0syscon"; - reg = <0x00 0xfd598000 0x00 0x100>; - phandle = <0x22>; - }; - - syscon@fd5a0000 { - compatible = "rockchip,rk3588-gpu-grf\0syscon"; - reg = <0x00 0xfd5a0000 0x00 0x100>; - phandle = <0x51>; - }; - - syscon@fd5a2000 { - compatible = "rockchip,rk3588-npu-grf\0syscon"; - reg = <0x00 0xfd5a2000 0x00 0x100>; - phandle = <0x9d>; - }; - - syscon@fd5a4000 { - compatible = "rockchip,rk3588-vop-grf\0syscon"; - reg = <0x00 0xfd5a4000 0x00 0x2000>; - phandle = <0xb9>; - }; - - syscon@fd5a6000 { - compatible = "rockchip,rk3588-vo-grf\0syscon"; - reg = <0x00 0xfd5a6000 0x00 0x2000>; - clocks = <0x5d>; - phandle = <0xd7>; - }; - - syscon@fd5a8000 { - compatible = "rockchip,rk3588-vo-grf\0syscon"; - reg = <0x00 0xfd5a8000 0x00 0x100>; - clocks = <0x5e>; - phandle = <0xba>; - }; - - syscon@fd5ac000 { - compatible = "rockchip,rk3588-usb-grf\0syscon"; - reg = <0x00 0xfd5ac000 0x00 0x4000>; - phandle = <0x5f>; - }; - - syscon@fd5b0000 { - compatible = "rockchip,rk3588-php-grf\0syscon"; - reg = <0x00 0xfd5b0000 0x00 0x1000>; - phandle = <0x61>; - }; - - syscon@fd5b4000 { - compatible = "rockchip,mipi-dphy-grf\0syscon"; - reg = <0x00 0xfd5b4000 0x00 0x1000>; - phandle = <0x173>; - }; - - syscon@fd5b5000 { - compatible = "rockchip,mipi-dphy-grf\0syscon"; - reg = <0x00 0xfd5b5000 0x00 0x1000>; - phandle = <0x1a7>; - }; - - syscon@fd5bc000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfd5bc000 0x00 0x100>; - phandle = <0x174>; - }; - - syscon@fd5c4000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfd5c4000 0x00 0x100>; - phandle = <0x175>; - }; - - syscon@fd5c8000 { - compatible = "rockchip,rk3588-usbdpphy-grf\0syscon"; - reg = <0x00 0xfd5c8000 0x00 0x4000>; - phandle = <0x16d>; - }; - - syscon@fd5d0000 { - compatible = "rockchip,rk3588-usb2phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd5d0000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - phandle = <0x16c>; - - usb2-phy@0 { - compatible = "rockchip,rk3588-usb2phy"; - reg = <0x00 0x10>; - interrupts = <0x00 0x189 0x04>; - resets = <0x02 0xc0047 0x02 0x488>; - reset-names = "phy\0apb"; - clocks = <0x02 0x2b5>; - clock-names = "phyclk"; - clock-output-names = "usb480m_phy0"; - #clock-cells = <0x00>; - rockchip,usbctrl-grf = <0x5f>; - status = "okay"; - phandle = <0x16e>; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - rockchip,typec-vbus-det; - phandle = <0x52>; - }; - }; - }; - - syscon@fd5d8000 { - compatible = "rockchip,rk3588-usb2phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd5d8000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - - usb2-phy@8000 { - compatible = "rockchip,rk3588-usb2phy"; - reg = <0x8000 0x10>; - interrupts = <0x00 0x187 0x04>; - resets = <0x02 0xc0049 0x02 0x48a>; - reset-names = "phy\0apb"; - clocks = <0x02 0x2b5>; - clock-names = "phyclk"; - clock-output-names = "usb480m_phy2"; - #clock-cells = <0x00>; - status = "okay"; - phandle = <0x55>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x60>; - phandle = <0x57>; - }; - }; - }; - - syscon@fd5dc000 { - compatible = "rockchip,rk3588-usb2phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd5dc000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - - usb2-phy@c000 { - compatible = "rockchip,rk3588-usb2phy"; - reg = <0xc000 0x10>; - interrupts = <0x00 0x188 0x04>; - resets = <0x02 0xc004a 0x02 0x48b>; - reset-names = "phy\0apb"; - clocks = <0x02 0x2b5>; - clock-names = "phyclk"; - clock-output-names = "usb480m_phy3"; - #clock-cells = <0x00>; - status = "okay"; - phandle = <0x58>; - - host-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x60>; - phandle = <0x5a>; - }; - }; - }; - - syscon@fd5e0000 { - compatible = "rockchip,rk3588-hdptxphy-grf\0syscon"; - reg = <0x00 0xfd5e0000 0x00 0x100>; - phandle = <0x16b>; - }; - - syscon@fd5e8000 { - compatible = "rockchip,mipi-dcphy-grf\0syscon"; - reg = <0x00 0xfd5e8000 0x00 0x4000>; - phandle = <0x171>; - }; - - syscon@fd5ec000 { - compatible = "rockchip,mipi-dcphy-grf\0syscon"; - reg = <0x00 0xfd5ec000 0x00 0x4000>; - phandle = <0x172>; - }; - - syscon@fd5f0000 { - compatible = "rockchip,rk3588-ioc\0syscon"; - reg = <0x00 0xfd5f0000 0x00 0x10000>; - phandle = <0x176>; - }; - - clock-controller@fd7c0000 { - compatible = "rockchip,rk3588-cru"; - rockchip,grf = <0x61>; - reg = <0x00 0xfd7c0000 0x00 0x5c000>; - #clock-cells = <0x01>; - #reset-cells = <0x01>; - assigned-clocks = <0x02 0x09 0x02 0x05 0x02 0x08 0x02 0x07 0x02 0xd8 0x02 0xda 0x02 0xd9 0x02 0x10e 0x02 0x10f 0x02 0x110 0x02 0x299 0x02 0x29a 0x02 0x7b 0x02 0xec 0x02 0x114 0x02 0x208 0x02 0x20e>; - assigned-clock-rates = <0x4190ab00 0x2ee00000 0x32a9f880 0x46cf7100 0x29d7ab80 0x17d78400 0x1dcd6500 0x2faf0800 0x5f5e100 0x17d78400 0x5f5e100 0xbebc200 0x165a0bc0 0x8f0d180 0xbebc200 0xb71b00 0xb71b00>; - phandle = <0x02>; - }; - - i2c@fd880000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfd880000 0x00 0x1000>; - clocks = <0x02 0x287 0x02 0x286>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x13d 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x62>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - rk8602@42 { - compatible = "rockchip,rk8602"; - reg = <0x42>; - vin-supply = <0x63>; - regulator-compatible = "rk860x-reg"; - regulator-name = "vdd_cpu_big0_s0"; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0x100590>; - regulator-ramp-delay = <0x8fc>; - rockchip,suspend-voltage-selector = <0x01>; - regulator-boot-on; - regulator-always-on; - phandle = <0x18>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - rk8603@43 { - compatible = "rockchip,rk8603"; - reg = <0x43>; - vin-supply = <0x63>; - regulator-compatible = "rk860x-reg"; - regulator-name = "vdd_cpu_big1_s0"; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0x100590>; - regulator-ramp-delay = <0x8fc>; - rockchip,suspend-voltage-selector = <0x01>; - regulator-boot-on; - regulator-always-on; - phandle = <0x1c>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - - serial@fd890000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfd890000 0x00 0x100>; - interrupts = <0x00 0x14b 0x04>; - clocks = <0x02 0x2ae 0x02 0x2af>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x64 0x06 0x64 0x07>; - pinctrl-names = "default"; - pinctrl-0 = <0x65>; - status = "disabled"; - }; - - pwm@fd8b0000 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfd8b0000 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x66>; - clocks = <0x02 0x2a5 0x02 0x2a4>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fd8b0010 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfd8b0010 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x67>; - clocks = <0x02 0x2a5 0x02 0x2a4>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fd8b0020 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfd8b0020 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x68>; - clocks = <0x02 0x2a5 0x02 0x2a4>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@fd8b0030 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfd8b0030 0x00 0x10>; - interrupts = <0x00 0x158 0x04 0x00 0x159 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x69>; - clocks = <0x02 0x2a5 0x02 0x2a4>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - power-management@fd8d8000 { - compatible = "rockchip,rk3588-pmu\0syscon\0simple-mfd"; - reg = <0x00 0xfd8d8000 0x00 0x400>; - phandle = <0xbb>; - - power-controller { - compatible = "rockchip,rk3588-power-controller"; - #power-domain-cells = <0x01>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x4d>; - - power-domain@8 { - reg = <0x08>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - power-domain@9 { - reg = <0x09>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0x12f 0x02 0x131 0x02 0x130 0x02 0x126>; - pm_qos = <0x6a 0x6b 0x6c>; - - power-domain@10 { - reg = <0x0a>; - clocks = <0x02 0x12f 0x02 0x131 0x02 0x130>; - pm_qos = <0x6d>; - }; - - power-domain@11 { - reg = <0x0b>; - clocks = <0x02 0x12f 0x02 0x131 0x02 0x130>; - pm_qos = <0x6e>; - }; - }; - }; - - power-domain@12 { - reg = <0x0c>; - clocks = <0x02 0x114 0x02 0x115 0x02 0x116>; - pm_qos = <0x6f 0x70 0x71 0x72>; - }; - - power-domain@13 { - reg = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - power-domain@14 { - reg = <0x0e>; - clocks = <0x02 0x18f 0x02 0x1be 0x02 0x1bc 0x02 0x190 0x02 0x18e>; - pm_qos = <0x73>; - }; - - power-domain@15 { - reg = <0x0f>; - clocks = <0x02 0x194 0x02 0x1be 0x02 0x1bc 0x02 0x195>; - pm_qos = <0x74>; - }; - - power-domain@16 { - reg = <0x10>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0x1c4 0x02 0x1c5>; - pm_qos = <0x75 0x76 0x77>; - - power-domain@17 { - reg = <0x11>; - clocks = <0x02 0x1c9 0x02 0x1c4 0x02 0x1c5 0x02 0x1ca>; - pm_qos = <0x78 0x79 0x7a>; - }; - }; - }; - - power-domain@21 { - reg = <0x15>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0x1be 0x02 0x1bd 0x02 0x1bc 0x02 0x1bf 0x02 0x1aa 0x02 0x1a9 0x02 0x1ac 0x02 0x1ad 0x02 0x1ae 0x02 0x1af 0x02 0x1b0 0x02 0x1b1 0x02 0x1b2 0x02 0x1b3 0x02 0x1b4 0x02 0x1b5 0x02 0x1b7 0x02 0x1b6>; - pm_qos = <0x7b 0x7c 0x7d 0x7e 0x7f 0x80 0x81 0x82>; - - power-domain@23 { - reg = <0x17>; - clocks = <0x02 0x4b 0x02 0x49 0x02 0x1be>; - pm_qos = <0x83>; - }; - - power-domain@14 { - reg = <0x0e>; - clocks = <0x02 0x18f 0x02 0x1be 0x02 0x1bc 0x02 0x190>; - pm_qos = <0x73>; - }; - - power-domain@15 { - reg = <0x0f>; - clocks = <0x02 0x194 0x02 0x1be 0x02 0x1bc>; - pm_qos = <0x74>; - }; - - power-domain@22 { - reg = <0x16>; - clocks = <0x02 0x1ba 0x02 0x1b9>; - pm_qos = <0x84>; - }; - }; - - power-domain@24 { - reg = <0x18>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0x26e 0x02 0x26d 0x02 0x270>; - pm_qos = <0x85 0x86>; - - power-domain@25 { - reg = <0x19>; - clocks = <0x02 0x1f6 0x02 0x1f7 0x02 0x1f5 0x02 0x1f3 0x02 0x1ee 0x02 0x1ed 0x02 0x26d>; - pm_qos = <0x87>; - }; - }; - - power-domain@26 { - reg = <0x1a>; - clocks = <0x02 0x22e 0x02 0x22f 0x02 0x22d 0x02 0x218 0x02 0x217 0x02 0x22b 0x02 0x264>; - pm_qos = <0x88 0x89>; - }; - - power-domain@27 { - reg = <0x1b>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0x1e1 0x02 0x1e2 0x02 0x1df 0x02 0x1de 0x02 0x1e5 0x02 0x1e4>; - pm_qos = <0x8a 0x8b 0x8c 0x8d>; - - power-domain@28 { - reg = <0x1c>; - clocks = <0x02 0x121 0x02 0x120 0x02 0x1e1 0x02 0x1e2>; - pm_qos = <0x8e 0x8f>; - }; - - power-domain@29 { - reg = <0x1d>; - clocks = <0x02 0x1d6 0x02 0x1d5 0x02 0x1d9 0x02 0x1d8 0x02 0x1e2>; - pm_qos = <0x90 0x91>; - }; - }; - - power-domain@30 { - reg = <0x1e>; - clocks = <0x02 0x189 0x02 0x18a>; - pm_qos = <0x92>; - }; - - power-domain@31 { - reg = <0x1f>; - clocks = <0x02 0x166 0x02 0x19b 0x02 0x19c 0x02 0x19d 0x02 0x19e 0x02 0x19f 0x02 0x1a0>; - pm_qos = <0x93 0x94 0x95 0x96>; - }; - - power-domain@33 { - reg = <0x21>; - clocks = <0x02 0x166 0x02 0x169 0x02 0x16a>; - }; - - power-domain@34 { - reg = <0x22>; - clocks = <0x02 0x166 0x02 0x169 0x02 0x16a>; - }; - - power-domain@37 { - reg = <0x25>; - clocks = <0x02 0x199 0x02 0x140>; - pm_qos = <0x97>; - }; - - power-domain@38 { - reg = <0x26>; - clocks = <0x02 0x3c 0x02 0x3d>; - }; - - power-domain@40 { - reg = <0x28>; - pm_qos = <0x98>; - }; - }; - }; - - pvtm@fda40000 { - compatible = "rockchip,rk3588-bigcore0-pvtm"; - reg = <0x00 0xfda40000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@0 { - reg = <0x00>; - clocks = <0x02 0x2c6 0x02 0x15>; - clock-names = "clk\0pclk"; - }; - }; - - pvtm@fda50000 { - compatible = "rockchip,rk3588-bigcore1-pvtm"; - reg = <0x00 0xfda50000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@1 { - reg = <0x01>; - clocks = <0x02 0x2c8 0x02 0x17>; - clock-names = "clk\0pclk"; - }; - }; - - pvtm@fda60000 { - compatible = "rockchip,rk3588-litcore-pvtm"; - reg = <0x00 0xfda60000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@2 { - reg = <0x02>; - clocks = <0x02 0x2ca 0x02 0x1b>; - clock-names = "clk\0pclk"; - }; - }; - - pvtm@fdaf0000 { - compatible = "rockchip,rk3588-npu-pvtm"; - reg = <0x00 0xfdaf0000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@3 { - reg = <0x03>; - clocks = <0x02 0x12b 0x02 0x129>; - clock-names = "clk\0pclk"; - resets = <0x02 0x1de 0x02 0x1dc>; - reset-names = "rts\0rst-p"; - }; - }; - - pvtm@fdb30000 { - compatible = "rockchip,rk3588-gpu-pvtm"; - reg = <0x00 0xfdb30000 0x00 0x100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - pvtm@4 { - reg = <0x04>; - clocks = <0x02 0x118>; - clock-names = "clk"; - resets = <0x02 0x430 0x02 0x42f>; - reset-names = "rts\0rst-p"; - }; - }; - - npu@fdab0000 { - compatible = "rockchip,rk3588-rknpu"; - reg = <0x00 0xfdab0000 0x00 0x10000 0x00 0xfdac0000 0x00 0x10000 0x00 0xfdad0000 0x00 0x10000>; - interrupts = <0x00 0x6e 0x04 0x00 0x6f 0x04 0x00 0x70 0x04>; - interrupt-names = "npu0_irq\0npu1_irq\0npu2_irq"; - clocks = <0x0e 0x06 0x02 0x12d 0x02 0x122 0x02 0x124 0x02 0x12e 0x02 0x123 0x02 0x125 0x02 0x131>; - clock-names = "clk_npu\0aclk0\0aclk1\0aclk2\0hclk0\0hclk1\0hclk2\0pclk"; - assigned-clocks = <0x0e 0x06>; - assigned-clock-rates = <0xbebc200>; - resets = <0x02 0x1e6 0x02 0x1b0 0x02 0x1c0 0x02 0x1e8 0x02 0x1b2 0x02 0x1c2>; - reset-names = "srst_a0\0srst_a1\0srst_a2\0srst_h0\0srst_h1\0srst_h2"; - power-domains = <0x4d 0x09 0x4d 0x0a 0x4d 0x0b>; - power-domain-names = "npu0\0npu1\0npu2"; - operating-points-v2 = <0x99>; - iommus = <0x9a>; - status = "okay"; - rknpu-supply = <0x9b>; - mem-supply = <0x9b>; - }; - - npu-opp-table { - compatible = "operating-points-v2"; - nvmem-cells = <0x9c 0x20>; - nvmem-cell-names = "leakage\0specification_serial_number"; - rockchip,supported-hw; - rockchip,pvtm-voltage-sel = <0x00 0x32f 0x00 0x330 0x343 0x01 0x344 0x35c 0x02 0x35d 0x375 0x03 0x376 0x38e 0x04 0x38f 0x270f 0x05>; - rockchip,pvtm-pvtpll; - rockchip,pvtm-offset = <0x50>; - rockchip,pvtm-sample-time = <0x44c>; - rockchip,pvtm-freq = <0xc3500>; - rockchip,pvtm-volt = <0xb71b0>; - rockchip,pvtm-ref-temp = <0x19>; - rockchip,pvtm-temp-prop = <0xffffff8f 0xffffff8f>; - rockchip,pvtm-thermal-zone = "npu-thermal"; - clocks = <0x02 0x12a>; - clock-names = "pclk"; - rockchip,grf = <0x9d>; - volt-mem-read-margin = <0xd0bd8 0x01 0xbac48 0x02 0xa4cb8 0x03 0x78d98 0x04>; - low-volt-read-margin = <0x04>; - intermediate-threshold-freq = <0x7a120>; - rockchip,init-freq = <0xf4240>; - rockchip,temp-hysteresis = <0x1388>; - rockchip,low-temp = <0x2710>; - rockchip,low-temp-min-volt = <0xb71b0>; - rockchip,high-temp = <0x14c08>; - rockchip,high-temp-max-freq = <0xc3500>; - phandle = <0x99>; - - opp-300000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x11e1a300>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L1 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-400000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x17d78400>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L1 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-500000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x1dcd6500>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L1 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-600000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x23c34600>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L1 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L2 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L3 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-700000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x29b92700>; - opp-microvolt = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - opp-microvolt-L3 = <0xa7d8c 0xa7d8c 0xcf850 0xa7d8c 0xa7d8c 0xcf850>; - opp-microvolt-L4 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - opp-microvolt-L5 = <0xa4cb8 0xa4cb8 0xcf850 0xa4cb8 0xa4cb8 0xcf850>; - }; - - opp-800000000 { - opp-supported-hw = <0xff 0xffff>; - opp-hz = <0x00 0x2faf0800>; - opp-microvolt = <0xb71b0 0xb71b0 0xcf850 0xb71b0 0xb71b0 0xcf850>; - opp-microvolt-L2 = <0xb40dc 0xb40dc 0xcf850 0xb40dc 0xb40dc 0xcf850>; - opp-microvolt-L3 = <0xb1008 0xb1008 0xcf850 0xb1008 0xb1008 0xcf850>; - opp-microvolt-L4 = <0xadf34 0xadf34 0xcf850 0xadf34 0xadf34 0xcf850>; - opp-microvolt-L5 = <0xaae60 0xaae60 0xcf850 0xaae60 0xaae60 0xcf850>; - }; - - opp-900000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x35a4e900>; - opp-microvolt = <0xc3500 0xc3500 0xcf850 0xc3500 0xc3500 0xcf850>; - opp-microvolt-L1 = <0xc042c 0xc042c 0xcf850 0xc042c 0xc042c 0xcf850>; - opp-microvolt-L2 = <0xbd358 0xbd358 0xcf850 0xbd358 0xbd358 0xcf850>; - opp-microvolt-L3 = <0xba284 0xba284 0xcf850 0xba284 0xba284 0xcf850>; - opp-microvolt-L4 = <0xb71b0 0xb71b0 0xcf850 0xb71b0 0xb71b0 0xcf850>; - opp-microvolt-L5 = <0xb40dc 0xb40dc 0xcf850 0xb40dc 0xb40dc 0xcf850>; - }; - - opp-1000000000 { - opp-supported-hw = <0xfb 0xffff>; - opp-hz = <0x00 0x3b9aca00>; - opp-microvolt = <0xcf850 0xcf850 0xcf850 0xcf850 0xcf850 0xcf850>; - opp-microvolt-L1 = <0xcc77c 0xcc77c 0xcf850 0xcc77c 0xcc77c 0xcf850>; - opp-microvolt-L2 = <0xc96a8 0xc96a8 0xcf850 0xc96a8 0xc96a8 0xcf850>; - opp-microvolt-L3 = <0xc65d4 0xc65d4 0xcf850 0xc65d4 0xc65d4 0xcf850>; - opp-microvolt-L4 = <0xc3500 0xc3500 0xcf850 0xc3500 0xc3500 0xcf850>; - opp-microvolt-L5 = <0xc042c 0xc042c 0xcf850 0xc042c 0xc042c 0xcf850>; - }; - }; - - iommu@fdab9000 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdab9000 0x00 0x100 0x00 0xfdaba000 0x00 0x100 0x00 0xfdaca000 0x00 0x100 0x00 0xfdada000 0x00 0x100>; - interrupts = <0x00 0x6e 0x04 0x00 0x6f 0x04 0x00 0x70 0x04>; - interrupt-names = "npu0_mmu\0npu1_mmu\0npu2_mmu"; - clocks = <0x02 0x12d 0x02 0x122 0x02 0x124 0x02 0x12e 0x02 0x123 0x02 0x125>; - clock-names = "aclk0\0aclk1\0aclk2\0iface0\0iface1\0iface2"; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x9a>; - }; - - vepu@fdb50000 { - compatible = "rockchip,vpu-encoder-v2"; - reg = <0x00 0xfdb50000 0x00 0x400>; - interrupts = <0x00 0x78 0x04>; - interrupt-names = "irq_vepu"; - clocks = <0x02 0x1c0 0x02 0x1c1>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1c0>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2c8 0x02 0x2c9>; - reset-names = "shared_video_a\0shared_video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0x9e>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x00>; - rockchip,resetgroup-node = <0x00>; - power-domains = <0x4d 0x15>; - status = "disabled"; - }; - - vdpu@fdb50400 { - compatible = "rockchip,vpu-decoder-v2"; - reg = <0x00 0xfdb50400 0x00 0x400>; - interrupts = <0x00 0x77 0x04>; - interrupt-names = "irq_vdpu"; - clocks = <0x02 0x1c0 0x02 0x1c1>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1c0>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2c8 0x02 0x2c9>; - reset-names = "shared_video_a\0shared_video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0x9e>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x00>; - rockchip,resetgroup-node = <0x00>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdb50800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdb50800 0x00 0x40>; - interrupts = <0x00 0x76 0x04>; - interrupt-names = "irq_vdpu_mmu"; - clocks = <0x02 0x1c0 0x02 0x1c1>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0x9e>; - }; - - avsd-plus@fdb51000 { - compatible = "rockchip,avs-plus-decoder"; - reg = <0x00 0xfdb51000 0x00 0x200>; - interrupts = <0x00 0x77 0x04>; - interrupt-names = "irq_avsd"; - clocks = <0x02 0x1c0 0x02 0x1c1>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - resets = <0x02 0x2c8 0x02 0x2c9>; - reset-names = "shared_video_a\0shared_video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0x9e>; - power-domains = <0x4d 0x15>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x00>; - rockchip,resetgroup-node = <0x00>; - status = "disabled"; - }; - - rga@fdb60000 { - compatible = "rockchip,rga3_core0"; - reg = <0x00 0xfdb60000 0x00 0x1000>; - interrupts = <0x00 0x72 0x04>; - interrupt-names = "rga3_core0_irq"; - clocks = <0x02 0x1ba 0x02 0x1b9 0x02 0x1bb>; - clock-names = "aclk_rga3_0\0hclk_rga3_0\0clk_rga3_0"; - power-domains = <0x4d 0x16>; - iommus = <0xa0>; - status = "okay"; - }; - - iommu@fdb60f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdb60f00 0x00 0x100>; - interrupts = <0x00 0x72 0x04>; - interrupt-names = "rga3_0_mmu"; - clocks = <0x02 0x1ba 0x02 0x1b9>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x16>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa0>; - }; - - rga@fdb70000 { - compatible = "rockchip,rga3_core1"; - reg = <0x00 0xfdb70000 0x00 0x1000>; - interrupts = <0x00 0x73 0x04>; - interrupt-names = "rga3_core1_irq"; - clocks = <0x02 0x18a 0x02 0x189 0x02 0x18b>; - clock-names = "aclk_rga3_1\0hclk_rga3_1\0clk_rga3_1"; - power-domains = <0x4d 0x1e>; - iommus = <0xa1>; - status = "okay"; - }; - - iommu@fdb70f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdb70f00 0x00 0x100>; - interrupts = <0x00 0x73 0x04>; - interrupt-names = "rga3_1_mmu"; - clocks = <0x02 0x18a 0x02 0x189>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x1e>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa1>; - }; - - rga@fdb80000 { - compatible = "rockchip,rga2_core0"; - reg = <0x00 0xfdb80000 0x00 0x1000>; - interrupts = <0x00 0x74 0x04>; - interrupt-names = "rga2_irq"; - clocks = <0x02 0x1b7 0x02 0x1b6 0x02 0x1b8>; - clock-names = "aclk_rga2\0hclk_rga2\0clk_rga2"; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - jpegd@fdb90000 { - compatible = "rockchip,rkv-jpeg-decoder-v1"; - reg = <0x00 0xfdb90000 0x00 0x400>; - interrupts = <0x00 0x81 0x04>; - interrupt-names = "irq_jpegd"; - clocks = <0x02 0x1b4 0x02 0x1b5>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x23c34600 0x00>; - assigned-clocks = <0x02 0x1b4>; - assigned-clock-rates = <0x23c34600>; - resets = <0x02 0x2d2 0x02 0x2d3>; - reset-names = "video_a\0video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0xa2>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x01>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdb90480 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdb90480 0x00 0x40>; - interrupts = <0x00 0x82 0x04>; - interrupt-names = "irq_jpegd_mmu"; - clocks = <0x02 0x1b4 0x02 0x1b5>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa2>; - }; - - jpege-core@fdba0000 { - compatible = "rockchip,vpu-jpege-core"; - reg = <0x00 0xfdba0000 0x00 0x400>; - interrupts = <0x00 0x7a 0x04>; - interrupt-names = "irq_jpege0"; - clocks = <0x02 0x1ac 0x02 0x1ad>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1ac>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2ca 0x02 0x2cb>; - reset-names = "video_a\0video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0xa3>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x02>; - rockchip,ccu = <0xa4>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdba0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdba0800 0x00 0x40>; - interrupts = <0x00 0x79 0x04>; - interrupt-names = "irq_jpege0_mmu"; - clocks = <0x02 0x1ac 0x02 0x1ad>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa3>; - }; - - jpege-core@fdba4000 { - compatible = "rockchip,vpu-jpege-core"; - reg = <0x00 0xfdba4000 0x00 0x400>; - interrupts = <0x00 0x7c 0x04>; - interrupt-names = "irq_jpege1"; - clocks = <0x02 0x1ae 0x02 0x1af>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1ae>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2cc 0x02 0x2cd>; - reset-names = "video_a\0video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0xa5>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x02>; - rockchip,ccu = <0xa4>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdba4800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdba4800 0x00 0x40>; - interrupts = <0x00 0x7b 0x04>; - interrupt-names = "irq_jpege1_mmu"; - clocks = <0x02 0x1ae 0x02 0x1af>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa5>; - }; - - jpege-core@fdba8000 { - compatible = "rockchip,vpu-jpege-core"; - reg = <0x00 0xfdba8000 0x00 0x400>; - interrupts = <0x00 0x7e 0x04>; - interrupt-names = "irq_jpege2"; - clocks = <0x02 0x1b0 0x02 0x1b1>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1b0>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2ce 0x02 0x2cf>; - reset-names = "video_a\0video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0xa6>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x02>; - rockchip,ccu = <0xa4>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdba8800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdba8800 0x00 0x40>; - interrupts = <0x00 0x7d 0x04>; - interrupt-names = "irq_jpege2_mmu"; - clocks = <0x02 0x1b0 0x02 0x1b1>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa6>; - }; - - jpege-core@fdbac000 { - compatible = "rockchip,vpu-jpege-core"; - reg = <0x00 0xfdbac000 0x00 0x400>; - interrupts = <0x00 0x80 0x04>; - interrupt-names = "irq_jpege3"; - clocks = <0x02 0x1b2 0x02 0x1b3>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x2367b880 0x00>; - assigned-clocks = <0x02 0x1b2>; - assigned-clock-rates = <0x2367b880>; - resets = <0x02 0x2d0 0x02 0x2d1>; - reset-names = "video_a\0video_h"; - rockchip,skip-pmu-idle-request; - iommus = <0xa7>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x02>; - rockchip,ccu = <0xa4>; - power-domains = <0x4d 0x15>; - status = "okay"; - }; - - iommu@fdbac800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdbac800 0x00 0x40>; - interrupts = <0x00 0x7f 0x04>; - interrupt-names = "irq_jpege3_mmu"; - clocks = <0x02 0x1b2 0x02 0x1b3>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x15>; - #iommu-cells = <0x00>; - status = "okay"; - phandle = <0xa7>; - }; - - iep@fdbb0000 { - compatible = "rockchip,iep-v2"; - reg = <0x00 0xfdbb0000 0x00 0x500>; - interrupts = <0x00 0x75 0x04>; - interrupt-names = "irq_iep"; - clocks = <0x02 0x1aa 0x02 0x1a9 0x02 0x1ab>; - clock-names = "aclk\0hclk\0sclk"; - resets = <0x02 0x2d5 0x02 0x2d4 0x02 0x2d6>; - reset-names = "rst_a\0rst_h\0rst_s"; - rockchip,skip-pmu-idle-request; - power-domains = <0x4d 0x15>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x06>; - iommus = <0xa8>; - status = "okay"; - }; - - iommu@fdbb0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdbb0800 0x00 0x100>; - interrupts = <0x00 0x75 0x04>; - interrupt-names = "irq_iep_mmu"; - clocks = <0x02 0x1aa 0x02 0x1a9>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x15>; - status = "okay"; - phandle = <0xa8>; - }; - - rkvenc-core@fdbd0000 { - compatible = "rockchip,rkv-encoder-v2-core"; - reg = <0x00 0xfdbd0000 0x00 0x6000>; - interrupts = <0x00 0x65 0x04>; - interrupt-names = "irq_rkvenc0"; - clocks = <0x02 0x1c5 0x02 0x1c4 0x02 0x1c6>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core"; - rockchip,normal-rates = <0x23c34600 0x00 0x2faf0800>; - assigned-clocks = <0x02 0x1c5 0x02 0x1c6>; - assigned-clock-rates = <0x23c34600 0x2faf0800>; - resets = <0x02 0x2f5 0x02 0x2f4 0x02 0x2f6>; - reset-names = "video_a\0video_h\0video_core"; - rockchip,skip-pmu-idle-request; - iommus = <0xa9>; - rockchip,srv = <0x9f>; - rockchip,ccu = <0xaa>; - rockchip,taskqueue-node = <0x07>; - rockchip,task-capacity = <0x08>; - power-domains = <0x4d 0x10>; - status = "okay"; - }; - - iommu@fdbdf000 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdbdf000 0x00 0x40 0x00 0xfdbdf040 0x00 0x40>; - interrupts = <0x00 0x63 0x04 0x00 0x64 0x04>; - interrupt-names = "irq_rkvenc0_mmu0\0irq_rkvenc0_mmu1"; - clocks = <0x02 0x1c5 0x02 0x1c4>; - clock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - rockchip,shootdown-entire; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x10>; - status = "okay"; - phandle = <0xa9>; - }; - - rkvenc-core@fdbe0000 { - compatible = "rockchip,rkv-encoder-v2-core"; - reg = <0x00 0xfdbe0000 0x00 0x6000>; - interrupts = <0x00 0x68 0x04>; - interrupt-names = "irq_rkvenc1"; - clocks = <0x02 0x1ca 0x02 0x1c9 0x02 0x1cb>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core"; - rockchip,normal-rates = <0x23c34600 0x00 0x2faf0800>; - assigned-clocks = <0x02 0x1ca 0x02 0x1cb>; - assigned-clock-rates = <0x23c34600 0x2faf0800>; - resets = <0x02 0x305 0x02 0x304 0x02 0x306>; - reset-names = "video_a\0video_h\0video_core"; - rockchip,skip-pmu-idle-request; - iommus = <0xab>; - rockchip,srv = <0x9f>; - rockchip,ccu = <0xaa>; - rockchip,taskqueue-node = <0x07>; - rockchip,task-capacity = <0x08>; - power-domains = <0x4d 0x11>; - status = "okay"; - }; - - iommu@fdbef000 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdbef000 0x00 0x40 0x00 0xfdbef040 0x00 0x40>; - interrupts = <0x00 0x66 0x04 0x00 0x67 0x04>; - interrupt-names = "irq_rkvenc1_mmu0\0irq_rkvenc1_mmu1"; - clocks = <0x02 0x1ca 0x02 0x1c9>; - lock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - rockchip,shootdown-entire; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x11>; - status = "okay"; - phandle = <0xab>; - }; - - rkvdec-ccu@fdc30000 { - compatible = "rockchip,rkv-decoder-v2-ccu"; - reg = <0x00 0xfdc30000 0x00 0x100>; - reg-names = "ccu"; - clocks = <0x02 0x18e>; - clock-names = "aclk_ccu"; - assigned-clocks = <0x02 0x18e>; - assigned-clock-rates = <0x23c34600>; - resets = <0x02 0x282>; - reset-names = "video_ccu"; - rockchip,skip-pmu-idle-request; - power-domains = <0x4d 0x0e>; - status = "okay"; - phandle = <0xad>; - }; - - rkvdec-core@fdc38000 { - compatible = "rockchip,rkv-decoder-v2"; - reg = <0x00 0xfdc38100 0x00 0x400 0x00 0xfdc38000 0x00 0x100>; - reg-names = "regs\0link"; - interrupts = <0x00 0x5f 0x04>; - interrupt-names = "irq_rkvdec0"; - clocks = <0x02 0x190 0x02 0x18f 0x02 0x193 0x02 0x191 0x02 0x192>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core\0clk_cabac\0clk_hevc_cabac"; - rockchip,normal-rates = <0x2faf0800 0x00 0x23c34600 0x23c34600 0x3b9aca00>; - assigned-clocks = <0x02 0x190 0x02 0x193 0x02 0x191 0x02 0x192>; - assigned-clock-rates = <0x2faf0800 0x23c34600 0x23c34600 0x3b9aca00>; - resets = <0x02 0x284 0x02 0x283 0x02 0x289 0x02 0x287 0x02 0x288>; - reset-names = "video_a\0video_h\0video_core\0video_cabac\0video_hevc_cabac"; - rockchip,skip-pmu-idle-request; - iommus = <0xac>; - rockchip,srv = <0x9f>; - rockchip,ccu = <0xad>; - rockchip,core-mask = <0x10001>; - rockchip,taskqueue-node = <0x09>; - rockchip,sram = <0xae>; - rockchip,rcb-iova = <0xfff00000 0x100000>; - rockchip,rcb-min-width = <0x200>; - power-domains = <0x4d 0x0e>; - status = "okay"; - }; - - iommu@fdc38700 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdc38700 0x00 0x40 0x00 0xfdc38740 0x00 0x40>; - interrupts = <0x00 0x60 0x04>; - interrupt-names = "irq_rkvdec0_mmu"; - clocks = <0x02 0x190 0x02 0x18f>; - clock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - rockchip,shootdown-entire; - rockchip,master-handle-irq; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x0e>; - status = "okay"; - phandle = <0xac>; - }; - - rkvdec-core@fdc48000 { - compatible = "rockchip,rkv-decoder-v2"; - reg = <0x00 0xfdc48100 0x00 0x400 0x00 0xfdc48000 0x00 0x100>; - reg-names = "regs\0link"; - interrupts = <0x00 0x61 0x04>; - interrupt-names = "irq_rkvdec1"; - clocks = <0x02 0x195 0x02 0x194 0x02 0x198 0x02 0x196 0x02 0x197>; - clock-names = "aclk_vcodec\0hclk_vcodec\0clk_core\0clk_cabac\0clk_hevc_cabac"; - rockchip,normal-rates = <0x2faf0800 0x00 0x23c34600 0x23c34600 0x3b9aca00>; - assigned-clocks = <0x02 0x195 0x02 0x198 0x02 0x196 0x02 0x197>; - assigned-clock-rates = <0x2faf0800 0x23c34600 0x23c34600 0x3b9aca00>; - resets = <0x02 0x293 0x02 0x292 0x02 0x298 0x02 0x296 0x02 0x297>; - reset-names = "video_a\0video_h\0video_core\0video_cabac\0video_hevc_cabac"; - rockchip,skip-pmu-idle-request; - iommus = <0xaf>; - rockchip,srv = <0x9f>; - rockchip,ccu = <0xad>; - rockchip,core-mask = <0x20002>; - rockchip,taskqueue-node = <0x09>; - rockchip,sram = <0xb0>; - rockchip,rcb-iova = <0xffe00000 0x100000>; - rockchip,rcb-min-width = <0x200>; - power-domains = <0x4d 0x0f>; - status = "okay"; - }; - - iommu@fdc48700 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdc48700 0x00 0x40 0x00 0xfdc48740 0x00 0x40>; - interrupts = <0x00 0x62 0x04>; - interrupt-names = "irq_rkvdec1_mmu"; - clocks = <0x02 0x195 0x02 0x194>; - clock-names = "aclk\0iface"; - rockchip,disable-mmu-reset; - rockchip,enable-cmd-retry; - rockchip,shootdown-entire; - rockchip,master-handle-irq; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x0f>; - status = "okay"; - phandle = <0xaf>; - }; - - av1d@fdc70000 { - compatible = "rockchip,av1-decoder"; - reg = <0x00 0xfdc70000 0x00 0x800 0x00 0xfdc80000 0x00 0x400 0x00 0xfdc90000 0x00 0x400>; - reg-names = "vcd\0cache\0afbc"; - interrupts = <0x00 0x6c 0x04 0x00 0x6b 0x04 0x00 0x6a 0x04>; - interrupt-names = "irq_av1d\0irq_cache\0irq_afbc"; - clocks = <0x02 0x49 0x02 0x4b>; - clock-names = "aclk_vcodec\0hclk_vcodec"; - rockchip,normal-rates = <0x17d78400 0x17d78400>; - assigned-clocks = <0x02 0x49 0x02 0x4b>; - assigned-clock-rates = <0x17d78400 0x17d78400>; - resets = <0x02 0x442 0x02 0x445>; - reset-names = "video_a\0video_h"; - iommus = <0xb1>; - rockchip,srv = <0x9f>; - rockchip,taskqueue-node = <0x0b>; - power-domains = <0x4d 0x17>; - status = "okay"; - }; - - iommu@fdca0000 { - compatible = "rockchip,iommu-av1"; - reg = <0x00 0xfdca0000 0x00 0x600>; - interrupts = <0x00 0x6d 0x04>; - interrupt-names = "irq_av1d_mmu"; - clocks = <0x02 0x49 0x02 0x4b>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - power-domains = <0x4d 0x17>; - status = "okay"; - phandle = <0xb1>; - }; - - rkisp-unite@fdcb0000 { - compatible = "rockchip,rk3588-rkisp-unite"; - reg = <0x00 0xfdcb0000 0x00 0x10000 0x00 0xfdcc0000 0x00 0x10000>; - interrupts = <0x00 0x87 0x04 0x00 0x89 0x04 0x00 0x8a 0x04>; - interrupt-names = "isp_irq\0mi_irq\0mipi_irq"; - clocks = <0x02 0x1de 0x02 0x1df 0x02 0x1db 0x02 0x1dc 0x02 0x1dd 0x02 0x120 0x02 0x121 0x02 0x11d 0x02 0x11e 0x02 0x11f>; - clock-names = "aclk_isp0\0hclk_isp0\0clk_isp_core0\0clk_isp_core_marvin0\0clk_isp_core_vicap0\0aclk_isp1\0hclk_isp1\0clk_isp_core1\0clk_isp_core_marvin1\0clk_isp_core_vicap1"; - power-domains = <0x4d 0x1c>; - iommus = <0xb2>; - status = "disabled"; - }; - - rkisp@fdcb0000 { - compatible = "rockchip,rk3588-rkisp"; - reg = <0x00 0xfdcb0000 0x00 0x7f00>; - interrupts = <0x00 0x83 0x04 0x00 0x85 0x04 0x00 0x86 0x04>; - interrupt-names = "isp_irq\0mi_irq\0mipi_irq"; - clocks = <0x02 0x1de 0x02 0x1df 0x02 0x1db 0x02 0x1dc 0x02 0x1dd>; - clock-names = "aclk_isp\0hclk_isp\0clk_isp_core\0clk_isp_core_marvin\0clk_isp_core_vicap"; - power-domains = <0x4d 0x1b>; - iommus = <0xb3>; - status = "disabled"; - phandle = <0x46>; - }; - - rkisp-unite-mmu@fdcb7f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdcb7f00 0x00 0x100 0x00 0xfdcc7f00 0x00 0x100>; - interrupts = <0x00 0x84 0x04 0x00 0x88 0x04>; - interrupt-names = "isp0_mmu\0isp1_mmu"; - clocks = <0x02 0x1de 0x02 0x1df 0x02 0x120 0x02 0x121>; - clock-names = "aclk0\0iface0\0aclk1\0iface1"; - power-domains = <0x4d 0x1c>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "disabled"; - phandle = <0xb2>; - }; - - iommu@fdcb7f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdcb7f00 0x00 0x100>; - interrupts = <0x00 0x84 0x04>; - interrupt-names = "isp0_mmu"; - clocks = <0x02 0x1de 0x02 0x1df>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x1b>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "disabled"; - phandle = <0xb3>; - }; - - rkisp@fdcc0000 { - compatible = "rockchip,rk3588-rkisp"; - reg = <0x00 0xfdcc0000 0x00 0x7f00>; - interrupts = <0x00 0x87 0x04 0x00 0x89 0x04 0x00 0x8a 0x04>; - interrupt-names = "isp_irq\0mi_irq\0mipi_irq"; - clocks = <0x02 0x120 0x02 0x121 0x02 0x11d 0x02 0x11e 0x02 0x11f>; - clock-names = "aclk_isp\0hclk_isp\0clk_isp_core\0clk_isp_core_marvin\0clk_isp_core_vicap"; - power-domains = <0x4d 0x1c>; - iommus = <0xb4>; - status = "disabled"; - phandle = <0x47>; - }; - - iommu@fdcc7f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdcc7f00 0x00 0x100>; - interrupts = <0x00 0x88 0x04>; - interrupt-names = "isp1_mmu"; - clocks = <0x02 0x120 0x02 0x121>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x1c>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "disabled"; - phandle = <0xb4>; - }; - - rkispp@fdcd0000 { - compatible = "rockchip,rk3588-rkispp"; - reg = <0x00 0xfdcd0000 0x00 0xf00>; - interrupts = <0x00 0x8b 0x04>; - interrupt-names = "fec_irq"; - clocks = <0x02 0x1d5 0x02 0x1d6 0x02 0x1d7>; - clock-names = "aclk_ispp\0hclk_ispp\0clk_ispp"; - assigned-clocks = <0x02 0x1d6>; - assigned-clock-rates = <0x5f5e100>; - power-domains = <0x4d 0x1d>; - iommus = <0xb5>; - status = "disabled"; - phandle = <0x48>; - }; - - iommu@fdcd0f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdcd0f00 0x00 0x100>; - interrupts = <0x00 0x8c 0x04>; - interrupt-names = "fec0_mmu"; - clocks = <0x02 0x1d5 0x02 0x1d6 0x02 0x1d7>; - clock-names = "aclk\0iface\0pclk"; - power-domains = <0x4d 0x1d>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "disabled"; - phandle = <0xb5>; - }; - - rkispp@fdcd8000 { - compatible = "rockchip,rk3588-rkispp"; - reg = <0x00 0xfdcd8000 0x00 0xf00>; - interrupts = <0x00 0x8d 0x04>; - interrupt-names = "fec_irq"; - clocks = <0x02 0x1d8 0x02 0x1d9 0x02 0x1da>; - clock-names = "aclk_ispp\0hclk_ispp\0clk_ispp"; - assigned-clocks = <0x02 0x1d9>; - assigned-clock-rates = <0x5f5e100>; - power-domains = <0x4d 0x1d>; - iommus = <0xb6>; - status = "disabled"; - phandle = <0x49>; - }; - - iommu@fdcd8f00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdcd8f00 0x00 0x100>; - interrupts = <0x00 0x8e 0x04>; - interrupt-names = "fec1_mmu"; - clocks = <0x02 0x1d8 0x02 0x1d9 0x02 0x1da>; - clock-names = "aclk\0iface\0pclk"; - power-domains = <0x4d 0x1d>; - #iommu-cells = <0x00>; - rockchip,disable-mmu-reset; - status = "disabled"; - phandle = <0xb6>; - }; - - rkcif@fdce0000 { - compatible = "rockchip,rk3588-cif"; - reg = <0x00 0xfdce0000 0x00 0x800>; - reg-names = "cif_regs"; - interrupts = <0x00 0x9b 0x04>; - interrupt-names = "cif-intr"; - clocks = <0x02 0x1e4 0x02 0x1e5 0x02 0x1e3>; - clock-names = "aclk_cif\0hclk_cif\0dclk_cif"; - resets = <0x02 0x317 0x02 0x318 0x02 0x316>; - reset-names = "rst_cif_a\0rst_cif_h\0rst_cif_d"; - assigned-clocks = <0x02 0x1e3>; - assigned-clock-rates = <0x23c34600>; - power-domains = <0x4d 0x1b>; - rockchip,grf = <0xb7>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x3f>; - }; - - iommu@fdce0800 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdce0800 0x00 0x100 0x00 0xfdce0900 0x00 0x100>; - interrupts = <0x00 0x71 0x04>; - interrupt-names = "cif_mmu"; - clocks = <0x02 0x1e4 0x02 0x1e5>; - clock-names = "aclk\0iface"; - power-domains = <0x4d 0x1b>; - rockchip,disable-mmu-reset; - #iommu-cells = <0x00>; - status = "disabled"; - phandle = <0x40>; - }; - - mipi0-csi2@fdd10000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd10000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x8f 0x04 0x00 0x90 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1cf 0x02 0x1cd>; - clock-names = "pclk_csi2host\0iclk_csi2host"; - resets = <0x02 0x324 0x02 0x334>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - mipi1-csi2@fdd20000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd20000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x91 0x04 0x00 0x92 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1d0 0x02 0x1ce>; - clock-names = "pclk_csi2host\0iclk_csi2host"; - resets = <0x02 0x325 0x02 0x335>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - mipi2-csi2@fdd30000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd30000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x93 0x04 0x00 0x94 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1d1>; - clock-names = "pclk_csi2host"; - resets = <0x02 0x326 0x02 0x336>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - mipi3-csi2@fdd40000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd40000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x95 0x04 0x00 0x96 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1d2>; - clock-names = "pclk_csi2host"; - resets = <0x02 0x327 0x02 0x337>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - vop@fdd90000 { - compatible = "rockchip,rk3588-vop"; - reg = <0x00 0xfdd90000 0x00 0x4200 0x00 0xfdd95000 0x00 0x1000>; - reg-names = "regs\0gamma_lut"; - interrupts = <0x00 0x9c 0x04>; - clocks = <0x02 0x270 0x02 0x26f 0x02 0x274 0x02 0x275 0x02 0x276 0x02 0x277 0x02 0x26e 0x02 0x271 0x02 0x272 0x02 0x273>; - clock-names = "aclk_vop\0hclk_vop\0dclk_vp0\0dclk_vp1\0dclk_vp2\0dclk_vp3\0pclk_vop\0dclk_src_vp0\0dclk_src_vp1\0dclk_src_vp2"; - assigned-clocks = <0x02 0x270>; - assigned-clock-rates = <0x2faf0800>; - resets = <0x02 0x349 0x02 0x348 0x02 0x34d 0x02 0x350 0x02 0x351 0x02 0x352>; - reset-names = "axi\0ahb\0dclk_vp0\0dclk_vp1\0dclk_vp2\0dclk_vp3"; - iommus = <0xb8>; - power-domains = <0x4d 0x18>; - rockchip,grf = <0xb7>; - rockchip,vop-grf = <0xb9>; - rockchip,vo1-grf = <0xba>; - rockchip,pmu = <0xbb>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - phandle = <0x2d>; - - port@0 { - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x00>; - rockchip,plane-mask = <0x05>; - rockchip,primary-plane = <0x02>; - assigned-clocks = <0x02 0x270>; - assigned-clock-rates = <0x2faf0800>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xbc>; - phandle = <0xd9>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xbd>; - phandle = <0xe4>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xbe>; - phandle = <0x35>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0xbf>; - phandle = <0x18a>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0xc0>; - phandle = <0x194>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0xc1>; - phandle = <0x191>; - }; - }; - - port@1 { - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x01>; - rockchip,plane-mask = <0x0a>; - rockchip,primary-plane = <0x03>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xc2>; - phandle = <0x31>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xc3>; - phandle = <0xe5>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xc4>; - phandle = <0xe1>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0xc5>; - phandle = <0x18b>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0xc6>; - phandle = <0x195>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0xc7>; - phandle = <0x38>; - }; - }; - - port@2 { - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x02>; - assigned-clocks = <0x02 0x273>; - assigned-clock-parents = <0x02 0x04>; - rockchip,plane-mask = <0x140>; - rockchip,primary-plane = <0x08>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xc8>; - phandle = <0xda>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xc9>; - phandle = <0x34>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xca>; - phandle = <0xe2>; - }; - - endpoint@3 { - reg = <0x03>; - remote-endpoint = <0xcb>; - phandle = <0xd5>; - }; - - endpoint@4 { - reg = <0x04>; - remote-endpoint = <0xcc>; - phandle = <0xd6>; - }; - - endpoint@5 { - reg = <0x05>; - remote-endpoint = <0xcd>; - phandle = <0x37>; - }; - - endpoint@6 { - reg = <0x06>; - remote-endpoint = <0xce>; - phandle = <0x196>; - }; - - endpoint@7 { - reg = <0x07>; - remote-endpoint = <0xcf>; - phandle = <0x192>; - }; - }; - - port@3 { - #address-cells = <0x01>; - #size-cells = <0x00>; - reg = <0x03>; - rockchip,plane-mask = <0x280>; - rockchip,primary-plane = <0x09>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xd0>; - phandle = <0x32>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xd1>; - phandle = <0x33>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xd2>; - phandle = <0x36>; - }; - }; - }; - }; - - iommu@fdd97e00 { - compatible = "rockchip,iommu-v2"; - reg = <0x00 0xfdd97e00 0x00 0x100 0x00 0xfdd97f00 0x00 0x100>; - interrupts = <0x00 0x9c 0x04>; - interrupt-names = "vop_mmu"; - clocks = <0x02 0x270 0x02 0x26f>; - clock-names = "aclk\0iface"; - #iommu-cells = <0x00>; - rockchip,disable-device-link-resume; - rockchip,shootdown-entire; - status = "okay"; - phandle = <0xb8>; - }; - - spdif-tx@fddb0000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfddb0000 0x00 0x1000>; - interrupts = <0x00 0xc3 0x04>; - dmas = <0xd3 0x06>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x209 0x02 0x204>; - assigned-clocks = <0x02 0x205>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x19>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x1af>; - }; - - i2s@fddc0000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddc0000 0x00 0x1000>; - interrupts = <0x00 0xb8 0x04>; - clocks = <0x02 0x1fb 0x02 0x1fb 0x02 0x1f0>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x1f9>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd4 0x00>; - dma-names = "tx"; - power-domains = <0x4d 0x19>; - resets = <0x02 0x38d>; - reset-names = "tx-m"; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-tx@fdde0000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfdde0000 0x00 0x1000>; - interrupts = <0x00 0xc4 0x04>; - dmas = <0xd3 0x07>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x257 0x02 0x253>; - assigned-clocks = <0x02 0x254>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x1a>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - i2s@fddf0000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddf0000 0x00 0x1000>; - interrupts = <0x00 0xb9 0x04>; - clocks = <0x02 0x246 0x02 0x246 0x02 0x248>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x243>; - assigned-clock-parents = <0x02 0x07>; - dmas = <0xd4 0x02>; - dma-names = "tx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x3e8>; - reset-names = "tx-m"; - rockchip,always-on; - rockchip,hdmi-path; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x1ad>; - }; - - i2s@fddfc000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddfc000 0x00 0x1000>; - interrupts = <0x00 0xbd 0x04>; - clocks = <0x02 0x242 0x02 0x242 0x02 0x23e>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x23f>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd4 0x17>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x413>; - reset-names = "rx-m"; - rockchip,capture-only; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-rx@fde08000 { - compatible = "rockchip,rk3588-spdifrx\0rockchip,rk3308-spdifrx"; - reg = <0x00 0xfde08000 0x00 0x1000>; - interrupts = <0x00 0xc7 0x04>; - clocks = <0x02 0x25e 0x02 0x25d>; - clock-names = "mclk\0hclk"; - assigned-clocks = <0x02 0x25e>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0x64 0x15>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x3fd>; - reset-names = "spdifrx-m"; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - dsi@fde20000 { - compatible = "rockchip,rk3588-mipi-dsi2"; - reg = <0x00 0xfde20000 0x00 0x10000>; - interrupts = <0x00 0xa7 0x04>; - clocks = <0x02 0x278 0x02 0x27a>; - clock-names = "pclk\0sys_clk"; - resets = <0x02 0x354>; - reset-names = "apb"; - power-domains = <0x4d 0x18>; - phys = <0x2a>; - phy-names = "dcphy"; - rockchip,grf = <0xb9>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xd5>; - status = "disabled"; - phandle = <0xcb>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x32>; - status = "disabled"; - phandle = <0xd0>; - }; - }; - }; - }; - - dsi@fde30000 { - compatible = "rockchip,rk3588-mipi-dsi2"; - reg = <0x00 0xfde30000 0x00 0x10000>; - interrupts = <0x00 0xa8 0x04>; - clocks = <0x02 0x279 0x02 0x27b>; - clock-names = "pclk\0sys_clk"; - resets = <0x02 0x355>; - reset-names = "apb"; - power-domains = <0x4d 0x18>; - phys = <0x2b>; - phy-names = "dcphy"; - rockchip,grf = <0xb9>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xd6>; - status = "disabled"; - phandle = <0xcc>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x33>; - status = "disabled"; - phandle = <0xd1>; - }; - }; - }; - }; - - hdcp@fde40000 { - compatible = "rockchip,rk3588-hdcp"; - reg = <0x00 0xfde40000 0x00 0x80>; - interrupts = <0x00 0x9f 0x04>; - clocks = <0x02 0x1ed 0x02 0x1ef 0x02 0x1ee 0x02 0x1ec 0x02 0x1f1 0x02 0x1f2>; - clock-names = "aclk\0pclk\0hclk\0hclk_key\0aclk_trng\0pclk_trng"; - resets = <0x02 0x37f 0x02 0x37d 0x02 0x37c 0x02 0x37b 0x02 0x381>; - reset-names = "hdcp\0h_hdcp\0a_hdcp\0hdcp_key\0trng"; - power-domains = <0x4d 0x19>; - rockchip,vo-grf = <0xd7>; - status = "disabled"; - }; - - dp@fde50000 { - compatible = "rockchip,rk3588-dp"; - reg = <0x00 0xfde50000 0x00 0x4000>; - interrupts = <0x00 0xa1 0x04>; - clocks = <0x02 0x1e6 0x02 0x2cc 0x02 0x1fb 0x02 0x207 0x04>; - clock-names = "apb\0aux\0i2s\0spdif\0hclk"; - assigned-clocks = <0x02 0x2cc>; - assigned-clock-rates = <0xf42400>; - resets = <0x02 0x388>; - phys = <0xd8>; - power-domains = <0x4d 0x19>; - #sound-dai-cells = <0x01>; - status = "disabled"; - phandle = <0x1b0>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xd9>; - status = "disabled"; - phandle = <0xbc>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x31>; - status = "disabled"; - phandle = <0xc2>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xda>; - status = "disabled"; - phandle = <0xc8>; - }; - }; - }; - }; - - hdcp@fde70000 { - compatible = "rockchip,rk3588-hdcp"; - reg = <0x00 0xfde70000 0x00 0x80>; - interrupts = <0x00 0xa0 0x04>; - clocks = <0x02 0x217 0x02 0x219 0x02 0x218 0x02 0x216 0x02 0x228 0x02 0x229>; - clock-names = "aclk\0pclk\0hclk\0hclk_key\0aclk_trng\0pclk_trng"; - resets = <0x02 0x3c8 0x02 0x3c6 0x02 0x3c5 0x02 0x3c4 0x02 0x3ca>; - reset-names = "hdcp\0h_hdcp\0a_hdcp\0hdcp_key\0trng"; - power-domains = <0x4d 0x1a>; - rockchip,vo-grf = <0xba>; - status = "disabled"; - }; - - hdmi@fde80000 { - compatible = "rockchip,rk3588-dw-hdmi"; - reg = <0x00 0xfde80000 0x00 0x20000>; - interrupts = <0x00 0xa9 0x04 0x00 0xaa 0x04 0x00 0xab 0x04 0x00 0xac 0x04 0x00 0x168 0x04>; - clocks = <0x02 0x221 0x02 0x265 0x02 0x222 0x02 0x223 0x02 0x246 0x02 0x274 0x02 0x275 0x02 0x276 0x02 0x277 0x05 0x2e>; - clock-names = "pclk\0hpd\0earc\0hdmitx_ref\0aud\0dclk_vp0\0dclk_vp1\0dclk_vp2\0dclk_vp3\0hclk_vo1\0link_clk"; - resets = <0x02 0x3d0 0x02 0x49c>; - reset-names = "ref\0hdp"; - power-domains = <0x4d 0x1a>; - pinctrl-names = "default"; - pinctrl-0 = <0xdb 0xdc 0xdd 0xde>; - reg-io-width = <0x04>; - rockchip,grf = <0xb7>; - rockchip,vo1_grf = <0xba>; - phys = <0xdf>; - phy-names = "hdmi"; - #sound-dai-cells = <0x00>; - status = "okay"; - enable-gpios = <0xe0 0x00 0x00>; - phandle = <0x1ae>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x35>; - status = "okay"; - phandle = <0xbe>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xe1>; - status = "disabled"; - phandle = <0xc4>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0xe2>; - status = "disabled"; - phandle = <0xca>; - }; - }; - }; - }; - - edp@fdec0000 { - compatible = "rockchip,rk3588-edp"; - reg = <0x00 0xfdec0000 0x00 0x1000>; - interrupts = <0x00 0xa3 0x04>; - clocks = <0x02 0x211 0x02 0x210 0x02 0x212 0x05>; - clock-names = "dp\0pclk\0spdif\0hclk"; - resets = <0x02 0x3e1 0x02 0x3e0>; - reset-names = "dp\0apb"; - phys = <0xe3>; - phy-names = "dp"; - power-domains = <0x4d 0x1a>; - rockchip,grf = <0xba>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0xe4>; - status = "disabled"; - phandle = <0xbd>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0xe5>; - status = "disabled"; - phandle = <0xc3>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x34>; - status = "disabled"; - phandle = <0xc9>; - }; - }; - }; - }; - - qos@fdf35000 { - compatible = "syscon"; - reg = <0x00 0xfdf35000 0x00 0x20>; - phandle = <0x6f>; - }; - - qos@fdf35200 { - compatible = "syscon"; - reg = <0x00 0xfdf35200 0x00 0x20>; - phandle = <0x70>; - }; - - qos@fdf35400 { - compatible = "syscon"; - reg = <0x00 0xfdf35400 0x00 0x20>; - phandle = <0x71>; - }; - - qos@fdf35600 { - compatible = "syscon"; - reg = <0x00 0xfdf35600 0x00 0x20>; - phandle = <0x72>; - }; - - qos@fdf36000 { - compatible = "syscon"; - reg = <0x00 0xfdf36000 0x00 0x20>; - phandle = <0x92>; - }; - - qos@fdf39000 { - compatible = "syscon"; - reg = <0x00 0xfdf39000 0x00 0x20>; - phandle = <0x97>; - }; - - qos@fdf3d800 { - compatible = "syscon"; - reg = <0x00 0xfdf3d800 0x00 0x20>; - phandle = <0x98>; - }; - - qos@fdf3e000 { - compatible = "syscon"; - reg = <0x00 0xfdf3e000 0x00 0x20>; - phandle = <0x94>; - }; - - qos@fdf3e200 { - compatible = "syscon"; - reg = <0x00 0xfdf3e200 0x00 0x20>; - phandle = <0x93>; - }; - - qos@fdf3e400 { - compatible = "syscon"; - reg = <0x00 0xfdf3e400 0x00 0x20>; - phandle = <0x95>; - }; - - qos@fdf3e600 { - compatible = "syscon"; - reg = <0x00 0xfdf3e600 0x00 0x20>; - phandle = <0x96>; - }; - - qos@fdf40000 { - compatible = "syscon"; - reg = <0x00 0xfdf40000 0x00 0x20>; - phandle = <0x90>; - }; - - qos@fdf40200 { - compatible = "syscon"; - reg = <0x00 0xfdf40200 0x00 0x20>; - phandle = <0x91>; - }; - - qos@fdf40400 { - compatible = "syscon"; - reg = <0x00 0xfdf40400 0x00 0x20>; - phandle = <0x8a>; - }; - - qos@fdf40500 { - compatible = "syscon"; - reg = <0x00 0xfdf40500 0x00 0x20>; - phandle = <0x8b>; - }; - - qos@fdf40600 { - compatible = "syscon"; - reg = <0x00 0xfdf40600 0x00 0x20>; - phandle = <0x8c>; - }; - - qos@fdf40800 { - compatible = "syscon"; - reg = <0x00 0xfdf40800 0x00 0x20>; - phandle = <0x8d>; - }; - - qos@fdf41000 { - compatible = "syscon"; - reg = <0x00 0xfdf41000 0x00 0x20>; - phandle = <0x8e>; - }; - - qos@fdf41100 { - compatible = "syscon"; - reg = <0x00 0xfdf41100 0x00 0x20>; - phandle = <0x8f>; - }; - - qos@fdf60000 { - compatible = "syscon"; - reg = <0x00 0xfdf60000 0x00 0x20>; - phandle = <0x75>; - }; - - qos@fdf60200 { - compatible = "syscon"; - reg = <0x00 0xfdf60200 0x00 0x20>; - phandle = <0x76>; - }; - - qos@fdf60400 { - compatible = "syscon"; - reg = <0x00 0xfdf60400 0x00 0x20>; - phandle = <0x77>; - }; - - qos@fdf61000 { - compatible = "syscon"; - reg = <0x00 0xfdf61000 0x00 0x20>; - phandle = <0x78>; - }; - - qos@fdf61200 { - compatible = "syscon"; - reg = <0x00 0xfdf61200 0x00 0x20>; - phandle = <0x79>; - }; - - qos@fdf61400 { - compatible = "syscon"; - reg = <0x00 0xfdf61400 0x00 0x20>; - phandle = <0x7a>; - }; - - qos@fdf62000 { - compatible = "syscon"; - reg = <0x00 0xfdf62000 0x00 0x20>; - phandle = <0x73>; - }; - - qos@fdf63000 { - compatible = "syscon"; - reg = <0x00 0xfdf63000 0x00 0x20>; - phandle = <0x74>; - }; - - qos@fdf64000 { - compatible = "syscon"; - reg = <0x00 0xfdf64000 0x00 0x20>; - phandle = <0x83>; - }; - - qos@fdf66000 { - compatible = "syscon"; - reg = <0x00 0xfdf66000 0x00 0x20>; - phandle = <0x7b>; - }; - - qos@fdf66200 { - compatible = "syscon"; - reg = <0x00 0xfdf66200 0x00 0x20>; - phandle = <0x7c>; - }; - - qos@fdf66400 { - compatible = "syscon"; - reg = <0x00 0xfdf66400 0x00 0x20>; - phandle = <0x7d>; - }; - - qos@fdf66600 { - compatible = "syscon"; - reg = <0x00 0xfdf66600 0x00 0x20>; - phandle = <0x7e>; - }; - - qos@fdf66800 { - compatible = "syscon"; - reg = <0x00 0xfdf66800 0x00 0x20>; - phandle = <0x7f>; - }; - - qos@fdf66a00 { - compatible = "syscon"; - reg = <0x00 0xfdf66a00 0x00 0x20>; - phandle = <0x80>; - }; - - qos@fdf66c00 { - compatible = "syscon"; - reg = <0x00 0xfdf66c00 0x00 0x20>; - phandle = <0x81>; - }; - - qos@fdf66e00 { - compatible = "syscon"; - reg = <0x00 0xfdf66e00 0x00 0x20>; - phandle = <0x82>; - }; - - qos@fdf67000 { - compatible = "syscon"; - reg = <0x00 0xfdf67000 0x00 0x20>; - phandle = <0x84>; - }; - - qos@fdf67200 { - compatible = "syscon"; - reg = <0x00 0xfdf67200 0x00 0x20>; - }; - - qos@fdf70000 { - compatible = "syscon"; - reg = <0x00 0xfdf70000 0x00 0x20>; - phandle = <0x6d>; - }; - - qos@fdf71000 { - compatible = "syscon"; - reg = <0x00 0xfdf71000 0x00 0x20>; - phandle = <0x6e>; - }; - - qos@fdf72000 { - compatible = "syscon"; - reg = <0x00 0xfdf72000 0x00 0x20>; - phandle = <0x6a>; - }; - - qos@fdf72200 { - compatible = "syscon"; - reg = <0x00 0xfdf72200 0x00 0x20>; - phandle = <0x6b>; - }; - - qos@fdf72400 { - compatible = "syscon"; - reg = <0x00 0xfdf72400 0x00 0x20>; - phandle = <0x6c>; - }; - - qos@fdf80000 { - compatible = "syscon"; - reg = <0x00 0xfdf80000 0x00 0x20>; - phandle = <0x87>; - }; - - qos@fdf81000 { - compatible = "syscon"; - reg = <0x00 0xfdf81000 0x00 0x20>; - phandle = <0x88>; - }; - - qos@fdf81200 { - compatible = "syscon"; - reg = <0x00 0xfdf81200 0x00 0x20>; - phandle = <0x89>; - }; - - qos@fdf82000 { - compatible = "syscon"; - reg = <0x00 0xfdf82000 0x00 0x20>; - phandle = <0x85>; - }; - - qos@fdf82200 { - compatible = "syscon"; - reg = <0x00 0xfdf82200 0x00 0x20>; - phandle = <0x86>; - }; - - dfi@fe060000 { - compatible = "rockchip,rk3588-dfi"; - reg = <0x00 0xfe060000 0x00 0x10000>; - rockchip,pmu_grf = <0xe6>; - status = "okay"; - phandle = <0x39>; - }; - - pcie@fe180000 { - compatible = "rockchip,rk3588-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x30 0x3f>; - clocks = <0x02 0x151 0x02 0x156 0x02 0x14c 0x02 0x15c 0x02 0x161 0x02 0x2c5>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux\0pipe"; - device_type = "pci"; - interrupts = <0x00 0xf8 0x04 0x00 0xf7 0x04 0x00 0xf6 0x04 0x00 0xf5 0x04 0x00 0xf4 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xe7 0x00 0x00 0x00 0x00 0x02 0xe7 0x01 0x00 0x00 0x00 0x03 0xe7 0x02 0x00 0x00 0x00 0x04 0xe7 0x03>; - linux,pci-domain = <0x03>; - num-ib-windows = <0x08>; - num-ob-windows = <0x08>; - num-viewport = <0x04>; - max-link-speed = <0x02>; - msi-map = <0x3000 0xe8 0x3000 0x1000>; - num-lanes = <0x01>; - phys = <0x5b 0x02>; - phy-names = "pcie-phy"; - ranges = <0x800 0x00 0xf3000000 0x00 0xf3000000 0x00 0x100000 0x81000000 0x00 0xf3100000 0x00 0xf3100000 0x00 0x100000 0x82000000 0x00 0xf3200000 0x00 0xf3200000 0x00 0xe00000 0xc3000000 0x09 0xc0000000 0x09 0xc0000000 0x00 0x40000000>; - reg = <0x00 0xfe180000 0x00 0x10000 0x0a 0x40c00000 0x00 0x400000>; - reg-names = "pcie-apb\0pcie-dbi"; - resets = <0x02 0x210 0x02 0x21f>; - reset-names = "pcie\0periph"; - rockchip,pipe-grf = <0x61>; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf5 0x01>; - phandle = <0xe7>; - }; - }; - - pcie@fe190000 { - compatible = "rockchip,rk3588-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x40 0x4f>; - clocks = <0x02 0x152 0x02 0x157 0x02 0x14d 0x02 0x15d 0x02 0x162 0x02 0x182>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux\0pipe"; - device_type = "pci"; - interrupts = <0x00 0xfd 0x04 0x00 0xfc 0x04 0x00 0xfb 0x04 0x00 0xfa 0x04 0x00 0xf9 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0xe9 0x00 0x00 0x00 0x00 0x02 0xe9 0x01 0x00 0x00 0x00 0x03 0xe9 0x02 0x00 0x00 0x00 0x04 0xe9 0x03>; - linux,pci-domain = <0x04>; - num-ib-windows = <0x08>; - num-ob-windows = <0x08>; - num-viewport = <0x04>; - max-link-speed = <0x02>; - msi-map = <0x4000 0xe8 0x4000 0x1000>; - num-lanes = <0x01>; - phys = <0xea 0x02>; - phy-names = "pcie-phy"; - ranges = <0x800 0x00 0xf4000000 0x00 0xf4000000 0x00 0x100000 0x81000000 0x00 0xf4100000 0x00 0xf4100000 0x00 0x100000 0x82000000 0x00 0xf4200000 0x00 0xf4200000 0x00 0xe00000 0xc3000000 0x0a 0x00 0x0a 0x00 0x00 0x40000000>; - reg = <0x00 0xfe190000 0x00 0x10000 0x0a 0x41000000 0x00 0x400000>; - reg-names = "pcie-apb\0pcie-dbi"; - resets = <0x02 0x211 0x02 0x220>; - reset-names = "pcie\0periph"; - rockchip,pipe-grf = <0x61>; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xfa 0x01>; - phandle = <0xe9>; - }; - }; - - ethernet@fe1b0000 { - compatible = "rockchip,rk3588-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe1b0000 0x00 0x10000>; - interrupts = <0x00 0xe3 0x04 0x00 0xe2 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0xb7>; - rockchip,php_grf = <0x61>; - local-mac-address = [d2 5e 8a 13 5d df]; - clocks = <0x02 0x144 0x02 0x145 0x02 0x167 0x02 0x16c 0x02 0x142>; - clock-names = "stmmaceth\0clk_mac_ref\0pclk_mac\0aclk_mac\0ptp_ref"; - resets = <0x02 0x20a>; - reset-names = "stmmaceth"; - power-domains = <0x4d 0x21>; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0xeb>; - snps,mtl-rx-config = <0xec>; - snps,mtl-tx-config = <0xed>; - snps,txpbl = <0x04>; - status = "okay"; - phy-mode = "rgmii-rxid"; - clock_in_out = "output"; - snps,reset-gpio = <0xee 0x17 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - pinctrl-names = "default"; - pinctrl-0 = <0xef 0xf0 0xf1 0xf2 0xf3>; - tx_delay = <0x45>; - phy-handle = <0xf4>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x01>; - phandle = <0xf4>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0xeb>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x02>; - phandle = <0xec>; - - queue0 { - }; - - queue1 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x02>; - phandle = <0xed>; - - queue0 { - }; - - queue1 { - }; - }; - }; - - ethernet@fe1c0000 { - compatible = "rockchip,rk3588-gmac\0snps,dwmac-4.20a"; - reg = <0x00 0xfe1c0000 0x00 0x10000>; - interrupts = <0x00 0xea 0x04 0x00 0xe9 0x04>; - interrupt-names = "macirq\0eth_wake_irq"; - rockchip,grf = <0xb7>; - rockchip,php_grf = <0x61>; - local-mac-address = [d6 5e 8a 13 5d df]; - clocks = <0x02 0x144 0x02 0x145 0x02 0x168 0x02 0x16d 0x02 0x143>; - clock-names = "stmmaceth\0clk_mac_ref\0pclk_mac\0aclk_mac\0ptp_ref"; - resets = <0x02 0x20b>; - reset-names = "stmmaceth"; - power-domains = <0x4d 0x21>; - snps,mixed-burst; - snps,tso; - snps,axi-config = <0xf5>; - snps,mtl-rx-config = <0xf6>; - snps,mtl-tx-config = <0xf7>; - snps,txpbl = <0x04>; - status = "okay"; - phy-mode = "rgmii-rxid"; - clock_in_out = "output"; - snps,reset-gpio = <0xee 0x0f 0x01>; - snps,reset-active-low; - snps,reset-delays-us = <0x00 0x4e20 0x186a0>; - pinctrl-names = "default"; - pinctrl-0 = <0xf8 0xf9 0xfa 0xfb 0xfc>; - tx_delay = <0x42>; - phy-handle = <0xfd>; - - mdio { - compatible = "snps,dwmac-mdio"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - phy@1 { - compatible = "ethernet-phy-ieee802.3-c22"; - reg = <0x01>; - phandle = <0xfd>; - }; - }; - - stmmac-axi-config { - snps,wr_osr_lmt = <0x04>; - snps,rd_osr_lmt = <0x08>; - snps,blen = <0x00 0x00 0x00 0x00 0x10 0x08 0x04>; - phandle = <0xf5>; - }; - - rx-queues-config { - snps,rx-queues-to-use = <0x02>; - phandle = <0xf6>; - - queue0 { - }; - - queue1 { - }; - }; - - tx-queues-config { - snps,tx-queues-to-use = <0x02>; - phandle = <0xf7>; - - queue0 { - }; - - queue1 { - }; - }; - }; - - sata@fe210000 { - compatible = "rockchip,rk-ahci\0snps,dwc-ahci"; - reg = <0x00 0xfe210000 0x00 0x1000>; - clocks = <0x02 0x171 0x02 0x16e 0x02 0x174 0x02 0x163 0x02 0x17e>; - clock-names = "sata\0pmalive\0rxoob\0ref\0asic"; - interrupts = <0x00 0x111 0x04>; - interrupt-names = "hostc"; - phys = <0xea 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - status = "disabled"; - }; - - sata@fe230000 { - compatible = "rockchip,rk-ahci\0snps,dwc-ahci"; - reg = <0x00 0xfe230000 0x00 0x1000>; - clocks = <0x02 0x173 0x02 0x170 0x02 0x176 0x02 0x165 0x02 0x180>; - clock-names = "sata\0pmalive\0rxoob\0ref\0asic"; - interrupts = <0x00 0x113 0x04>; - interrupt-names = "hostc"; - phys = <0x5b 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - status = "disabled"; - }; - - spi@fe2b0000 { - compatible = "rockchip,sfc"; - reg = <0x00 0xfe2b0000 0x00 0x4000>; - interrupts = <0x00 0xce 0x04>; - clocks = <0x02 0x13d 0x02 0x13e>; - clock-names = "clk_sfc\0hclk_sfc"; - assigned-clocks = <0x02 0x13d>; - assigned-clock-rates = <0x5f5e100>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - mmc@fe2c0000 { - compatible = "rockchip,rk3588-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2c0000 0x00 0x4000>; - interrupts = <0x00 0xcb 0x04>; - clocks = <0x0e 0x17 0x0e 0x09 0x02 0x2c2 0x02 0x2c3>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - max-frequency = <0x8f0d180>; - pinctrl-names = "default"; - pinctrl-0 = <0xfe 0xff 0x100 0x101>; - power-domains = <0x4d 0x28>; - status = "okay"; - no-sdio; - no-mmc; - bus-width = <0x04>; - cap-mmc-highspeed; - cap-sd-highspeed; - disable-wp; - sd-uhs-sdr104; - vqmmc-supply = <0x102>; - }; - - mmc@fe2d0000 { - compatible = "rockchip,rk3588-dw-mshc\0rockchip,rk3288-dw-mshc"; - reg = <0x00 0xfe2d0000 0x00 0x4000>; - interrupts = <0x00 0xcc 0x04>; - clocks = <0x02 0x199 0x02 0x19a 0x02 0x2c0 0x02 0x2c1>; - clock-names = "biu\0ciu\0ciu-drive\0ciu-sample"; - fifo-depth = <0x100>; - max-frequency = <0xbebc200>; - pinctrl-names = "default"; - pinctrl-0 = <0x103>; - power-domains = <0x4d 0x25>; - status = "disabled"; - }; - - mmc@fe2e0000 { - compatible = "rockchip,rk3588-dwcmshc\0rockchip,dwcmshc-sdhci"; - reg = <0x00 0xfe2e0000 0x00 0x10000>; - interrupts = <0x00 0xcd 0x04>; - assigned-clocks = <0x02 0x13b 0x02 0x13c 0x02 0x13a>; - assigned-clock-rates = <0xbebc200 0x16e3600 0xbebc200>; - clocks = <0x02 0x13a 0x02 0x138 0x02 0x139 0x02 0x13b 0x02 0x13c>; - clock-names = "core\0bus\0axi\0block\0timer"; - resets = <0x02 0x1f6 0x02 0x1f4 0x02 0x1f5 0x02 0x1f7 0x02 0x1f8>; - reset-names = "core\0bus\0axi\0block\0timer"; - max-frequency = <0xbebc200>; - status = "okay"; - bus-width = <0x08>; - no-sdio; - no-sd; - non-removable; - mmc-hs400-1_8v; - mmc-hs400-enhanced-strobe; - }; - - crypto@fe370000 { - compatible = "rockchip,rk3588-crypto"; - reg = <0x00 0xfe370000 0x00 0x2000>; - interrupts = <0x00 0xd1 0x04>; - clocks = <0x0e 0x0b 0x0e 0x0c 0x0e 0x14 0x0e 0x15>; - clock-names = "aclk\0hclk\0sclk\0pka"; - resets = <0x104 0x0f>; - reset-names = "crypto-rst"; - status = "disabled"; - }; - - rng@fe378000 { - compatible = "rockchip,trngv1"; - reg = <0x00 0xfe378000 0x00 0x200>; - interrupts = <0x00 0x190 0x04>; - clocks = <0x0e 0x0c>; - clock-names = "hclk_trng"; - resets = <0x104 0x30>; - reset-names = "reset"; - status = "okay"; - }; - - i2s@fe470000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfe470000 0x00 0x1000>; - interrupts = <0x00 0xb4 0x04>; - clocks = <0x02 0x33 0x02 0x37 0x02 0x30>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x31 0x02 0x35>; - assigned-clock-parents = <0x02 0x05 0x02 0x05>; - dmas = <0x64 0x00 0x64 0x01>; - dma-names = "tx\0rx"; - power-domains = <0x4d 0x26>; - resets = <0x02 0x77 0x02 0x7a>; - reset-names = "tx-m\0rx-m"; - rockchip,clk-trcm = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x105 0x106 0x107 0x108>; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x1b4>; - }; - - i2s@fe480000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfe480000 0x00 0x1000>; - interrupts = <0x00 0xb5 0x04>; - clocks = <0x02 0x28c 0x02 0x290 0x02 0x288>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - dmas = <0x64 0x02 0x64 0x03>; - dma-names = "tx\0rx"; - resets = <0x02 0xc002a 0x02 0xc002d>; - reset-names = "tx-m\0rx-m"; - rockchip,clk-trcm = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x109 0x10a 0x10b 0x10c 0x10d 0x10e 0x10f 0x110 0x111 0x112>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - i2s@fe490000 { - compatible = "rockchip,rk3588-i2s\0rockchip,rk3066-i2s"; - reg = <0x00 0xfe490000 0x00 0x1000>; - interrupts = <0x00 0xb6 0x04>; - clocks = <0x02 0x27 0x02 0x22>; - clock-names = "i2s_clk\0i2s_hclk"; - assigned-clocks = <0x02 0x24>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd3 0x00 0xd3 0x01>; - dma-names = "tx\0rx"; - power-domains = <0x4d 0x26>; - rockchip,clk-trcm = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x113 0x114 0x115 0x116>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - i2s@fe4a0000 { - compatible = "rockchip,rk3588-i2s\0rockchip,rk3066-i2s"; - reg = <0x00 0xfe4a0000 0x00 0x1000>; - interrupts = <0x00 0xb7 0x04>; - clocks = <0x02 0x2d 0x02 0x23>; - clock-names = "i2s_clk\0i2s_hclk"; - assigned-clocks = <0x02 0x2a>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd3 0x02 0xd3 0x03>; - dma-names = "tx\0rx"; - power-domains = <0x4d 0x26>; - rockchip,clk-trcm = <0x01>; - pinctrl-names = "default"; - pinctrl-0 = <0x117 0x118 0x119 0x11a>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - pdm@fe4b0000 { - compatible = "rockchip,rk3588-pdm"; - reg = <0x00 0xfe4b0000 0x00 0x1000>; - clocks = <0x02 0x29f 0x02 0x29e>; - clock-names = "pdm_clk\0pdm_hclk"; - dmas = <0x64 0x04>; - dma-names = "rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x11b 0x11c 0x11d 0x11e 0x11f 0x120>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - pdm@fe4c0000 { - compatible = "rockchip,rk3588-pdm"; - reg = <0x00 0xfe4c0000 0x00 0x1000>; - clocks = <0x02 0x3b 0x02 0x3a>; - clock-names = "pdm_clk\0pdm_hclk"; - assigned-clocks = <0x02 0x3b>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd3 0x04>; - dma-names = "rx"; - power-domains = <0x4d 0x26>; - pinctrl-names = "default"; - pinctrl-0 = <0x121 0x122 0x123 0x124 0x125 0x126>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - vad@fe4d0000 { - compatible = "rockchip,rk3588-vad"; - reg = <0x00 0xfe4d0000 0x00 0x1000>; - reg-names = "vad"; - clocks = <0x02 0x2a0>; - clock-names = "hclk"; - interrupts = <0x00 0xca 0x04>; - rockchip,audio-src = <0x00>; - rockchip,det-channel = <0x00>; - rockchip,mode = <0x00>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-tx@fe4e0000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfe4e0000 0x00 0x1000>; - interrupts = <0x00 0xc1 0x04>; - dmas = <0x64 0x05>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x41 0x02 0x3e>; - assigned-clocks = <0x02 0x3f>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x26>; - pinctrl-names = "default"; - pinctrl-0 = <0x127>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-tx@fe4f0000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfe4f0000 0x00 0x1000>; - interrupts = <0x00 0xc2 0x04>; - dmas = <0xd3 0x05>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x47 0x02 0x44>; - assigned-clocks = <0x02 0x45>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x26>; - pinctrl-names = "default"; - pinctrl-0 = <0x128>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x1b1>; - }; - - codec-digital@fe500000 { - compatible = "rockchip,rk3588-codec-digital\0rockchip,codec-digital-v1"; - reg = <0x00 0xfe500000 0x00 0x1000>; - clocks = <0x02 0x29 0x02 0x2f>; - clock-names = "dac\0pclk"; - power-domains = <0x4d 0x26>; - resets = <0x02 0x84>; - reset-names = "reset"; - rockchip,grf = <0xb7>; - rockchip,pwm-output-mode; - pinctrl-names = "default"; - pinctrl-0 = <0x129>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - hwspinlock@fe5a0000 { - compatible = "rockchip,hwspinlock"; - reg = <0x00 0xfe5a0000 0x00 0x100>; - #hwlock-cells = <0x01>; - }; - - interrupt-controller@fe600000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0xfe600000 0x00 0x10000 0x00 0xfe680000 0x00 0x100000>; - interrupts = <0x01 0x09 0x04>; - phandle = <0x01>; - }; - - dma-controller@fea10000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfea10000 0x00 0x4000>; - interrupts = <0x00 0x56 0x04 0x00 0x57 0x04>; - clocks = <0x02 0x78>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0x64>; - }; - - dma-controller@fea30000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfea30000 0x00 0x4000>; - interrupts = <0x00 0x58 0x04 0x00 0x59 0x04>; - clocks = <0x02 0x79>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0xd3>; - }; - - can@fea50000 { - compatible = "rockchip,can-2.0"; - reg = <0x00 0xfea50000 0x00 0x1000>; - interrupts = <0x00 0x155 0x04>; - clocks = <0x02 0x70 0x02 0x6f>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x02 0xb9 0x02 0xb8>; - reset-names = "can\0can-apb"; - pinctrl-names = "default"; - pinctrl-0 = <0x12a>; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - }; - - can@fea60000 { - compatible = "rockchip,can-2.0"; - reg = <0x00 0xfea60000 0x00 0x1000>; - interrupts = <0x00 0x156 0x04>; - clocks = <0x02 0x72 0x02 0x71>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x02 0xbb 0x02 0xba>; - reset-names = "can\0can-apb"; - pinctrl-names = "default"; - pinctrl-0 = <0x12b>; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "okay"; - assigned-clocks = <0x02 0x72>; - assigned-clock-rates = <0xbebc200>; - }; - - can@fea70000 { - compatible = "rockchip,can-2.0"; - reg = <0x00 0xfea70000 0x00 0x1000>; - interrupts = <0x00 0x157 0x04>; - clocks = <0x02 0x74 0x02 0x73>; - clock-names = "baudclk\0apb_pclk"; - resets = <0x02 0xbd 0x02 0xbc>; - reset-names = "can\0can-apb"; - pinctrl-names = "default"; - pinctrl-0 = <0x12c>; - tx-fifo-depth = <0x01>; - rx-fifo-depth = <0x06>; - status = "disabled"; - }; - - decompress@fea80000 { - compatible = "rockchip,hw-decompress"; - reg = <0x00 0xfea80000 0x00 0x1000>; - interrupts = <0x00 0x55 0x04>; - clocks = <0x02 0x75 0x02 0x77 0x02 0x76>; - clock-names = "aclk\0dclk\0pclk"; - resets = <0x02 0x118>; - reset-names = "dresetn"; - status = "disabled"; - }; - - i2c@fea90000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfea90000 0x00 0x1000>; - clocks = <0x02 0x8d 0x02 0x85>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x13e 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x12d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - rk8602@42 { - compatible = "rockchip,rk8602"; - reg = <0x42>; - vin-supply = <0x63>; - regulator-compatible = "rk860x-reg"; - regulator-name = "vdd_npu_s0"; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0xe7ef0>; - regulator-ramp-delay = <0x8fc>; - rockchip,suspend-voltage-selector = <0x01>; - regulator-boot-on; - regulator-always-on; - phandle = <0x9b>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - - i2c@feaa0000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfeaa0000 0x00 0x1000>; - clocks = <0x02 0x8e 0x02 0x86>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x13f 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x12e>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@feab0000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfeab0000 0x00 0x1000>; - clocks = <0x02 0x8f 0x02 0x87>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x140 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x12f>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - - es8388@11 { - status = "disabled"; - #sound-dai-cells = <0x00>; - compatible = "everest,es8388\0everest,es8323"; - reg = <0x11>; - clocks = <0x02 0x39>; - clock-names = "mclk"; - assigned-clocks = <0x02 0x39>; - assigned-clock-rates = <0xbb8000>; - pinctrl-names = "default"; - pinctrl-0 = <0x130>; - phandle = <0x1b5>; - }; - }; - - i2c@feac0000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfeac0000 0x00 0x1000>; - clocks = <0x02 0x90 0x02 0x88>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x141 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x131>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@fead0000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfead0000 0x00 0x1000>; - clocks = <0x02 0x91 0x02 0x89>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x142 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x132>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - timer@feae0000 { - compatible = "rockchip,rk3588-timer\0rockchip,rk3288-timer"; - reg = <0x00 0xfeae0000 0x00 0x20>; - interrupts = <0x00 0x121 0x04>; - clocks = <0x02 0x5c 0x02 0x5f>; - clock-names = "pclk\0timer"; - }; - - watchdog@feaf0000 { - compatible = "snps,dw-wdt"; - reg = <0x00 0xfeaf0000 0x00 0x100>; - clocks = <0x02 0x6c 0x02 0x6b>; - clock-names = "tclk\0pclk"; - interrupts = <0x00 0x13b 0x04>; - status = "okay"; - }; - - spi@feb00000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfeb00000 0x00 0x1000>; - interrupts = <0x00 0x146 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0xa3 0x02 0x9e>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x64 0x0e 0x64 0x0f>; - dma-names = "tx\0rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x133 0x134 0x135>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@feb10000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfeb10000 0x00 0x1000>; - interrupts = <0x00 0x147 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0xa4 0x02 0x9f>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0x64 0x10 0x64 0x11>; - dma-names = "tx\0rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x136 0x137 0x138>; - num-cs = <0x02>; - status = "disabled"; - }; - - spi@feb20000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfeb20000 0x00 0x1000>; - interrupts = <0x00 0x148 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0xa5 0x02 0xa0>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0xd3 0x0f 0xd3 0x10>; - dma-names = "tx\0rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x139 0x13a>; - num-cs = <0x01>; - status = "okay"; - assigned-clocks = <0x02 0xa5>; - assigned-clock-rates = <0xbebc200>; - - rk806single@0 { - compatible = "rockchip,rk806"; - spi-max-frequency = <0xf4240>; - reg = <0x00>; - interrupt-parent = <0x13b>; - interrupts = <0x07 0x08>; - pinctrl-names = "default\0pmic-power-off"; - pinctrl-0 = <0x13c 0x13d 0x13e 0x13f>; - pinctrl-1 = <0x140>; - low_voltage_threshold = <0xbb8>; - shutdown_voltage_threshold = <0xa8c>; - shutdown_temperture_threshold = <0xa0>; - hotdie_temperture_threshold = <0x73>; - pmic-reset-func = <0x01>; - vcc1-supply = <0x63>; - vcc2-supply = <0x63>; - vcc3-supply = <0x63>; - vcc4-supply = <0x63>; - vcc5-supply = <0x63>; - vcc6-supply = <0x63>; - vcc7-supply = <0x63>; - vcc8-supply = <0x63>; - vcc9-supply = <0x63>; - vcc10-supply = <0x63>; - vcc11-supply = <0x141>; - vcc12-supply = <0x63>; - vcc13-supply = <0x142>; - vcc14-supply = <0x142>; - vcca-supply = <0x63>; - - pwrkey { - status = "okay"; - }; - - pinctrl_rk806 { - gpio-controller; - #gpio-cells = <0x02>; - - rk806_dvs1_null { - pins = "gpio_pwrctrl2"; - function = "pin_fun0"; - phandle = <0x13d>; - }; - - rk806_dvs1_slp { - pins = "gpio_pwrctrl1"; - function = "pin_fun1"; - }; - - rk806_dvs1_pwrdn { - pins = "gpio_pwrctrl1"; - function = "pin_fun2"; - phandle = <0x140>; - }; - - rk806_dvs1_rst { - pins = "gpio_pwrctrl1"; - function = "pin_fun3"; - }; - - rk806_dvs2_null { - pins = "gpio_pwrctrl2"; - function = "pin_fun0"; - phandle = <0x13e>; - }; - - rk806_dvs2_slp { - pins = "gpio_pwrctrl2"; - function = "pin_fun1"; - }; - - rk806_dvs2_pwrdn { - pins = "gpio_pwrctrl2"; - function = "pin_fun2"; - }; - - rk806_dvs2_rst { - pins = "gpio_pwrctrl2"; - function = "pin_fun3"; - }; - - rk806_dvs2_dvs { - pins = "gpio_pwrctrl2"; - function = "pin_fun4"; - }; - - rk806_dvs2_gpio { - pins = "gpio_pwrctrl2"; - function = "pin_fun5"; - }; - - rk806_dvs3_null { - pins = "gpio_pwrctrl3"; - function = "pin_fun0"; - phandle = <0x13f>; - }; - - rk806_dvs3_slp { - pins = "gpio_pwrctrl3"; - function = "pin_fun1"; - }; - - rk806_dvs3_pwrdn { - pins = "gpio_pwrctrl3"; - function = "pin_fun2"; - }; - - rk806_dvs3_rst { - pins = "gpio_pwrctrl3"; - function = "pin_fun3"; - }; - - rk806_dvs3_dvs { - pins = "gpio_pwrctrl3"; - function = "pin_fun4"; - }; - - rk806_dvs3_gpio { - pins = "gpio_pwrctrl3"; - function = "pin_fun5"; - }; - }; - - regulators { - - DCDC_REG1 { - regulator-boot-on; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0xe7ef0>; - regulator-ramp-delay = <0x30d4>; - regulator-name = "vdd_gpu_s0"; - regulator-enable-ramp-delay = <0x190>; - phandle = <0x4f>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0xe7ef0>; - regulator-ramp-delay = <0x30d4>; - regulator-name = "vdd_cpu_lit_s0"; - phandle = <0x12>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xa4cb8>; - regulator-max-microvolt = <0xb71b0>; - regulator-ramp-delay = <0x30d4>; - regulator-name = "vdd_log_s0"; - phandle = <0x3c>; - - regulator-state-mem { - regulator-suspend-microvolt = <0xb71b0>; - regulator-on-in-suspend; - }; - }; - - DCDC_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x86470>; - regulator-max-microvolt = <0xe7ef0>; - regulator-init-microvolt = <0xb71b0>; - regulator-ramp-delay = <0x30d4>; - regulator-name = "vdd_vdenc_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xa4cb8>; - regulator-max-microvolt = <0xdbba0>; - regulator-ramp-delay = <0x30d4>; - regulator-name = "vdd_ddr_s0"; - phandle = <0x3b>; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <0xcf850>; - }; - }; - - DCDC_REG6 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vdd2_ddr_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - }; - }; - - DCDC_REG7 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1e8480>; - regulator-max-microvolt = <0x1e8480>; - regulator-name = "vdd_2v0_pldo_s3"; - phandle = <0x141>; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x1e8480>; - }; - }; - - DCDC_REG8 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vcc_3v3_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x325aa0>; - }; - }; - - DCDC_REG9 { - regulator-always-on; - regulator-boot-on; - regulator-name = "vddq_ddr_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - DCDC_REG10 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcc_1v8_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x1b7740>; - }; - }; - - PLDO_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "avcc_1v8_s0"; - phandle = <0x1ba>; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-on-in-suspend; - }; - }; - - PLDO_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "vcc_1v8_s0"; - phandle = <0x15e>; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <0x1b7740>; - regulator-on-in-suspend; - }; - }; - - PLDO_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x124f80>; - regulator-max-microvolt = <0x124f80>; - regulator-name = "avdd_1v2_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - PLDO_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vcc_3v3_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - PLDO_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x325aa0>; - regulator-name = "vccio_sd_s0"; - phandle = <0x102>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - PLDO_REG6 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - regulator-name = "pldo6_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0x1b7740>; - }; - }; - - NLDO_REG1 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b0>; - regulator-max-microvolt = <0xb71b0>; - regulator-name = "vdd_0v75_s3"; - - regulator-state-mem { - regulator-on-in-suspend; - regulator-suspend-microvolt = <0xb71b0>; - }; - }; - - NLDO_REG2 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xcf850>; - regulator-max-microvolt = <0xcf850>; - regulator-name = "vdd_ddr_pll_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - regulator-suspend-microvolt = <0xcf850>; - }; - }; - - NLDO_REG3 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b0>; - regulator-max-microvolt = <0xb71b0>; - regulator-name = "avdd_0v75_s0"; - phandle = <0x1bb>; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - NLDO_REG4 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xcf850>; - regulator-max-microvolt = <0xcf850>; - regulator-name = "vdd_0v85_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - - NLDO_REG5 { - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b0>; - regulator-max-microvolt = <0xb71b0>; - regulator-name = "vdd_0v75_s0"; - - regulator-state-mem { - regulator-off-in-suspend; - }; - }; - }; - }; - }; - - spi@feb30000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfeb30000 0x00 0x1000>; - interrupts = <0x00 0x149 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0xa6 0x02 0xa1>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0xd3 0x11 0xd3 0x12>; - dma-names = "tx\0rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x143 0x144 0x145>; - num-cs = <0x02>; - status = "disabled"; - }; - - serial@feb40000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb40000 0x00 0x100>; - interrupts = <0x00 0x14c 0x04>; - clocks = <0x02 0xb7 0x02 0xab>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x64 0x08 0x64 0x09>; - pinctrl-names = "default"; - pinctrl-0 = <0x146>; - status = "disabled"; - }; - - serial@feb50000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb50000 0x00 0x100>; - interrupts = <0x00 0x14d 0x04>; - clocks = <0x02 0xbb 0x02 0xac>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x64 0x0a 0x64 0x0b>; - pinctrl-names = "default"; - pinctrl-0 = <0x147>; - status = "disabled"; - }; - - serial@feb60000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb60000 0x00 0x100>; - interrupts = <0x00 0x14e 0x04>; - clocks = <0x02 0xbf 0x02 0xad>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0x64 0x0c 0x64 0x0d>; - pinctrl-names = "default"; - pinctrl-0 = <0x148>; - status = "disabled"; - }; - - serial@feb70000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb70000 0x00 0x100>; - interrupts = <0x00 0x14f 0x04>; - clocks = <0x02 0xc3 0x02 0xae>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd3 0x09 0xd3 0x0a>; - pinctrl-names = "default"; - pinctrl-0 = <0x149>; - status = "disabled"; - }; - - serial@feb80000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb80000 0x00 0x100>; - interrupts = <0x00 0x150 0x04>; - clocks = <0x02 0xc7 0x02 0xaf>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd3 0x0b 0xd3 0x0c>; - pinctrl-names = "default"; - pinctrl-0 = <0x14a>; - status = "disabled"; - }; - - serial@feb90000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeb90000 0x00 0x100>; - interrupts = <0x00 0x151 0x04>; - clocks = <0x02 0xcb 0x02 0xb0>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd3 0x0d 0xd3 0x0e>; - pinctrl-names = "default"; - pinctrl-0 = <0x14b 0x14c>; - status = "okay"; - }; - - serial@feba0000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfeba0000 0x00 0x100>; - interrupts = <0x00 0x152 0x04>; - clocks = <0x02 0xcf 0x02 0xb1>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd4 0x07 0xd4 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0x14d>; - status = "disabled"; - }; - - serial@febb0000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfebb0000 0x00 0x100>; - interrupts = <0x00 0x153 0x04>; - clocks = <0x02 0xd3 0x02 0xb2>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd4 0x09 0xd4 0x0a>; - pinctrl-names = "default"; - pinctrl-0 = <0x14e>; - status = "disabled"; - }; - - serial@febc0000 { - compatible = "rockchip,rk3588-uart\0snps,dw-apb-uart"; - reg = <0x00 0xfebc0000 0x00 0x100>; - interrupts = <0x00 0x154 0x04>; - clocks = <0x02 0xd7 0x02 0xb3>; - clock-names = "baudclk\0apb_pclk"; - reg-shift = <0x02>; - reg-io-width = <0x04>; - dmas = <0xd4 0x0b 0xd4 0x0c>; - pinctrl-names = "default"; - pinctrl-0 = <0x14f>; - status = "disabled"; - }; - - pwm@febd0000 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebd0000 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x150>; - clocks = <0x02 0x54 0x02 0x53>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febd0010 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebd0010 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x151>; - clocks = <0x02 0x54 0x02 0x53>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febd0020 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebd0020 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x152>; - clocks = <0x02 0x54 0x02 0x53>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febd0030 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebd0030 0x00 0x10>; - interrupts = <0x00 0x15a 0x04 0x00 0x15b 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x153>; - clocks = <0x02 0x54 0x02 0x53>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febe0000 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebe0000 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x154>; - clocks = <0x02 0x57 0x02 0x56>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febe0010 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebe0010 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x155>; - clocks = <0x02 0x57 0x02 0x56>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febe0020 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebe0020 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x156>; - clocks = <0x02 0x57 0x02 0x56>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febe0030 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebe0030 0x00 0x10>; - interrupts = <0x00 0x15c 0x04 0x00 0x15d 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x157>; - clocks = <0x02 0x57 0x02 0x56>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febf0000 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebf0000 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x158>; - clocks = <0x02 0x5a 0x02 0x59>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febf0010 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebf0010 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x159>; - clocks = <0x02 0x5a 0x02 0x59>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febf0020 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebf0020 0x00 0x10>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x15a>; - clocks = <0x02 0x5a 0x02 0x59>; - clock-names = "pwm\0pclk"; - status = "disabled"; - }; - - pwm@febf0030 { - compatible = "rockchip,rk3588-pwm\0rockchip,rk3328-pwm"; - reg = <0x00 0xfebf0030 0x00 0x10>; - interrupts = <0x00 0x15e 0x04 0x00 0x15f 0x04>; - #pwm-cells = <0x03>; - pinctrl-names = "active"; - pinctrl-0 = <0x15b>; - clocks = <0x02 0x5a 0x02 0x59>; - clock-names = "pwm\0pclk"; - status = "okay"; - phandle = <0x1ca>; - }; - - tsadc@fec00000 { - compatible = "rockchip,rk3588-tsadc"; - reg = <0x00 0xfec00000 0x00 0x400>; - interrupts = <0x00 0x18d 0x04>; - clocks = <0x02 0xaa 0x02 0xa9>; - clock-names = "tsadc\0apb_pclk"; - assigned-clocks = <0x02 0xaa>; - assigned-clock-rates = <0x1e8480>; - resets = <0x02 0xc1 0x02 0xc0>; - reset-names = "tsadc\0tsadc-apb"; - #thermal-sensor-cells = <0x01>; - rockchip,hw-tshut-temp = <0x1d4c0>; - rockchip,hw-tshut-mode = <0x00>; - rockchip,hw-tshut-polarity = <0x00>; - pinctrl-names = "gpio\0otpout"; - pinctrl-0 = <0x15c>; - pinctrl-1 = <0x15d>; - status = "okay"; - phandle = <0x4a>; - }; - - saradc@fec10000 { - compatible = "rockchip,rk3588-saradc"; - reg = <0x00 0xfec10000 0x00 0x10000>; - interrupts = <0x00 0x18e 0x04>; - #io-channel-cells = <0x01>; - clocks = <0x02 0x9d 0x02 0x9c>; - clock-names = "saradc\0apb_pclk"; - resets = <0x02 0xbe>; - reset-names = "saradc-apb"; - status = "okay"; - vref-supply = <0x15e>; - phandle = <0x1b3>; - }; - - mailbox@fec60000 { - compatible = "rockchip,rk3588-mailbox\0rockchip,rk3368-mailbox"; - reg = <0x00 0xfec60000 0x00 0x200>; - interrupts = <0x00 0x3d 0x04 0x00 0x3e 0x04 0x00 0x3f 0x04 0x00 0x40 0x04>; - clocks = <0x02 0x4c>; - clock-names = "pclk_mailbox"; - #mbox-cells = <0x01>; - status = "disabled"; - }; - - mailbox@fec70000 { - compatible = "rockchip,rk3588-mailbox\0rockchip,rk3368-mailbox"; - reg = <0x00 0xfec70000 0x00 0x200>; - interrupts = <0x00 0x45 0x04 0x00 0x46 0x04 0x00 0x47 0x04 0x00 0x48 0x04>; - clocks = <0x02 0x4d>; - clock-names = "pclk_mailbox"; - #mbox-cells = <0x01>; - status = "disabled"; - }; - - i2c@fec80000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfec80000 0x00 0x1000>; - clocks = <0x02 0x92 0x02 0x8a>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x143 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x15f>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - clock-frequency = <0x61a80>; - - gpio@21 { - compatible = "nxp,pca9555"; - reg = <0x21>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-group-num = <0xc8>; - status = "disabled"; - phandle = <0x1b8>; - }; - - hym8563@51 { - compatible = "haoyu,hym8563"; - reg = <0x51>; - #clock-cells = <0x00>; - clock-frequency = <0x8000>; - clock-output-names = "hym8563"; - pinctrl-names = "default"; - pinctrl-0 = <0x160>; - interrupt-parent = <0x13b>; - interrupts = <0x08 0x08>; - wakeup-source; - status = "disabled"; - phandle = <0x1c0>; - }; - - fusb302@22 { - compatible = "fcs,fusb302"; - reg = <0x22>; - interrupt-parent = <0x13b>; - interrupts = <0x1b 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0x161>; - vbus-supply = <0x162>; - status = "okay"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint@0 { - remote-endpoint = <0x163>; - phandle = <0x54>; - }; - }; - }; - - connector { - compatible = "usb-c-connector"; - label = "USB-C"; - data-role = "dual"; - power-role = "dual"; - try-power-role = "sink"; - op-sink-microwatt = <0xf4240>; - sink-pdos = <0x4019064>; - source-pdos = <0x401912c>; - - altmodes { - #address-cells = <0x01>; - #size-cells = <0x00>; - - altmode@0 { - reg = <0x00>; - svid = <0xff01>; - vdo = <0xffffffff>; - }; - }; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - - endpoint { - remote-endpoint = <0x164>; - phandle = <0x16f>; - }; - }; - - port@1 { - reg = <0x01>; - - endpoint { - remote-endpoint = <0x165>; - phandle = <0x170>; - }; - }; - }; - }; - }; - }; - - i2c@fec90000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfec90000 0x00 0x1000>; - clocks = <0x02 0x93 0x02 0x8b>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x144 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x166>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - i2c@feca0000 { - compatible = "rockchip,rk3588-i2c\0rockchip,rk3399-i2c"; - reg = <0x00 0xfeca0000 0x00 0x1000>; - clocks = <0x02 0x94 0x02 0x8c>; - clock-names = "i2c\0pclk"; - interrupts = <0x00 0x145 0x04>; - pinctrl-names = "default"; - pinctrl-0 = <0x167>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@fecb0000 { - compatible = "rockchip,rk3066-spi"; - reg = <0x00 0xfecb0000 0x00 0x1000>; - interrupts = <0x00 0x14a 0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - clocks = <0x02 0xa7 0x02 0xa2>; - clock-names = "spiclk\0apb_pclk"; - dmas = <0xd4 0x0d 0xd4 0x0e>; - dma-names = "tx\0rx"; - pinctrl-names = "default"; - pinctrl-0 = <0x168 0x169 0x16a>; - num-cs = <0x02>; - status = "disabled"; - }; - - otp@fecc0000 { - compatible = "rockchip,rk3588-otp"; - reg = <0x00 0xfecc0000 0x00 0x400>; - #address-cells = <0x01>; - #size-cells = <0x01>; - clocks = <0x02 0x96 0x02 0x95 0x02 0x97 0x02 0x99>; - clock-names = "otpc\0apb\0arb\0phy"; - resets = <0x02 0x12a 0x02 0x129 0x02 0x12b>; - reset-names = "otpc\0apb\0arb"; - - cpu-code@2 { - reg = <0x02 0x02>; - phandle = <0x29>; - }; - - specification-serial-number@6 { - reg = <0x06 0x01>; - bits = <0x00 0x05>; - phandle = <0x20>; - }; - - id@7 { - reg = <0x07 0x10>; - phandle = <0x27>; - }; - - cpu-version@1c { - reg = <0x1c 0x01>; - bits = <0x03 0x03>; - phandle = <0x28>; - }; - - cpub0-leakage@17 { - reg = <0x17 0x01>; - phandle = <0x23>; - }; - - cpub1-leakage@18 { - reg = <0x18 0x01>; - phandle = <0x25>; - }; - - cpul-leakage@19 { - reg = <0x19 0x01>; - phandle = <0x1f>; - }; - - log-leakage@1a { - reg = <0x1a 0x01>; - phandle = <0x3d>; - }; - - gpu-leakage@1b { - reg = <0x1b 0x01>; - phandle = <0x50>; - }; - - npu-leakage@28 { - reg = <0x28 0x01>; - phandle = <0x9c>; - }; - - codec-leakage@29 { - reg = <0x29 0x01>; - }; - }; - - mailbox@fece0000 { - compatible = "rockchip,rk3588-mailbox\0rockchip,rk3368-mailbox"; - reg = <0x00 0xfece0000 0x00 0x200>; - interrupts = <0x00 0x4d 0x04 0x00 0x4e 0x04 0x00 0x4f 0x04 0x00 0x50 0x04>; - clocks = <0x02 0x4e>; - clock-names = "pclk_mailbox"; - #mbox-cells = <0x01>; - status = "disabled"; - }; - - dma-controller@fed10000 { - compatible = "arm,pl330\0arm,primecell"; - reg = <0x00 0xfed10000 0x00 0x4000>; - interrupts = <0x00 0x5a 0x04 0x00 0x5b 0x04>; - clocks = <0x02 0x7a>; - clock-names = "apb_pclk"; - #dma-cells = <0x01>; - arm,pl330-periph-burst; - phandle = <0xd4>; - }; - - phy@fed60000 { - compatible = "rockchip,rk3588-hdptx-phy"; - reg = <0x00 0xfed60000 0x00 0x2000>; - clocks = <0x02 0x2b5 0x02 0x267>; - clock-names = "ref\0apb"; - resets = <0x02 0x485 0x02 0xc003b 0x02 0xc003c 0x02 0xc003d>; - reset-names = "apb\0init\0cmn\0lane"; - rockchip,grf = <0x16b>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0xe3>; - }; - - hdmiphy@fed60000 { - compatible = "rockchip,rk3588-hdptx-phy-hdmi"; - reg = <0x00 0xfed60000 0x00 0x2000>; - clocks = <0x02 0x2b5 0x02 0x267>; - clock-names = "ref\0apb"; - resets = <0x02 0x48e 0x02 0x485 0x02 0xc003b 0x02 0xc003c 0x02 0xc003d 0x02 0x48c 0x02 0x48d>; - reset-names = "phy\0apb\0init\0cmn\0lane\0ropll\0lcpll"; - rockchip,grf = <0x16b>; - #phy-cells = <0x00>; - status = "okay"; - phandle = <0xdf>; - - clk-port { - #clock-cells = <0x00>; - status = "okay"; - phandle = <0x2e>; - }; - }; - - phy@fed80000 { - compatible = "rockchip,rk3588-usbdp-phy"; - reg = <0x00 0xfed80000 0x00 0x10000>; - rockchip,u2phy-grf = <0x16c>; - rockchip,usb-grf = <0x5f>; - rockchip,usbdpphy-grf = <0x16d>; - rockchip,vo-grf = <0xd7>; - clocks = <0x02 0x2b6 0x02 0x27f 0x02 0x269 0x16e>; - clock-names = "refclk\0immortal\0pclk\0utmi"; - resets = <0x02 0x28 0x02 0x29 0x02 0x2a 0x02 0x2b 0x02 0x482>; - reset-names = "init\0cmn\0lane\0pcs_apb\0pma_apb"; - status = "okay"; - orientation-switch; - svid = <0xff01>; - sbu1-dc-gpios = <0xe0 0x06 0x00>; - sbu2-dc-gpios = <0xe0 0x07 0x00>; - - dp-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0xd8>; - }; - - u3-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0x53>; - }; - - port { - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x16f>; - phandle = <0x164>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x170>; - phandle = <0x165>; - }; - }; - }; - - phy@feda0000 { - compatible = "rockchip,rk3588-mipi-dcphy"; - reg = <0x00 0xfeda0000 0x00 0x10000>; - rockchip,grf = <0x171>; - clocks = <0x02 0x108 0x02 0x2b6>; - clock-names = "pclk\0ref"; - resets = <0x02 0xc0043 0x02 0x3e 0x02 0x3f 0x02 0xc0044>; - reset-names = "m_phy\0apb\0grf\0s_phy"; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x2a>; - }; - - phy@fedb0000 { - compatible = "rockchip,rk3588-mipi-dcphy"; - reg = <0x00 0xfedb0000 0x00 0x10000>; - rockchip,grf = <0x172>; - clocks = <0x02 0x109 0x02 0x2b6>; - clock-names = "pclk\0ref"; - resets = <0x02 0xc0045 0x02 0x43 0x02 0x44 0x02 0xc0046>; - reset-names = "m_phy\0apb\0grf\0s_phy"; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x2b>; - }; - - csi2-dphy0-hw@fedc0000 { - compatible = "rockchip,rk3588-csi2-dphy-hw"; - reg = <0x00 0xfedc0000 0x00 0x8000>; - clocks = <0x02 0x10c>; - clock-names = "pclk"; - resets = <0x02 0x17 0x02 0x16>; - reset-names = "srst_csiphy0\0srst_p_csiphy0"; - rockchip,grf = <0x173>; - rockchip,sys_grf = <0xb7>; - status = "disabled"; - phandle = <0x2c>; - }; - - phy@fee00000 { - compatible = "rockchip,rk3588-naneng-combphy"; - reg = <0x00 0xfee00000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x02 0x2bd 0x02 0x185 0x02 0x166>; - clock-names = "refclk\0apbclk\0phpclk"; - assigned-clocks = <0x02 0x2bd>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x02 0x20005 0x02 0x4d6>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x61>; - rockchip,pipe-phy-grf = <0x174>; - status = "disabled"; - phandle = <0xea>; - }; - - phy@fee20000 { - compatible = "rockchip,rk3588-naneng-combphy"; - reg = <0x00 0xfee20000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x02 0x2bf 0x02 0x187 0x02 0x166>; - clock-names = "refclk\0apbclk\0phpclk"; - assigned-clocks = <0x02 0x2bf>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x02 0x20007 0x02 0x4d8>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x61>; - rockchip,pipe-phy-grf = <0x175>; - rockchip,pcie1ln-sel-bits = <0x100 0x01 0x01 0x00>; - status = "disabled"; - phandle = <0x5b>; - }; - - sram@ff001000 { - compatible = "mmio-sram"; - reg = <0x00 0xff001000 0x00 0xef000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0xff001000 0xef000>; - - rkvdec-sram@0 { - reg = <0x00 0x78000>; - phandle = <0xae>; - }; - - rkvdec-sram@78000 { - reg = <0x78000 0x77000>; - phandle = <0xb0>; - }; - }; - - pinctrl { - compatible = "rockchip,rk3588-pinctrl"; - rockchip,grf = <0x176>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - phandle = <0x177>; - - gpio@fd8a0000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfd8a0000 0x00 0x100>; - interrupts = <0x00 0x115 0x04>; - clocks = <0x02 0x284 0x02 0x285>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-ranges = <0x177 0x00 0x00 0x20>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x13b>; - }; - - gpio@fec20000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfec20000 0x00 0x100>; - interrupts = <0x00 0x116 0x04>; - clocks = <0x02 0x7d 0x02 0x7e>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-ranges = <0x177 0x00 0x20 0x20>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x189>; - }; - - gpio@fec30000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfec30000 0x00 0x100>; - interrupts = <0x00 0x117 0x04>; - clocks = <0x02 0x7f 0x02 0x80>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-ranges = <0x177 0x00 0x40 0x20>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0x1b9>; - }; - - gpio@fec40000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfec40000 0x00 0x100>; - interrupts = <0x00 0x118 0x04>; - clocks = <0x02 0x81 0x02 0x82>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-ranges = <0x177 0x00 0x60 0x20>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0xee>; - }; - - gpio@fec50000 { - compatible = "rockchip,gpio-bank"; - reg = <0x00 0xfec50000 0x00 0x100>; - interrupts = <0x00 0x119 0x04>; - clocks = <0x02 0x83 0x02 0x84>; - gpio-controller; - #gpio-cells = <0x02>; - gpio-ranges = <0x177 0x00 0x80 0x20>; - interrupt-controller; - #interrupt-cells = <0x02>; - phandle = <0xe0>; - }; - - pcfg-pull-up { - bias-pull-up; - phandle = <0x17e>; - }; - - pcfg-pull-down { - bias-pull-down; - phandle = <0x181>; - }; - - pcfg-pull-none { - bias-disable; - phandle = <0x178>; - }; - - pcfg-pull-none-drv-level-2 { - bias-disable; - drive-strength = <0x02>; - phandle = <0x180>; - }; - - pcfg-pull-up-drv-level-1 { - bias-pull-up; - drive-strength = <0x01>; - phandle = <0x17f>; - }; - - pcfg-pull-up-drv-level-2 { - bias-pull-up; - drive-strength = <0x02>; - phandle = <0x179>; - }; - - pcfg-pull-up-drv-level-6 { - bias-pull-up; - drive-strength = <0x06>; - phandle = <0x17a>; - }; - - pcfg-pull-none-smt { - bias-disable; - input-schmitt-enable; - phandle = <0x17d>; - }; - - pcfg-pull-none-drv-level-1-smt { - bias-disable; - drive-strength = <0x01>; - input-schmitt-enable; - phandle = <0x17c>; - }; - - pcfg-pull-none-drv-level-5-smt { - bias-disable; - drive-strength = <0x05>; - input-schmitt-enable; - phandle = <0x17b>; - }; - - auddsm { - - auddsm-pins { - rockchip,pins = <0x03 0x01 0x04 0x178 0x03 0x02 0x04 0x178 0x03 0x03 0x04 0x178 0x03 0x04 0x04 0x178>; - phandle = <0x129>; - }; - }; - - bt1120 { - - bt1120-pins { - rockchip,pins = <0x04 0x08 0x02 0x178 0x04 0x00 0x02 0x178 0x04 0x01 0x02 0x178 0x04 0x02 0x02 0x178 0x04 0x03 0x02 0x178 0x04 0x04 0x02 0x178 0x04 0x05 0x02 0x178 0x04 0x06 0x02 0x178 0x04 0x07 0x02 0x178 0x04 0x0a 0x02 0x178 0x04 0x0b 0x02 0x178 0x04 0x0c 0x02 0x178 0x04 0x0d 0x02 0x178 0x04 0x0e 0x02 0x178 0x04 0x0f 0x02 0x178 0x04 0x10 0x02 0x178 0x04 0x11 0x02 0x178>; - phandle = <0x5c>; - }; - }; - - can0 { - - can0m0-pins { - rockchip,pins = <0x00 0x10 0x0b 0x178 0x00 0x0f 0x0b 0x178>; - phandle = <0x12a>; - }; - }; - - can1 { - - can1m1-pins { - rockchip,pins = <0x04 0x0a 0x0c 0x178 0x04 0x0b 0x0c 0x178>; - phandle = <0x12b>; - }; - }; - - can2 { - - can2m0-pins { - rockchip,pins = <0x03 0x14 0x09 0x178 0x03 0x15 0x09 0x178>; - phandle = <0x12c>; - }; - }; - - gmac1 { - - gmac1-miim { - rockchip,pins = <0x03 0x12 0x01 0x178 0x03 0x13 0x01 0x178>; - phandle = <0xf8>; - }; - - gmac1-rx-bus2 { - rockchip,pins = <0x03 0x07 0x01 0x178 0x03 0x08 0x01 0x178 0x03 0x09 0x01 0x178>; - phandle = <0xfa>; - }; - - gmac1-tx-bus2 { - rockchip,pins = <0x03 0x0b 0x01 0x17a 0x03 0x0c 0x01 0x17a 0x03 0x0d 0x01 0x178>; - phandle = <0xf9>; - }; - - gmac1-rgmii-clk { - rockchip,pins = <0x03 0x05 0x01 0x178 0x03 0x04 0x01 0x178>; - phandle = <0xfb>; - }; - - gmac1-rgmii-bus { - rockchip,pins = <0x03 0x02 0x01 0x178 0x03 0x03 0x01 0x178 0x03 0x00 0x01 0x17a 0x03 0x01 0x01 0x17a>; - phandle = <0xfc>; - }; - }; - - hdmi { - - hdmim0-tx0-cec { - rockchip,pins = <0x04 0x11 0x05 0x178>; - phandle = <0xdb>; - }; - - hdmim0-tx0-hpd { - rockchip,pins = <0x01 0x05 0x05 0x178>; - phandle = <0xdc>; - }; - - hdmim0-tx0-scl { - rockchip,pins = <0x04 0x0f 0x05 0x17b>; - phandle = <0xdd>; - }; - - hdmim0-tx0-sda { - rockchip,pins = <0x04 0x10 0x05 0x17c>; - phandle = <0xde>; - }; - - hdmim0-tx1-hpd { - rockchip,pins = <0x01 0x06 0x05 0x178>; - phandle = <0x18d>; - }; - - hdmim1-rx-cec { - rockchip,pins = <0x03 0x19 0x05 0x178>; - phandle = <0x197>; - }; - - hdmim1-rx-hpdin { - rockchip,pins = <0x03 0x1c 0x05 0x178>; - phandle = <0x198>; - }; - - hdmim1-rx-scl { - rockchip,pins = <0x03 0x1a 0x05 0x17d>; - phandle = <0x199>; - }; - - hdmim1-rx-sda { - rockchip,pins = <0x03 0x1b 0x05 0x17d>; - phandle = <0x19a>; - }; - - hdmim1-tx1-scl { - rockchip,pins = <0x03 0x16 0x05 0x17b>; - phandle = <0x18e>; - }; - - hdmim1-tx1-sda { - rockchip,pins = <0x03 0x15 0x05 0x17c>; - phandle = <0x18f>; - }; - - hdmim2-tx1-cec { - rockchip,pins = <0x03 0x14 0x05 0x178>; - phandle = <0x18c>; - }; - }; - - i2c0 { - - i2c0m2-xfer { - rockchip,pins = <0x00 0x19 0x03 0x17d 0x00 0x1a 0x03 0x17d>; - phandle = <0x62>; - }; - }; - - i2c1 { - - i2c1m2-xfer { - rockchip,pins = <0x00 0x1c 0x09 0x17d 0x00 0x1d 0x09 0x17d>; - phandle = <0x12d>; - }; - }; - - i2c2 { - - i2c2m0-xfer { - rockchip,pins = <0x00 0x0f 0x09 0x17d 0x00 0x10 0x09 0x17d>; - phandle = <0x12e>; - }; - }; - - i2c3 { - - i2c3m0-xfer { - rockchip,pins = <0x01 0x11 0x09 0x17d 0x01 0x10 0x09 0x17d>; - phandle = <0x12f>; - }; - }; - - i2c4 { - - i2c4m0-xfer { - rockchip,pins = <0x03 0x06 0x09 0x17d 0x03 0x05 0x09 0x17d>; - phandle = <0x131>; - }; - }; - - i2c5 { - - i2c5m0-xfer { - rockchip,pins = <0x03 0x17 0x09 0x17d 0x03 0x18 0x09 0x17d>; - phandle = <0x132>; - }; - }; - - i2c6 { - - i2c6m0-xfer { - rockchip,pins = <0x00 0x18 0x09 0x17d 0x00 0x17 0x09 0x17d>; - phandle = <0x15f>; - }; - }; - - i2c7 { - - i2c7m0-xfer { - rockchip,pins = <0x01 0x18 0x09 0x17d 0x01 0x19 0x09 0x17d>; - phandle = <0x166>; - }; - }; - - i2c8 { - - i2c8m0-xfer { - rockchip,pins = <0x04 0x1a 0x09 0x17d 0x04 0x1b 0x09 0x17d>; - phandle = <0x167>; - }; - }; - - i2s0 { - - i2s0-lrck { - rockchip,pins = <0x01 0x15 0x01 0x178>; - phandle = <0x105>; - }; - - i2s0-mclk { - rockchip,pins = <0x01 0x12 0x01 0x178>; - phandle = <0x130>; - }; - - i2s0-sclk { - rockchip,pins = <0x01 0x13 0x01 0x178>; - phandle = <0x106>; - }; - - i2s0-sdi0 { - rockchip,pins = <0x01 0x1c 0x02 0x178>; - phandle = <0x107>; - }; - - i2s0-sdo0 { - rockchip,pins = <0x01 0x17 0x01 0x178>; - phandle = <0x108>; - }; - }; - - i2s1 { - - i2s1m0-lrck { - rockchip,pins = <0x04 0x02 0x03 0x178>; - phandle = <0x109>; - }; - - i2s1m0-sclk { - rockchip,pins = <0x04 0x01 0x03 0x178>; - phandle = <0x10a>; - }; - - i2s1m0-sdi0 { - rockchip,pins = <0x04 0x05 0x03 0x178>; - phandle = <0x10b>; - }; - - i2s1m0-sdi1 { - rockchip,pins = <0x04 0x06 0x03 0x178>; - phandle = <0x10c>; - }; - - i2s1m0-sdi2 { - rockchip,pins = <0x04 0x07 0x03 0x178>; - phandle = <0x10d>; - }; - - i2s1m0-sdi3 { - rockchip,pins = <0x04 0x08 0x03 0x178>; - phandle = <0x10e>; - }; - - i2s1m0-sdo0 { - rockchip,pins = <0x04 0x09 0x03 0x178>; - phandle = <0x10f>; - }; - - i2s1m0-sdo1 { - rockchip,pins = <0x04 0x0a 0x03 0x178>; - phandle = <0x110>; - }; - - i2s1m0-sdo2 { - rockchip,pins = <0x04 0x0b 0x03 0x178>; - phandle = <0x111>; - }; - - i2s1m0-sdo3 { - rockchip,pins = <0x04 0x0c 0x03 0x178>; - phandle = <0x112>; - }; - }; - - i2s2 { - - i2s2m1-lrck { - rockchip,pins = <0x03 0x0e 0x03 0x178>; - phandle = <0x113>; - }; - - i2s2m1-sclk { - rockchip,pins = <0x03 0x0d 0x03 0x178>; - phandle = <0x114>; - }; - - i2s2m1-sdi { - rockchip,pins = <0x03 0x0a 0x03 0x178>; - phandle = <0x115>; - }; - - i2s2m1-sdo { - rockchip,pins = <0x03 0x0b 0x03 0x178>; - phandle = <0x116>; - }; - }; - - i2s3 { - - i2s3-lrck { - rockchip,pins = <0x03 0x02 0x03 0x178>; - phandle = <0x117>; - }; - - i2s3-sclk { - rockchip,pins = <0x03 0x01 0x03 0x178>; - phandle = <0x118>; - }; - - i2s3-sdi { - rockchip,pins = <0x03 0x04 0x03 0x178>; - phandle = <0x119>; - }; - - i2s3-sdo { - rockchip,pins = <0x03 0x03 0x03 0x178>; - phandle = <0x11a>; - }; - }; - - pdm0 { - - pdm0m0-clk { - rockchip,pins = <0x01 0x16 0x03 0x178>; - phandle = <0x11b>; - }; - - pdm0m0-clk1 { - rockchip,pins = <0x01 0x14 0x03 0x178>; - phandle = <0x11c>; - }; - - pdm0m0-sdi0 { - rockchip,pins = <0x01 0x1d 0x03 0x178>; - phandle = <0x11d>; - }; - - pdm0m0-sdi1 { - rockchip,pins = <0x01 0x19 0x03 0x178>; - phandle = <0x11e>; - }; - - pdm0m0-sdi2 { - rockchip,pins = <0x01 0x1a 0x03 0x178>; - phandle = <0x11f>; - }; - - pdm0m0-sdi3 { - rockchip,pins = <0x01 0x1b 0x03 0x178>; - phandle = <0x120>; - }; - }; - - pdm1 { - - pdm1m0-clk { - rockchip,pins = <0x04 0x1d 0x02 0x178>; - phandle = <0x121>; - }; - - pdm1m0-clk1 { - rockchip,pins = <0x04 0x1c 0x02 0x178>; - phandle = <0x122>; - }; - - pdm1m0-sdi0 { - rockchip,pins = <0x04 0x1b 0x02 0x178>; - phandle = <0x123>; - }; - - pdm1m0-sdi1 { - rockchip,pins = <0x04 0x1a 0x02 0x178>; - phandle = <0x124>; - }; - - pdm1m0-sdi2 { - rockchip,pins = <0x04 0x19 0x02 0x178>; - phandle = <0x125>; - }; - - pdm1m0-sdi3 { - rockchip,pins = <0x04 0x18 0x02 0x178>; - phandle = <0x126>; - }; - }; - - pmic { - - pmic-pins { - rockchip,pins = <0x00 0x07 0x00 0x17e 0x00 0x02 0x01 0x178 0x00 0x03 0x01 0x178 0x00 0x11 0x01 0x178 0x00 0x12 0x01 0x178 0x00 0x13 0x01 0x178 0x00 0x1e 0x01 0x178>; - phandle = <0x13c>; - }; - }; - - pwm0 { - - pwm0m0-pins { - rockchip,pins = <0x00 0x0f 0x03 0x178>; - phandle = <0x66>; - }; - }; - - pwm1 { - - pwm1m0-pins { - rockchip,pins = <0x00 0x10 0x03 0x178>; - phandle = <0x67>; - }; - }; - - pwm2 { - - pwm2m0-pins { - rockchip,pins = <0x00 0x14 0x03 0x178>; - phandle = <0x68>; - }; - }; - - pwm3 { - - pwm3m0-pins { - rockchip,pins = <0x00 0x1c 0x03 0x178>; - phandle = <0x69>; - }; - }; - - pwm4 { - - pwm4m0-pins { - rockchip,pins = <0x00 0x15 0x0b 0x178>; - phandle = <0x150>; - }; - }; - - pwm5 { - - pwm5m0-pins { - rockchip,pins = <0x00 0x09 0x03 0x178>; - phandle = <0x151>; - }; - }; - - pwm6 { - - pwm6m0-pins { - rockchip,pins = <0x00 0x17 0x0b 0x178>; - phandle = <0x152>; - }; - }; - - pwm7 { - - pwm7m0-pins { - rockchip,pins = <0x00 0x18 0x0b 0x178>; - phandle = <0x153>; - }; - }; - - pwm8 { - - pwm8m0-pins { - rockchip,pins = <0x03 0x07 0x0b 0x178>; - phandle = <0x154>; - }; - }; - - pwm9 { - - pwm9m0-pins { - rockchip,pins = <0x03 0x08 0x0b 0x178>; - phandle = <0x155>; - }; - }; - - pwm10 { - - pwm10m0-pins { - rockchip,pins = <0x03 0x00 0x0b 0x178>; - phandle = <0x156>; - }; - }; - - pwm11 { - - pwm11m0-pins { - rockchip,pins = <0x03 0x01 0x0b 0x178>; - phandle = <0x157>; - }; - }; - - pwm12 { - - pwm12m0-pins { - rockchip,pins = <0x03 0x0d 0x0b 0x178>; - phandle = <0x158>; - }; - }; - - pwm13 { - - pwm13m0-pins { - rockchip,pins = <0x03 0x0e 0x0b 0x178>; - phandle = <0x159>; - }; - }; - - pwm14 { - - pwm14m0-pins { - rockchip,pins = <0x03 0x12 0x0b 0x178>; - phandle = <0x15a>; - }; - }; - - pwm15 { - - pwm15m2-pins { - rockchip,pins = <0x01 0x16 0x0b 0x178>; - phandle = <0x15b>; - }; - }; - - sdio { - - sdiom1-pins { - rockchip,pins = <0x03 0x05 0x02 0x178 0x03 0x04 0x02 0x17e 0x03 0x00 0x02 0x17e 0x03 0x01 0x02 0x17e 0x03 0x02 0x02 0x17e 0x03 0x03 0x02 0x17e>; - phandle = <0x103>; - }; - }; - - sdmmc { - - sdmmc-bus4 { - rockchip,pins = <0x04 0x18 0x01 0x179 0x04 0x19 0x01 0x179 0x04 0x1a 0x01 0x179 0x04 0x1b 0x01 0x179>; - phandle = <0x101>; - }; - - sdmmc-clk { - rockchip,pins = <0x04 0x1d 0x01 0x179>; - phandle = <0xfe>; - }; - - sdmmc-cmd { - rockchip,pins = <0x04 0x1c 0x01 0x179>; - phandle = <0xff>; - }; - - sdmmc-det { - rockchip,pins = <0x00 0x04 0x01 0x17e>; - phandle = <0x100>; - }; - }; - - spdif0 { - - spdif0m0-tx { - rockchip,pins = <0x01 0x0e 0x03 0x178>; - phandle = <0x127>; - }; - }; - - spdif1 { - - spdif1m0-tx { - rockchip,pins = <0x01 0x0f 0x03 0x178>; - phandle = <0x128>; - }; - }; - - spi0 { - - spi0m0-pins { - rockchip,pins = <0x00 0x16 0x08 0x17a 0x00 0x17 0x08 0x17a 0x00 0x10 0x08 0x17a>; - phandle = <0x135>; - }; - - spi0m0-cs0 { - rockchip,pins = <0x00 0x19 0x08 0x17a>; - phandle = <0x133>; - }; - - spi0m0-cs1 { - rockchip,pins = <0x00 0x0f 0x08 0x17a>; - phandle = <0x134>; - }; - }; - - spi1 { - - spi1m1-pins { - rockchip,pins = <0x03 0x11 0x08 0x17a 0x03 0x10 0x08 0x17a 0x03 0x0f 0x08 0x17a>; - phandle = <0x138>; - }; - - spi1m1-cs0 { - rockchip,pins = <0x03 0x12 0x08 0x17a>; - phandle = <0x136>; - }; - - spi1m1-cs1 { - rockchip,pins = <0x03 0x13 0x08 0x17a>; - phandle = <0x137>; - }; - }; - - spi2 { - - spi2m2-pins { - rockchip,pins = <0x00 0x05 0x01 0x17f 0x00 0x0b 0x01 0x17f 0x00 0x06 0x01 0x17f>; - phandle = <0x13a>; - }; - - spi2m2-cs0 { - rockchip,pins = <0x00 0x09 0x01 0x17f>; - phandle = <0x139>; - }; - }; - - spi3 { - - spi3m1-pins { - rockchip,pins = <0x04 0x0f 0x08 0x17a 0x04 0x0d 0x08 0x17a 0x04 0x0e 0x08 0x17a>; - phandle = <0x145>; - }; - - spi3m1-cs0 { - rockchip,pins = <0x04 0x10 0x08 0x17a>; - phandle = <0x143>; - }; - - spi3m1-cs1 { - rockchip,pins = <0x04 0x11 0x08 0x17a>; - phandle = <0x144>; - }; - }; - - spi4 { - - spi4m0-pins { - rockchip,pins = <0x01 0x12 0x08 0x17a 0x01 0x10 0x08 0x17a 0x01 0x11 0x08 0x17a>; - phandle = <0x16a>; - }; - - spi4m0-cs0 { - rockchip,pins = <0x01 0x13 0x08 0x17a>; - phandle = <0x168>; - }; - - spi4m0-cs1 { - rockchip,pins = <0x01 0x14 0x08 0x17a>; - phandle = <0x169>; - }; - }; - - tsadc { - - tsadc-shut { - rockchip,pins = <0x00 0x01 0x02 0x178>; - phandle = <0x15d>; - }; - }; - - uart0 { - - uart0m2-xfer { - rockchip,pins = <0x04 0x04 0x0a 0x17e 0x04 0x03 0x0a 0x17e>; - phandle = <0x65>; - }; - }; - - uart1 { - - uart1m1-xfer { - rockchip,pins = <0x01 0x0f 0x0a 0x17e 0x01 0x0e 0x0a 0x17e>; - phandle = <0x146>; - }; - }; - - uart2 { - - uart2m0-xfer { - rockchip,pins = <0x00 0x0e 0x0a 0x17e 0x00 0x0d 0x0a 0x17e>; - phandle = <0x1ab>; - }; - - uart2m1-xfer { - rockchip,pins = <0x04 0x19 0x0a 0x17e 0x04 0x18 0x0a 0x17e>; - phandle = <0x147>; - }; - }; - - uart3 { - - uart3m1-xfer { - rockchip,pins = <0x03 0x0e 0x0a 0x17e 0x03 0x0d 0x0a 0x17e>; - phandle = <0x148>; - }; - }; - - uart4 { - - uart4m1-xfer { - rockchip,pins = <0x03 0x18 0x0a 0x17e 0x03 0x19 0x0a 0x17e>; - phandle = <0x149>; - }; - }; - - uart5 { - - uart5m1-xfer { - rockchip,pins = <0x03 0x15 0x0a 0x17e 0x03 0x14 0x0a 0x17e>; - phandle = <0x14a>; - }; - }; - - uart6 { - - uart6m1-xfer { - rockchip,pins = <0x01 0x00 0x0a 0x17e 0x01 0x01 0x0a 0x17e>; - phandle = <0x14b>; - }; - - uart6m1-ctsn { - rockchip,pins = <0x01 0x03 0x0a 0x178>; - phandle = <0x14c>; - }; - - uart6m1-rtsn { - rockchip,pins = <0x01 0x02 0x0a 0x178>; - phandle = <0x1c1>; - }; - }; - - uart7 { - - uart7m1-xfer { - rockchip,pins = <0x03 0x11 0x0a 0x17e 0x03 0x10 0x0a 0x17e>; - phandle = <0x14d>; - }; - }; - - uart8 { - - uart8m1-xfer { - rockchip,pins = <0x03 0x03 0x0a 0x17e 0x03 0x02 0x0a 0x17e>; - phandle = <0x14e>; - }; - }; - - uart9 { - - uart9m1-xfer { - rockchip,pins = <0x04 0x0d 0x0a 0x17e 0x04 0x0c 0x0a 0x17e>; - phandle = <0x14f>; - }; - }; - - gpio-func { - - tsadc-gpio-func { - rockchip,pins = <0x00 0x01 0x00 0x178>; - phandle = <0x15c>; - }; - }; - - gmac0 { - - gmac0-miim { - rockchip,pins = <0x04 0x14 0x01 0x178 0x04 0x15 0x01 0x178>; - phandle = <0xef>; - }; - - gmac0-rx-bus2 { - rockchip,pins = <0x02 0x11 0x01 0x178 0x02 0x12 0x01 0x178 0x04 0x12 0x01 0x178>; - phandle = <0xf1>; - }; - - gmac0-tx-bus2 { - rockchip,pins = <0x02 0x0e 0x01 0x17a 0x02 0x0f 0x01 0x17a 0x02 0x10 0x01 0x178>; - phandle = <0xf0>; - }; - - gmac0-rgmii-clk { - rockchip,pins = <0x02 0x08 0x01 0x178 0x02 0x0b 0x01 0x178>; - phandle = <0xf2>; - }; - - gmac0-rgmii-bus { - rockchip,pins = <0x02 0x06 0x01 0x178 0x02 0x07 0x01 0x178 0x02 0x09 0x01 0x17a 0x02 0x0a 0x01 0x17a>; - phandle = <0xf3>; - }; - }; - - dp { - - dp1-hpd { - rockchip,pins = <0x01 0x04 0x00 0x178>; - phandle = <0x188>; - }; - }; - - leds { - - leds-gpio { - rockchip,pins = <0x01 0x0b 0x00 0x178>; - phandle = <0x1ac>; - }; - }; - - hdmirx { - - hdmirx-det { - rockchip,pins = <0x01 0x1d 0x00 0x17e>; - phandle = <0x19b>; - }; - }; - - headphone { - - hp-det { - rockchip,pins = <0x01 0x14 0x00 0x178>; - phandle = <0x1b6>; - }; - }; - - hym8563 { - - hym8563-int { - rockchip,pins = <0x00 0x08 0x00 0x17e>; - phandle = <0x160>; - }; - }; - - lcd { - - lcd-rst-gpio { - rockchip,pins = <0x02 0x0c 0x00 0x178>; - }; - }; - - touch { - - touch-gpio { - rockchip,pins = <0x00 0x1d 0x00 0x17e 0x00 0x16 0x00 0x178>; - }; - }; - - usb-typec { - - usbc0-int { - rockchip,pins = <0x00 0x1b 0x00 0x17e>; - phandle = <0x161>; - }; - }; - - wireless-bluetooth { - - uart6-gpios { - rockchip,pins = <0x01 0x02 0x00 0x178>; - phandle = <0x1c5>; - }; - - bt-reset-gpio { - rockchip,pins = <0x00 0x16 0x00 0x178>; - phandle = <0x1c2>; - }; - - bt-wake-gpio { - rockchip,pins = <0x00 0x15 0x00 0x178>; - phandle = <0x1c3>; - }; - - bt-irq-gpio { - rockchip,pins = <0x00 0x00 0x00 0x181>; - phandle = <0x1c4>; - }; - }; - - wireless-wlan { - - wifi-host-wake-irq { - rockchip,pins = <0x00 0x0a 0x00 0x181>; - }; - - wifi-poweren-gpio { - rockchip,pins = <0x00 0x14 0x00 0x17e>; - phandle = <0x1c6>; - }; - }; - }; - - csi2-dphy3 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x182>; - status = "disabled"; - }; - - csi2-dphy4 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x182>; - status = "disabled"; - }; - - csi2-dphy5 { - compatible = "rockchip,rk3568-csi2-dphy"; - rockchip,hw = <0x182>; - status = "disabled"; - }; - - rkcif-mipi-lvds4 { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x183>; - }; - - rkcif-mipi-lvds4-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x183>; - status = "disabled"; - }; - - rkcif-mipi-lvds4-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x183>; - status = "disabled"; - }; - - rkcif-mipi-lvds4-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x183>; - status = "disabled"; - }; - - rkcif-mipi-lvds4-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x183>; - status = "disabled"; - }; - - rkcif-mipi-lvds5 { - compatible = "rockchip,rkcif-mipi-lvds"; - rockchip,hw = <0x3f>; - iommus = <0x40>; - status = "disabled"; - phandle = <0x184>; - }; - - rkcif-mipi-lvds5-sditf { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x184>; - status = "disabled"; - }; - - rkcif-mipi-lvds5-sditf-vir1 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x184>; - status = "disabled"; - }; - - rkcif-mipi-lvds5-sditf-vir2 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x184>; - status = "disabled"; - }; - - rkcif-mipi-lvds5-sditf-vir3 { - compatible = "rockchip,rkcif-sditf"; - rockchip,cif = <0x184>; - status = "disabled"; - }; - - usbdrd3_1 { - compatible = "rockchip,rk3588-dwc3\0rockchip,rk3399-dwc3"; - clocks = <0x02 0x1a6 0x02 0x1a5 0x02 0x1a4>; - clock-names = "ref\0suspend\0bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - status = "okay"; - - usb@fc400000 { - compatible = "snps,dwc3"; - reg = <0x00 0xfc400000 0x00 0x400000>; - interrupts = <0x00 0xdd 0x04>; - power-domains = <0x4d 0x1f>; - resets = <0x02 0x2a7>; - reset-names = "usb3-otg"; - dr_mode = "host"; - phys = <0x185 0x186>; - phy-names = "usb2-phy\0usb3-phy"; - phy_type = "utmi_wide"; - snps,dis_enblslpm_quirk; - snps,dis-u2-freeclk-exists-quirk; - snps,dis-del-phy-power-chg-quirk; - snps,dis-tx-ipgap-linecheck-quirk; - snps,parkmode-disable-ss-quirk; - status = "okay"; - }; - }; - - syscon@fd5b8000 { - compatible = "rockchip,pcie30-phy-grf\0syscon"; - reg = <0x00 0xfd5b8000 0x00 0x10000>; - phandle = <0x1a9>; - }; - - syscon@fd5c0000 { - compatible = "rockchip,pipe-phy-grf\0syscon"; - reg = <0x00 0xfd5c0000 0x00 0x100>; - phandle = <0x1a8>; - }; - - syscon@fd5cc000 { - compatible = "rockchip,rk3588-usbdpphy-grf\0syscon"; - reg = <0x00 0xfd5cc000 0x00 0x4000>; - phandle = <0x1a5>; - }; - - syscon@fd5d4000 { - compatible = "rockchip,rk3588-usb2phy-grf\0syscon\0simple-mfd"; - reg = <0x00 0xfd5d4000 0x00 0x4000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - phandle = <0x1a4>; - - usb2-phy@4000 { - compatible = "rockchip,rk3588-usb2phy"; - reg = <0x4000 0x10>; - interrupts = <0x00 0x18a 0x04>; - resets = <0x02 0xc0048 0x02 0x489>; - reset-names = "phy\0apb"; - clocks = <0x02 0x2b5>; - clock-names = "phyclk"; - clock-output-names = "usb480m_phy1"; - #clock-cells = <0x00>; - rockchip,usbctrl-grf = <0x5f>; - status = "okay"; - phandle = <0x1a6>; - - otg-port { - #phy-cells = <0x00>; - status = "okay"; - phy-supply = <0x60>; - phandle = <0x185>; - }; - }; - }; - - syscon@fd5e4000 { - compatible = "rockchip,rk3588-hdptxphy-grf\0syscon"; - reg = <0x00 0xfd5e4000 0x00 0x100>; - phandle = <0x1a3>; - }; - - mipi4-csi2@fdd50000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd50000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x97 0x04 0x00 0x98 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1d3>; - clock-names = "pclk_csi2host"; - resets = <0x02 0x328 0x02 0x338>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - mipi5-csi2@fdd60000 { - compatible = "rockchip,rk3588-mipi-csi2"; - reg = <0x00 0xfdd60000 0x00 0x10000>; - reg-names = "csihost_regs"; - interrupts = <0x00 0x99 0x04 0x00 0x9a 0x04>; - interrupt-names = "csi-intr1\0csi-intr2"; - clocks = <0x02 0x1d4>; - clock-names = "pclk_csi2host"; - resets = <0x02 0x329 0x02 0x339>; - reset-names = "srst_csihost_p\0srst_csihost_vicap"; - status = "disabled"; - }; - - spdif-tx@fddb8000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfddb8000 0x00 0x1000>; - interrupts = <0x00 0xc6 0x04>; - dmas = <0xd3 0x16>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x20f 0x02 0x20a>; - assigned-clocks = <0x02 0x20b>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x19>; - #sound-dai-cells = <0x00>; - status = "disabled"; - phandle = <0x1be>; - }; - - i2s@fddc8000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddc8000 0x00 0x1000>; - interrupts = <0x00 0xbc 0x04>; - clocks = <0x02 0x201 0x02 0x1fe>; - clock-names = "mclk_tx\0hclk"; - assigned-clocks = <0x02 0x1ff>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd4 0x16>; - dma-names = "tx"; - power-domains = <0x4d 0x19>; - resets = <0x02 0x391>; - reset-names = "tx-m"; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-tx@fdde8000 { - compatible = "rockchip,rk3588-spdif\0rockchip,rk3568-spdif"; - reg = <0x00 0xfdde8000 0x00 0x1000>; - interrupts = <0x00 0xc5 0x04>; - dmas = <0xd3 0x08>; - dma-names = "tx"; - clock-names = "mclk\0hclk"; - clocks = <0x02 0x25c 0x02 0x258>; - assigned-clocks = <0x02 0x259>; - assigned-clock-parents = <0x02 0x05>; - power-domains = <0x4d 0x1a>; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - i2s@fddf4000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddf4000 0x00 0x1000>; - interrupts = <0x00 0xba 0x04>; - clocks = <0x02 0x24c 0x02 0x24c 0x02 0x252>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x249>; - assigned-clock-parents = <0x02 0x07>; - dmas = <0xd4 0x04>; - dma-names = "tx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x3ef>; - reset-names = "tx-m"; - rockchip,always-on; - rockchip,hdmi-path; - rockchip,playback-only; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x1bc>; - }; - - i2s@fddf8000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfddf8000 0x00 0x1000>; - interrupts = <0x00 0xbb 0x04>; - clocks = <0x02 0x23c 0x02 0x23c 0x02 0x238>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x239>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd4 0x15>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x3c3>; - reset-names = "rx-m"; - rockchip,capture-only; - #sound-dai-cells = <0x00>; - status = "okay"; - phandle = <0x1c8>; - }; - - i2s@fde00000 { - compatible = "rockchip,rk3588-i2s-tdm"; - reg = <0x00 0xfde00000 0x00 0x1000>; - interrupts = <0x00 0xbe 0x04>; - clocks = <0x02 0x237 0x02 0x237 0x02 0x233>; - clock-names = "mclk_tx\0mclk_rx\0hclk"; - assigned-clocks = <0x02 0x234>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0xd4 0x18>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x417>; - reset-names = "rx-m"; - rockchip,capture-only; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-rx@fde10000 { - compatible = "rockchip,rk3588-spdifrx\0rockchip,rk3308-spdifrx"; - reg = <0x00 0xfde10000 0x00 0x1000>; - interrupts = <0x00 0xc8 0x04>; - clocks = <0x02 0x260 0x02 0x25f>; - clock-names = "mclk\0hclk"; - assigned-clocks = <0x02 0x260>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0x64 0x16>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x3ff>; - reset-names = "spdifrx-m"; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - spdif-rx@fde18000 { - compatible = "rockchip,rk3588-spdifrx\0rockchip,rk3308-spdifrx"; - reg = <0x00 0xfde18000 0x00 0x1000>; - interrupts = <0x00 0xc9 0x04>; - clocks = <0x02 0x262 0x02 0x261>; - clock-names = "mclk\0hclk"; - assigned-clocks = <0x02 0x262>; - assigned-clock-parents = <0x02 0x05>; - dmas = <0x64 0x17>; - dma-names = "rx"; - power-domains = <0x4d 0x1a>; - resets = <0x02 0x401>; - reset-names = "spdifrx-m"; - #sound-dai-cells = <0x00>; - status = "disabled"; - }; - - dp@fde60000 { - compatible = "rockchip,rk3588-dp"; - reg = <0x00 0xfde60000 0x00 0x4000>; - interrupts = <0x00 0xa2 0x04>; - clocks = <0x02 0x1e7 0x02 0x2cd 0x02 0x201 0x02 0x20d 0x04>; - clock-names = "apb\0aux\0i2s\0spdif\0hclk"; - assigned-clocks = <0x02 0x2cd>; - assigned-clock-rates = <0xf42400>; - resets = <0x02 0x389>; - phys = <0x187>; - power-domains = <0x4d 0x19>; - #sound-dai-cells = <0x01>; - status = "disabled"; - pinctrl-names = "default"; - pinctrl-0 = <0x188>; - hpd-gpios = <0x189 0x04 0x00>; - phandle = <0x1bf>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x18a>; - status = "disabled"; - phandle = <0xbf>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x18b>; - status = "disabled"; - phandle = <0xc5>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x37>; - status = "disabled"; - phandle = <0xcd>; - }; - }; - }; - }; - - hdmi@fdea0000 { - compatible = "rockchip,rk3588-dw-hdmi"; - reg = <0x00 0xfdea0000 0x00 0x20000>; - interrupts = <0x00 0xad 0x04 0x00 0xae 0x04 0x00 0xaf 0x04 0x00 0xb0 0x04 0x00 0x169 0x04>; - clocks = <0x02 0x224 0x02 0x266 0x02 0x225 0x02 0x226 0x02 0x24c 0x02 0x274 0x02 0x275 0x02 0x276 0x02 0x277 0x05 0x2f>; - clock-names = "pclk\0hpd\0earc\0hdmitx_ref\0aud\0dclk_vp0\0dclk_vp1\0dclk_vp2\0dclk_vp3\0hclk_vo1\0link_clk"; - resets = <0x02 0x3d7 0x02 0x49d>; - reset-names = "ref\0hdp"; - power-domains = <0x4d 0x1a>; - pinctrl-names = "default"; - pinctrl-0 = <0x18c 0x18d 0x18e 0x18f>; - reg-io-width = <0x04>; - rockchip,grf = <0xb7>; - rockchip,vo1_grf = <0xba>; - phys = <0x190>; - phy-names = "hdmi"; - #sound-dai-cells = <0x00>; - status = "disabled"; - enable-gpios = <0xe0 0x0a 0x00>; - phandle = <0x1bd>; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x191>; - status = "disabled"; - phandle = <0xc1>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x38>; - status = "disabled"; - phandle = <0xc7>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x192>; - status = "disabled"; - phandle = <0xcf>; - }; - }; - }; - }; - - edp@fded0000 { - compatible = "rockchip,rk3588-edp"; - reg = <0x00 0xfded0000 0x00 0x1000>; - interrupts = <0x00 0xa4 0x04>; - clocks = <0x02 0x214 0x02 0x213 0x02 0x215 0x05>; - clock-names = "dp\0pclk\0spdif\0hclk"; - resets = <0x02 0x3e4 0x02 0x3e3>; - reset-names = "dp\0apb"; - phys = <0x193>; - phy-names = "dp"; - power-domains = <0x4d 0x1a>; - rockchip,grf = <0xba>; - status = "disabled"; - - ports { - #address-cells = <0x01>; - #size-cells = <0x00>; - - port@0 { - reg = <0x00>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - endpoint@0 { - reg = <0x00>; - remote-endpoint = <0x194>; - status = "disabled"; - phandle = <0xc0>; - }; - - endpoint@1 { - reg = <0x01>; - remote-endpoint = <0x195>; - status = "disabled"; - phandle = <0xc6>; - }; - - endpoint@2 { - reg = <0x02>; - remote-endpoint = <0x196>; - status = "disabled"; - phandle = <0xce>; - }; - }; - }; - }; - - hdmirx-controller@fdee0000 { - compatible = "rockchip,rk3588-hdmirx-ctrler\0rockchip,hdmirx-ctrler"; - reg = <0x00 0xfdee0000 0x00 0x6000>; - reg-names = "hdmirx_regs"; - power-domains = <0x4d 0x1a>; - rockchip,grf = <0xb7>; - rockchip,vo1_grf = <0xba>; - interrupts = <0x00 0xb1 0x04 0x00 0x1b4 0x04 0x00 0xb3 0x04>; - interrupt-names = "cec\0hdmi\0dma"; - clocks = <0x02 0x21a 0x02 0x21f 0x02 0x2b2 0x02 0x21b 0x02 0x21c 0x02 0x232 0x05>; - clock-names = "aclk\0audio\0cr_para\0pclk\0ref\0hclk_s_hdmirx\0hclk_vo1"; - resets = <0x02 0x3d9 0x02 0x3da 0x02 0x3db 0x02 0x3b7>; - reset-names = "rst_a\0rst_p\0rst_ref\0rst_biu"; - pinctrl-0 = <0x197 0x198 0x199 0x19a 0x19b>; - pinctrl-names = "default"; - status = "disabled"; - hpd-trigger-level = <0x01>; - hdmirx-det-gpios = <0x189 0x1d 0x01>; - }; - - pcie@fe150000 { - compatible = "rockchip,rk3588-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x00 0x0f>; - clocks = <0x02 0x14e 0x02 0x153 0x02 0x149 0x02 0x158 0x02 0x15e 0x02 0x183>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux\0pipe"; - device_type = "pci"; - interrupts = <0x00 0x107 0x04 0x00 0x106 0x04 0x00 0x105 0x04 0x00 0x104 0x04 0x00 0x103 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x19c 0x00 0x00 0x00 0x00 0x02 0x19c 0x01 0x00 0x00 0x00 0x03 0x19c 0x02 0x00 0x00 0x00 0x04 0x19c 0x03>; - linux,pci-domain = <0x00>; - num-ib-windows = <0x10>; - num-ob-windows = <0x10>; - num-viewport = <0x08>; - max-link-speed = <0x03>; - msi-map = <0x00 0x19d 0x00 0x1000>; - num-lanes = <0x04>; - phys = <0x19e>; - phy-names = "pcie-phy"; - power-domains = <0x4d 0x22>; - ranges = <0x800 0x00 0xf0000000 0x00 0xf0000000 0x00 0x100000 0x81000000 0x00 0xf0100000 0x00 0xf0100000 0x00 0x100000 0x82000000 0x00 0xf0200000 0x00 0xf0200000 0x00 0xe00000 0xc3000000 0x09 0x00 0x09 0x00 0x00 0x40000000>; - reg = <0x00 0xfe150000 0x00 0x10000 0x0a 0x40000000 0x00 0x400000>; - reg-names = "pcie-apb\0pcie-dbi"; - resets = <0x02 0x20d 0x02 0x21c>; - reset-names = "pcie\0periph"; - rockchip,pipe-grf = <0x61>; - status = "disabled"; - reset-gpios = <0xe0 0x0e 0x00>; - vpcie3v3-supply = <0x19f>; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0x104 0x01>; - phandle = <0x19c>; - }; - }; - - pcie@fe160000 { - compatible = "rockchip,rk3588-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x10 0x1f>; - clocks = <0x02 0x14f 0x02 0x154 0x02 0x14a 0x02 0x159 0x02 0x15f 0x02 0x184>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux\0pipe"; - device_type = "pci"; - interrupts = <0x00 0x102 0x04 0x00 0x101 0x04 0x00 0x100 0x04 0x00 0xff 0x04 0x00 0xfe 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x1a0 0x00 0x00 0x00 0x00 0x02 0x1a0 0x01 0x00 0x00 0x00 0x03 0x1a0 0x02 0x00 0x00 0x00 0x04 0x1a0 0x03>; - linux,pci-domain = <0x01>; - num-ib-windows = <0x10>; - num-ob-windows = <0x10>; - num-viewport = <0x08>; - max-link-speed = <0x03>; - msi-map = <0x1000 0x19d 0x1000 0x1000>; - num-lanes = <0x02>; - phys = <0x19e>; - phy-names = "pcie-phy"; - power-domains = <0x4d 0x22>; - ranges = <0x800 0x00 0xf1000000 0x00 0xf1000000 0x00 0x100000 0x81000000 0x00 0xf1100000 0x00 0xf1100000 0x00 0x100000 0x82000000 0x00 0xf1200000 0x00 0xf1200000 0x00 0xe00000 0xc3000000 0x09 0x40000000 0x09 0x40000000 0x00 0x40000000>; - reg = <0x00 0xfe160000 0x00 0x10000 0x0a 0x40400000 0x00 0x400000>; - reg-names = "pcie-apb\0pcie-dbi"; - resets = <0x02 0x20e 0x02 0x21d>; - reset-names = "pcie\0periph"; - rockchip,pipe-grf = <0x61>; - status = "disabled"; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xff 0x01>; - phandle = <0x1a0>; - }; - }; - - pcie@fe170000 { - compatible = "rockchip,rk3588-pcie\0snps,dw-pcie"; - #address-cells = <0x03>; - #size-cells = <0x02>; - bus-range = <0x20 0x2f>; - clocks = <0x02 0x150 0x02 0x155 0x02 0x14b 0x02 0x15b 0x02 0x160 0x02 0x2c4>; - clock-names = "aclk_mst\0aclk_slv\0aclk_dbi\0pclk\0aux\0pipe"; - device_type = "pci"; - interrupts = <0x00 0xf3 0x04 0x00 0xf2 0x04 0x00 0xf1 0x04 0x00 0xf0 0x04 0x00 0xef 0x04>; - interrupt-names = "sys\0pmc\0msg\0legacy\0err"; - #interrupt-cells = <0x01>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x1a1 0x00 0x00 0x00 0x00 0x02 0x1a1 0x01 0x00 0x00 0x00 0x03 0x1a1 0x02 0x00 0x00 0x00 0x04 0x1a1 0x03>; - linux,pci-domain = <0x02>; - num-ib-windows = <0x08>; - num-ob-windows = <0x08>; - num-viewport = <0x04>; - max-link-speed = <0x02>; - msi-map = <0x2000 0xe8 0x2000 0x1000>; - num-lanes = <0x01>; - phys = <0x1a2 0x02>; - phy-names = "pcie-phy"; - ranges = <0x800 0x00 0xf2000000 0x00 0xf2000000 0x00 0x100000 0x81000000 0x00 0xf2100000 0x00 0xf2100000 0x00 0x100000 0x82000000 0x00 0xf2200000 0x00 0xf2200000 0x00 0xe00000 0xc3000000 0x09 0x80000000 0x09 0x80000000 0x00 0x40000000>; - reg = <0x00 0xfe170000 0x00 0x10000 0x0a 0x40800000 0x00 0x400000>; - reg-names = "pcie-apb\0pcie-dbi"; - resets = <0x02 0x20f 0x02 0x21e>; - reset-names = "pcie\0periph"; - rockchip,pipe-grf = <0x61>; - status = "disabled"; - reset-gpios = <0x189 0x0c 0x00>; - rockchip,skip-scan-in-resume; - - legacy-interrupt-controller { - interrupt-controller; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - interrupt-parent = <0x01>; - interrupts = <0x00 0xf0 0x01>; - phandle = <0x1a1>; - }; - }; - - sata@fe220000 { - compatible = "rockchip,rk-ahci\0snps,dwc-ahci"; - reg = <0x00 0xfe220000 0x00 0x1000>; - clocks = <0x02 0x172 0x02 0x16f 0x02 0x175 0x02 0x164 0x02 0x17f>; - clock-names = "sata\0pmalive\0rxoob\0ref\0asic"; - interrupts = <0x00 0x112 0x04>; - interrupt-names = "hostc"; - phys = <0x1a2 0x01>; - phy-names = "sata-phy"; - ports-implemented = <0x01>; - status = "disabled"; - }; - - phy@fed70000 { - compatible = "rockchip,rk3588-hdptx-phy"; - reg = <0x00 0xfed70000 0x00 0x2000>; - clocks = <0x02 0x2b5 0x02 0x268>; - clock-names = "ref\0apb"; - resets = <0x02 0x486 0x02 0xc003f 0x02 0xc0040 0x02 0xc0041>; - reset-names = "apb\0init\0cmn\0lane"; - rockchip,grf = <0x1a3>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x193>; - }; - - hdmiphy@fed70000 { - compatible = "rockchip,rk3588-hdptx-phy-hdmi"; - reg = <0x00 0xfed70000 0x00 0x2000>; - clocks = <0x02 0x2b5 0x02 0x268>; - clock-names = "ref\0apb"; - resets = <0x02 0x491 0x02 0x486 0x02 0xc003f 0x02 0xc0040 0x02 0xc0041 0x02 0x48f 0x02 0x490>; - reset-names = "phy\0apb\0init\0cmn\0lane\0ropll\0lcpll"; - rockchip,grf = <0x1a3>; - #phy-cells = <0x00>; - status = "disabled"; - phandle = <0x190>; - - clk-port { - #clock-cells = <0x00>; - status = "okay"; - phandle = <0x2f>; - }; - }; - - phy@fed90000 { - compatible = "rockchip,rk3588-usbdp-phy"; - reg = <0x00 0xfed90000 0x00 0x10000>; - rockchip,u2phy-grf = <0x1a4>; - rockchip,usb-grf = <0x5f>; - rockchip,usbdpphy-grf = <0x1a5>; - rockchip,vo-grf = <0xd7>; - clocks = <0x02 0x2b6 0x02 0x280 0x02 0x26a 0x1a6>; - clock-names = "refclk\0immortal\0pclk\0utmi"; - resets = <0x02 0x2f 0x02 0x30 0x02 0x31 0x02 0x32 0x02 0x484>; - reset-names = "init\0cmn\0lane\0pcs_apb\0pma_apb"; - status = "okay"; - rockchip,dp-lane-mux = <0x00 0x01>; - - dp-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0x187>; - }; - - u3-port { - #phy-cells = <0x00>; - status = "okay"; - phandle = <0x186>; - }; - }; - - csi2-dphy1-hw@fedc8000 { - compatible = "rockchip,rk3588-csi2-dphy-hw"; - reg = <0x00 0xfedc8000 0x00 0x8000>; - clocks = <0x02 0x10d>; - clock-names = "pclk"; - resets = <0x02 0x19 0x02 0x18>; - reset-names = "srst_csiphy1\0srst_p_csiphy1"; - rockchip,grf = <0x1a7>; - rockchip,sys_grf = <0xb7>; - status = "disabled"; - phandle = <0x182>; - }; - - phy@fee10000 { - compatible = "rockchip,rk3588-naneng-combphy"; - reg = <0x00 0xfee10000 0x00 0x100>; - #phy-cells = <0x01>; - clocks = <0x02 0x2be 0x02 0x186 0x02 0x166>; - clock-names = "refclk\0apbclk\0phpclk"; - assigned-clocks = <0x02 0x2be>; - assigned-clock-rates = <0x5f5e100>; - resets = <0x02 0x20006 0x02 0x4d7>; - reset-names = "combphy-apb\0combphy"; - rockchip,pipe-grf = <0x61>; - rockchip,pipe-phy-grf = <0x1a8>; - rockchip,pcie1ln-sel-bits = <0x100 0x00 0x00 0x00>; - status = "okay"; - phandle = <0x1a2>; - }; - - phy@fee80000 { - compatible = "rockchip,rk3588-pcie3-phy"; - reg = <0x00 0xfee80000 0x00 0x20000>; - #phy-cells = <0x00>; - clocks = <0x02 0x188>; - clock-names = "pclk"; - resets = <0x02 0x2000a>; - reset-names = "phy"; - rockchip,pipe-grf = <0x61>; - rockchip,phy-grf = <0x1a9>; - status = "disabled"; - phandle = <0x19e>; - }; - - test-power { - status = "okay"; - }; - - vcc12v-dcin { - compatible = "regulator-fixed"; - regulator-name = "vcc12v_dcin"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0xb71b00>; - regulator-max-microvolt = <0xb71b00>; - phandle = <0x1aa>; - }; - - vcc5v0-sys { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_sys"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - vin-supply = <0x1aa>; - phandle = <0x63>; - }; - - vcc5v0-usbdcin { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_usbdcin"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - vin-supply = <0x1aa>; - }; - - vcc5v0-usb { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_usb"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - vin-supply = <0x1aa>; - phandle = <0x1b7>; - }; - - vcc-1v1-nldo-s3 { - compatible = "regulator-fixed"; - regulator-name = "vcc_1v1_nldo_s3"; - regulator-always-on; - regulator-boot-on; - regulator-min-microvolt = <0x10c8e0>; - regulator-max-microvolt = <0x10c8e0>; - vin-supply = <0x63>; - phandle = <0x142>; - }; - - chosen { - bootargs = "storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal storagenode=/mmc@fe2e0000 androidboot.verifiedbootstate=orange rw rootwait earlycon=video,efifb,800x600,32 console=tty2 irqchip.gicv3_pseudo_nmi=0 root=PARTLABEL=rootfs rootfstype=ext4 overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 loglevel=7 nokaslr"; - }; - - memory@40000000 { - device_type = "memory"; - reg = <0x00 0x9400000 0x00 0xe6c00000>; - }; - - cspmu@fd10c000 { - compatible = "rockchip,cspmu"; - reg = <0x00 0xfd10c000 0x00 0x1000 0x00 0xfd10d000 0x00 0x1000 0x00 0xfd10e000 0x00 0x1000 0x00 0xfd10f000 0x00 0x1000 0x00 0xfd12c000 0x00 0x1000 0x00 0xfd12d000 0x00 0x1000 0x00 0xfd12e000 0x00 0x1000 0x00 0xfd12f000 0x00 0x1000>; - }; - - debug@fd104000 { - compatible = "rockchip,debug"; - reg = <0x00 0xfd104000 0x00 0x1000 0x00 0xfd105000 0x00 0x1000 0x00 0xfd106000 0x00 0x1000 0x00 0xfd107000 0x00 0x1000 0x00 0xfd124000 0x00 0x1000 0x00 0xfd125000 0x00 0x1000 0x00 0xfd126000 0x00 0x1000 0x00 0xfd127000 0x00 0x1000>; - }; - - fiq-debugger { - compatible = "rockchip,fiq-debugger"; - rockchip,serial-id = <0x02>; - rockchip,wake-irq = <0x00>; - rockchip,irq-mode-enable = <0x01>; - rockchip,baudrate = <0x16e360>; - interrupts = <0x00 0x1a7 0x08>; - pinctrl-names = "default"; - pinctrl-0 = <0x1ab>; - status = "okay"; - }; - - reserved-memory { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - cma { - compatible = "shared-dma-pool"; - reusable; - size = <0x00 0x800000>; - linux,cma-default; - reg = <0x00 0x49400000 0x00 0x10000000>; - }; - - drm-logo@00000000 { - compatible = "rockchip,drm-logo"; - reg = <0x00 0x00 0x00 0x00>; - phandle = <0x30>; - }; - - drm-cubic-lut@00000000 { - compatible = "rockchip,drm-cubic-lut"; - reg = <0x00 0x00 0x00 0x00>; - }; - - ramoops@110000 { - compatible = "ramoops"; - reg = <0x00 0x110000 0x00 0xf0000>; - record-size = <0x20000>; - console-size = <0x80000>; - ftrace-size = <0x00>; - pmsg-size = <0x50000>; - }; - }; - - leds { - compatible = "gpio-leds"; - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <0x1ac>; - - power { - label = ":power"; - linux,default-trigger = "ir-power-click"; - default-state = "on"; - gpios = <0x189 0x0b 0x00>; - }; - }; - - hdmi0-sound { - status = "okay"; - compatible = "rockchip,hdmi"; - rockchip,mclk-fs = <0x80>; - rockchip,card-name = "rockchip-hdmi0"; - rockchip,cpu = <0x1ad>; - rockchip,codec = <0x1ae>; - rockchip,jack-det; - }; - - dp0-sound { - status = "disabled"; - compatible = "rockchip,hdmi"; - rockchip,card-name = "rockchip-dp0"; - rockchip,mclk-fs = <0x200>; - rockchip,cpu = <0x1af>; - rockchip,codec = <0x1b0 0x01>; - rockchip,jack-det; - }; - - spdif-tx1-dc { - status = "disabled"; - compatible = "linux,spdif-dit"; - #sound-dai-cells = <0x00>; - phandle = <0x1b2>; - }; - - spdif-tx1-sound { - status = "disabled"; - compatible = "simple-audio-card"; - simple-audio-card,mclk-fs = <0x80>; - simple-audio-card,name = "rockchip,spdif-tx1"; - - simple-audio-card,cpu { - sound-dai = <0x1b1>; - }; - - simple-audio-card,codec { - sound-dai = <0x1b2>; - }; - }; - - adc-keys { - status = "okay"; - compatible = "adc-keys"; - io-channels = <0x1b3 0x01>; - io-channel-names = "buttons"; - keyup-threshold-microvolt = <0x1b7740>; - poll-interval = <0x64>; - - recovery-key { - label = "F12"; - linux,code = <0x58>; - press-threshold-microvolt = <0x4268>; - }; - }; - - es8388-sound { - status = "disabled"; - compatible = "firefly,multicodecs-card"; - rockchip,card-name = "rockchip-es8388"; - hp-det-gpio = <0x189 0x14 0x01>; - io-channels = <0x1b3 0x03>; - io-channel-names = "adc-detect"; - spk-con-gpio = <0xee 0x0a 0x00>; - hp-con-gpio = <0xe0 0x08 0x00>; - linein-type = <0x00>; - rockchip,format = "i2s"; - rockchip,mclk-fs = <0x100>; - rockchip,cpu = <0x1b4>; - rockchip,codec = <0x1b5>; - rockchip,audio-routing = "Headphone\0LOUT1\0Headphone\0ROUT1\0Speaker\0LOUT2\0Speaker\0ROUT2\0Headphone\0Headphone Power\0Headphone\0Headphone Power\0Speaker\0Speaker Power\0Speaker\0Speaker Power\0LINPUT1\0Main Mic\0LINPUT2\0Main Mic\0RINPUT1\0Headset Mic\0RINPUT2\0Headset Mic"; - pinctrl-names = "default"; - pinctrl-0 = <0x1b6>; - firefly,not-use-dapm; - }; - - vcc5v0-host { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_host"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - enable-active-high; - gpio = <0xe0 0x0e 0x00>; - vin-supply = <0x1b7>; - status = "okay"; - phandle = <0x60>; - }; - - vcc-hub-reset-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_hub_reset"; - regulator-boot-on; - regulator-always-on; - enable-active-high; - status = "disabled"; - gpio = <0x1b8 0x04 0x00>; - }; - - vbus5v0-typec-pwr-en-regulator { - compatible = "regulator-fixed"; - regulator-name = "vbus5v0_typec_pwr_en"; - enable-active-high; - status = "disabled"; - gpio = <0xe0 0x0a 0x00>; - phandle = <0x162>; - }; - - vcc-hub3-reset-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_hub3_reset"; - regulator-always-on; - enable-active-high; - status = "disabled"; - gpio = <0x1b8 0x06 0x00>; - }; - - vcc5v0-host3 { - compatible = "regulator-fixed"; - regulator-name = "vcc5v0_host3"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0x4c4b40>; - regulator-max-microvolt = <0x4c4b40>; - enable-active-high; - gpio = <0xe0 0x0a 0x00>; - vin-supply = <0x1b7>; - status = "disabled"; - }; - - vcc-sata-pwr-en-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_sata_pwr_en"; - regulator-boot-on; - regulator-always-on; - enable-active-high; - status = "disabled"; - gpio = <0x1b8 0x0a 0x00>; - }; - - vcc-fan-pwr-en-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_fan_pwr_en"; - regulator-boot-on; - regulator-always-on; - enable-active-high; - status = "okay"; - gpio = <0x1b8 0x0b 0x00>; - }; - - vcc-sdcard-pwr-en-regulator { - compatible = "regulator-fixed"; - regulator-name = "vcc_sdcard_pwr_en"; - regulator-boot-on; - regulator-always-on; - enable-active-high; - gpio = <0xee 0x11 0x00>; - status = "okay"; - }; - - vcc3v3-pcie30 { - compatible = "regulator-fixed"; - regulator-name = "vcc3v3_pcie30"; - regulator-min-microvolt = <0x325aa0>; - regulator-max-microvolt = <0x325aa0>; - enable-active-high; - gpios = <0x1b9 0x15 0x00>; - startup-delay-us = <0x1388>; - vin-supply = <0x1aa>; - status = "disabled"; - phandle = <0x19f>; - }; - - pcie30-avdd1v8 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd1v8"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0x1b7740>; - regulator-max-microvolt = <0x1b7740>; - vin-supply = <0x1ba>; - }; - - pcie30-avdd0v75 { - compatible = "regulator-fixed"; - regulator-name = "pcie30_avdd0v75"; - regulator-boot-on; - regulator-always-on; - regulator-min-microvolt = <0xb71b0>; - regulator-max-microvolt = <0xb71b0>; - vin-supply = <0x1bb>; - }; - - hdmi1-sound { - status = "disabled"; - compatible = "rockchip,hdmi"; - rockchip,mclk-fs = <0x80>; - rockchip,card-name = "rockchip-hdmi1"; - rockchip,cpu = <0x1bc>; - rockchip,codec = <0x1bd>; - rockchip,jack-det; - }; - - dp1-sound { - status = "disabled"; - compatible = "rockchip,hdmi"; - rockchip,card-name = "rockchip,dp1"; - rockchip,mclk-fs = <0x200>; - rockchip,cpu = <0x1be>; - rockchip,codec = <0x1bf 0x01>; - rockchip,jack-det; - }; - - wireless-bluetooth { - compatible = "bluetooth-platdata"; - clocks = <0x1c0>; - clock-names = "ext_clock"; - uart_rts_gpios = <0x189 0x02 0x01>; - pinctrl-names = "default\0rts_gpio"; - pinctrl-0 = <0x1c1 0x1c2 0x1c3 0x1c4>; - pinctrl-1 = <0x1c5>; - BT,reset_gpio = <0x13b 0x16 0x00>; - BT,wake_gpio = <0x13b 0x15 0x00>; - BT,wake_host_irq = <0x13b 0x00 0x00>; - status = "disabled"; - }; - - wireless-wlan { - compatible = "wlan-platdata"; - wifi_chip_type = "ap6275p"; - pinctrl-names = "default"; - pinctrl-0 = <0x1c6>; - WIFI,poweren_gpio = <0x13b 0x14 0x00>; - status = "disabled"; - }; - - hdmiin-dc { - compatible = "rockchip,dummy-codec"; - #sound-dai-cells = <0x00>; - phandle = <0x1c9>; - }; - - hdmiin-sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "rockchip,hdmiin"; - simple-audio-card,bitclock-master = <0x1c7>; - simple-audio-card,frame-master = <0x1c7>; - status = "disabled"; - - simple-audio-card,cpu { - sound-dai = <0x1c8>; - }; - - simple-audio-card,codec { - sound-dai = <0x1c9>; - phandle = <0x1c7>; - }; - }; - - pwm-fan { - compatible = "pwm-fan"; - #cooling-cells = <0x02>; - fan-supply = <0x1aa>; - pwms = <0x1ca 0x00 0xc350 0x01>; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.toml b/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.toml deleted file mode 100644 index 5dd99570d..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-rk3588-smp8.toml +++ /dev/null @@ -1,122 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 8 -# Guest vm physical cpu sets. -phys_cpu_sets = [1, 2, 4, 8, 16, 32, 64, 128] -phys_cpu_ids = [0x00, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700] - - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x1008_0000 -# The load address of the kernel image. -kernel_load_addr = 0x1008_0000 -# The load address of the device tree blob (DTB). -dtb_load_addr = 0x1000_0000 -# The location of image: "memory" | "fs". -# load from memory -image_location = "memory" -# The file path of the kernel image. -kernel_path = "/path/to/kernel" -# The file path of the device tree blob (DTB). -dtb_path = "/path/to/dtb" - -# load from file system. -# image_location = "fs". -## The file path of the kernel image. -# kernel_path = "linux-arceos-aarch64.bin" -## The file path of the device tree blob (DTB). -# dtb_path = "linux-rk3588.dtb" - -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "disk.img" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x940_0000, 0xd550_0000, 0x7, 1], # ram 3G MAP_IDENTICAL - [0x920_0000, 0x2000, 0xf, 2] - -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - - -interrupt_mode = "passthrough" -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - [ - "ramoops", - 0x11_0000, - 0x11_0000, - 0xf_0000, - 0x17, - ], - [ - "sram", - 0x10_f000, - 0x10_f000, - 0x1000, - 0x17, - ], - [ - "gpu", - 0xfb00_0000, - 0xfb00_0000, - 0x20_0000, - 0x17, - ], - [ - "uart8250 UART", - 0xfd00_0000, - 0xfd00_0000, - 0x200_0000, - 0x17, - ], - [ - "usb", - 0xfc00_0000, - 0xfc00_0000, - 0x100_0000, - 0x17, - ], - [ - "uncached", - 0x0, - 0x0, - 0x10_f000, - 0x17, - ], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.dts b/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.dts deleted file mode 100644 index f59ffcabe..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.dts +++ /dev/null @@ -1,1302 +0,0 @@ -/dts-v1/; - -/memreserve/ 0x0000000080000000 0x0000000000010000; -/ { - compatible = "phytium,pe2204"; - interrupt-parent = <0x01>; - #address-cells = <0x02>; - #size-cells = <0x02>; - model = "Phytium Pi Board"; - - // memory@01 { - // device_type = "memory"; - // reg = <0x20 0x40000000 0x00 0x40000000>; - // numa-node-id = <0x00>; - // }; - - aliases { - serial0 = "/soc/uart@2800c000"; - serial1 = "/soc/uart@2800d000"; - serial2 = "/soc/uart@2800e000"; - serial3 = "/soc/uart@2800f000"; - ethernet0 = "/soc/ethernet@3200c000"; - ethernet1 = "/soc/ethernet@3200e000"; - ethernet2 = "/soc/ethernet@32010000"; - ethernet3 = "/soc/ethernet@32012000"; - serial4 = "/soc/uart@28014000"; - serial5 = "/soc/uart@2802A000"; - serial6 = "/soc/uart@28032000"; - }; - - psci { - compatible = "arm,psci-1.0"; - method = "smc"; - cpu_suspend = <0xc4000001>; - cpu_off = <0x84000002>; - cpu_on = <0xc4000003>; - sys_poweroff = <0x84000008>; - sys_reset = <0x84000009>; - }; - - firmware { - - scmi { - compatible = "arm,scmi"; - mboxes = <0x02 0x00>; - mbox-names = "tx"; - shmem = <0x03>; - #address-cells = <0x01>; - #size-cells = <0x00>; - - protocol@13 { - reg = <0x13>; - #clock-cells = <0x01>; - phandle = <0x09>; - }; - - protocol@15 { - reg = <0x15>; - #thermal-sensor-cells = <0x01>; - phandle = <0x04>; - }; - }; - - optee { - compatible = "linaro,optee-tz"; - method = "smc"; - }; - }; - - thermal-zones { - - sensor0 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x00>; - }; - - sensor1 { - polling-delay-passive = <0x64>; - polling-delay = <0x3e8>; - thermal-sensors = <0x04 0x01>; - }; - }; - - cpus { - #address-cells = <0x02>; - #size-cells = <0x00>; - - cpu-map { - - // cluster0 { - - // core0 { - // cpu = <0x05>; - // }; - // }; - - cluster1 { - - core0 { - cpu = <0x06>; - }; - }; - - // cluster2 { - - // core0 { - // cpu = <0x07>; - // }; - - // core1 { - // cpu = <0x08>; - // }; - // }; - }; - - // cpu@0 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x200>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x07>; - // }; - - // cpu@1 { - // device_type = "cpu"; - // compatible = "phytium,ftc310\0arm,armv8"; - // reg = <0x00 0x201>; - // enable-method = "psci"; - // clocks = <0x09 0x02>; - // capacity-dmips-mhz = <0xb22>; - // phandle = <0x08>; - // }; - - // cpu@100 { - // device_type = "cpu"; - // compatible = "phytium,ftc664\0arm,armv8"; - // reg = <0x00 0x00>; - // enable-method = "psci"; - // clocks = <0x09 0x00>; - // capacity-dmips-mhz = <0x161c>; - // phandle = <0x05>; - // }; - - cpu@101 { - device_type = "cpu"; - compatible = "phytium,ftc664\0arm,armv8"; - reg = <0x00 0x100>; - enable-method = "psci"; - clocks = <0x09 0x01>; - capacity-dmips-mhz = <0x161c>; - phandle = <0x06>; - }; - }; - - interrupt-controller@30800000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - interrupt-controller; - reg = <0x00 0x30800000 0x00 0x20000 0x00 0x30880000 0x00 0x80000 0x00 0x30840000 0x00 0x10000 0x00 0x30850000 0x00 0x10000 0x00 0x30860000 0x00 0x10000>; - interrupts = <0x01 0x09 0x08>; - phandle = <0x01>; - - gic-its@30820000 { - compatible = "arm,gic-v3-its"; - msi-controller; - reg = <0x00 0x30820000 0x00 0x20000>; - phandle = <0x0f>; - }; - }; - - pmu { - compatible = "arm,armv8-pmuv3"; - interrupts = <0x01 0x07 0x08>; - }; - - timer { - compatible = "arm,armv8-timer"; - interrupts = <0x01 0x0d 0x08 0x01 0x0e 0x08 0x01 0x0b 0x08 0x01 0x0a 0x08>; - clock-frequency = <0x2faf080>; - }; - - clocks { - #address-cells = <0x02>; - #size-cells = <0x02>; - ranges; - - clk48mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2dc6c00>; - phandle = <0x13>; - }; - - clk50mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x2faf080>; - phandle = <0x0d>; - }; - - clk100mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x5f5e100>; - phandle = <0x0c>; - }; - - clk200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xbebc200>; - phandle = <0x11>; - }; - - clk250mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0xee6b280>; - phandle = <0x12>; - }; - - clk300mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x11e1a300>; - phandle = <0x0b>; - }; - - clk600mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x23c34600>; - phandle = <0x0e>; - }; - - clk1200mhz { - compatible = "fixed-clock"; - #clock-cells = <0x00>; - clock-frequency = <0x47868c00>; - phandle = <0x0a>; - }; - }; - - iommu@30000000 { - compatible = "arm,smmu-v3"; - reg = <0x00 0x30000000 0x00 0x800000>; - interrupts = <0x00 0xf0 0x01 0x00 0xef 0x01 0x00 0xec 0x01 0x00 0xf2 0x01>; - interrupt-names = "eventq\0priq\0cmdq-sync\0gerror"; - dma-coherent; - #iommu-cells = <0x01>; - phandle = <0x10>; - }; - - soc { - compatible = "simple-bus"; - #address-cells = <0x02>; - #size-cells = <0x02>; - dma-coherent; - ranges; - - mmc@28000000 { - compatible = "phytium,mci"; - reg = <0x00 0x28000000 0x00 0x1000>; - interrupts = <0x00 0x48 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "disabled"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - }; - - mmc@28001000 { - compatible = "phytium,mci"; - reg = <0x00 0x28001000 0x00 0x1000>; - interrupts = <0x00 0x49 0x04>; - clocks = <0x0a>; - clock-names = "phytium_mci_clk"; - status = "disabled"; - bus-width = <0x04>; - max-frequency = <0x2faf080>; - cap-sdio-irq; - cap-sd-highspeed; - no-mmc; - no-sd; - non-removable; - }; - - nand@28002000 { - compatible = "phytium,nfc"; - reg = <0x00 0x28002000 0x00 0x1000>; - interrupts = <0x00 0x4a 0x04>; - status = "disabled"; - }; - - ddma@28003000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28003000 0x00 0x1000>; - interrupts = <0x00 0x4b 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - ddma@28004000 { - compatible = "phytium,ddma"; - reg = <0x00 0x28004000 0x00 0x1000>; - interrupts = <0x00 0x4c 0x04>; - #dma-cells = <0x02>; - dma-channels = <0x08>; - }; - - spi@28008000 { - compatible = "phytium,qspi-nor"; - reg = <0x00 0x28008000 0x00 0x1000 0x00 0x00 0x00 0xfffffff>; - reg-names = "qspi\0qspi_mm"; - clocks = <0x0b>; - status = "okay"; - #address-cells = <0x01>; - #size-cells = <0x00>; - - flash@0 { - compatible = "jedec,spi-nor"; - reg = <0x00>; - spi-rx-bus-width = <0x01>; - spi-max-frequency = <0x1312d00>; - status = "okay"; - }; - }; - - uart@2800c000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800c000 0x00 0x1000>; - interrupts = <0x00 0x53 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800d000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800d000 0x00 0x1000>; - interrupts = <0x00 0x54 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800e000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800e000 0x00 0x1000>; - interrupts = <0x00 0x55 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@2800f000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2800f000 0x00 0x1000>; - interrupts = <0x00 0x56 0x04>; - clocks = <0x0c 0x0c>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - lpc@28010000 { - compatible = "simple-mfd\0syscon"; - reg = <0x00 0x28010000 0x00 0x1000>; - reg-io-width = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x28010000 0x1000>; - - kcs@24 { - compatible = "phytium,kcs-bmc"; - reg = <0x24 0x01 0x30 0x01 0x3c 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@28 { - compatible = "phytium,kcs-bmc"; - reg = <0x28 0x01 0x34 0x01 0x40 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@2c { - compatible = "phytium,kcs-bmc"; - reg = <0x2c 0x01 0x38 0x01 0x44 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - kcs@8c { - compatible = "phytium,kcs-bmc"; - reg = <0x8c 0x01 0x90 0x01 0x94 0x01>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - - bt@48 { - compatible = "phytium,bt-bmc"; - reg = <0x48 0x20>; - interrupts = <0x00 0x58 0x04>; - status = "disabled"; - }; - }; - - gpio@28034000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28034000 0x00 0x1000>; - interrupts = <0x00 0x6c 0x04 0x00 0x6d 0x04 0x00 0x6e 0x04 0x00 0x6f 0x04 0x00 0x70 0x04 0x00 0x71 0x04 0x00 0x72 0x04 0x00 0x73 0x04 0x00 0x74 0x04 0x00 0x75 0x04 0x00 0x76 0x04 0x00 0x77 0x04 0x00 0x78 0x04 0x00 0x79 0x04 0x00 0x7a 0x04 0x00 0x7b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28035000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28035000 0x00 0x1000>; - interrupts = <0x00 0x7c 0x04 0x00 0x7d 0x04 0x00 0x7e 0x04 0x00 0x7f 0x04 0x00 0x80 0x04 0x00 0x81 0x04 0x00 0x82 0x04 0x00 0x83 0x04 0x00 0x84 0x04 0x00 0x85 0x04 0x00 0x86 0x04 0x00 0x87 0x04 0x00 0x88 0x04 0x00 0x89 0x04 0x00 0x8a 0x04 0x00 0x8b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x14>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28036000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28036000 0x00 0x1000>; - interrupts = <0x00 0x8c 0x04 0x00 0x8d 0x04 0x00 0x8e 0x04 0x00 0x8f 0x04 0x00 0x90 0x04 0x00 0x91 0x04 0x00 0x92 0x04 0x00 0x93 0x04 0x00 0x94 0x04 0x00 0x95 0x04 0x00 0x96 0x04 0x00 0x97 0x04 0x00 0x98 0x04 0x00 0x99 0x04 0x00 0x9a 0x04 0x00 0x9b 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - phandle = <0x15>; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28037000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28037000 0x00 0x1000>; - interrupts = <0x00 0x9c 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28038000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28038000 0x00 0x1000>; - interrupts = <0x00 0x9d 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - gpio@28039000 { - compatible = "phytium,gpio"; - reg = <0x00 0x28039000 0x00 0x1000>; - interrupts = <0x00 0x9e 0x04>; - gpio-controller; - #gpio-cells = <0x02>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - porta { - compatible = "phytium,gpio-port"; - reg = <0x00>; - ngpios = <0x10>; - }; - }; - - spi@2803a000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803a000 0x00 0x1000>; - interrupts = <0x00 0x9f 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - global-cs = <0x01>; - - spidev@0 { - compatible = "spidev"; - reg = <0x00>; - spi-max-frequency = <0x2faf080>; - status = "disabled"; - }; - }; - - spi@2803b000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803b000 0x00 0x1000>; - interrupts = <0x00 0xa0 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803c000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803c000 0x00 0x1000>; - interrupts = <0x00 0xa1 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - spi@2803d000 { - compatible = "phytium,spi"; - reg = <0x00 0x2803d000 0x00 0x1000>; - interrupts = <0x00 0xa2 0x04>; - clocks = <0x0d>; - num-cs = <0x04>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "disabled"; - }; - - watchdog@28040000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28041000 0x00 0x1000 0x00 0x28040000 0x00 0x1000>; - interrupts = <0x00 0xa4 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - watchdog@28042000 { - compatible = "arm,sbsa-gwdt"; - reg = <0x00 0x28043000 0x00 0x1000 0x00 0x28042000 0x00 0x1000>; - interrupts = <0x00 0xa5 0x04>; - timeout-sec = <0x1e>; - status = "okay"; - }; - - pwm@2804a000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804a000 0x00 0x1000>; - interrupts = <0x00 0xad 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - pwm@2804b000 { - compatible = "phytium,pwm"; - reg = <0x00 0x2804b000 0x00 0x1000>; - interrupts = <0x00 0xae 0x04>; - clocks = <0x0d>; - status = "okay"; - phytium,db = <0x00 0x00 0x64 0x3e8 0x3e8 0x00>; - }; - - tacho@28054000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28054000 0x00 0x1000>; - interrupts = <0x00 0xc2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28055000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28055000 0x00 0x1000>; - interrupts = <0x00 0xc3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28056000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28056000 0x00 0x1000>; - interrupts = <0x00 0xc4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28057000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28057000 0x00 0x1000>; - interrupts = <0x00 0xc5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28058000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28058000 0x00 0x1000>; - interrupts = <0x00 0xc6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28059000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28059000 0x00 0x1000>; - interrupts = <0x00 0xc7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805a000 0x00 0x1000>; - interrupts = <0x00 0xc8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805b000 0x00 0x1000>; - interrupts = <0x00 0xc9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805c000 0x00 0x1000>; - interrupts = <0x00 0xca 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805d000 0x00 0x1000>; - interrupts = <0x00 0xcb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805e000 0x00 0x1000>; - interrupts = <0x00 0xcc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2805f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2805f000 0x00 0x1000>; - interrupts = <0x00 0xcd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28060000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28060000 0x00 0x1000>; - interrupts = <0x00 0xce 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28061000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28061000 0x00 0x1000>; - interrupts = <0x00 0xcf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28062000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28062000 0x00 0x1000>; - interrupts = <0x00 0xd0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28063000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28063000 0x00 0x1000>; - interrupts = <0x00 0xd1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28064000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28064000 0x00 0x1000>; - interrupts = <0x00 0xd2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28065000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28065000 0x00 0x1000>; - interrupts = <0x00 0xd3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28066000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28066000 0x00 0x1000>; - interrupts = <0x00 0xd4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28067000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28067000 0x00 0x1000>; - interrupts = <0x00 0xd5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28068000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28068000 0x00 0x1000>; - interrupts = <0x00 0xd6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28069000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28069000 0x00 0x1000>; - interrupts = <0x00 0xd7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806a000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806a000 0x00 0x1000>; - interrupts = <0x00 0xd8 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806b000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806b000 0x00 0x1000>; - interrupts = <0x00 0xd9 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806c000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806c000 0x00 0x1000>; - interrupts = <0x00 0xda 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806d000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806d000 0x00 0x1000>; - interrupts = <0x00 0xdb 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806e000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806e000 0x00 0x1000>; - interrupts = <0x00 0xdc 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@2806f000 { - compatible = "phytium,tacho"; - reg = <0x00 0x2806f000 0x00 0x1000>; - interrupts = <0x00 0xdd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28070000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28070000 0x00 0x1000>; - interrupts = <0x00 0xde 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28071000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28071000 0x00 0x1000>; - interrupts = <0x00 0xdf 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28072000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28072000 0x00 0x1000>; - interrupts = <0x00 0xe0 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28073000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28073000 0x00 0x1000>; - interrupts = <0x00 0xe1 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28074000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28074000 0x00 0x1000>; - interrupts = <0x00 0xe2 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28075000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28075000 0x00 0x1000>; - interrupts = <0x00 0xe3 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28076000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28076000 0x00 0x1000>; - interrupts = <0x00 0xe4 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28077000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28077000 0x00 0x1000>; - interrupts = <0x00 0xe5 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28078000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28078000 0x00 0x1000>; - interrupts = <0x00 0xe6 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - tacho@28079000 { - compatible = "phytium,tacho"; - reg = <0x00 0x28079000 0x00 0x1000>; - interrupts = <0x00 0xe7 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb2@31800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31800000 0x00 0x80000 0x00 0x31990000 0x00 0x10000>; - interrupts = <0x00 0x20 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@31880000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31880000 0x00 0x80000 0x00 0x319a0000 0x00 0x10000>; - interrupts = <0x00 0x21 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@31900000 { - compatible = "phytium,usb2"; - reg = <0x00 0x31900000 0x00 0x80000 0x00 0x319b0000 0x00 0x10000>; - interrupts = <0x00 0x22 0x04>; - status = "disabled"; - dr_mode = "peripheral"; - }; - - usb2@32800000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32800000 0x00 0x40000 0x00 0x32880000 0x00 0x40000>; - interrupts = <0x00 0x0e 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - usb2@32840000 { - compatible = "phytium,usb2"; - reg = <0x00 0x32840000 0x00 0x40000 0x00 0x328c0000 0x00 0x40000>; - interrupts = <0x00 0x0f 0x04>; - status = "okay"; - dr_mode = "host"; - }; - - dc@32000000 { - compatible = "phytium,dc"; - reg = <0x00 0x32000000 0x00 0x8000>; - interrupts = <0x00 0x2c 0x04>; - status = "okay"; - pipe_mask = [01]; - edp_mask = [00]; - }; - - i2s_dp0@32009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x32009000 0x00 0x1000 0x00 0x32008000 0x00 0x1000>; - interrupts = <0x00 0x2f 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp0"; - status = "okay"; - }; - - i2s_dp1@3200B000 { - compatible = "phytium,i2s"; - reg = <0x00 0x3200b000 0x00 0x1000 0x00 0x3200a000 0x00 0x1000>; - interrupts = <0x00 0x30 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - dai-name = "phytium-i2s-dp1"; - status = "disabled"; - }; - - pmdk_dp { - compatible = "phytium,pmdk-dp"; - status = "okay"; - num-dp = <0x01>; - dp-mask = [01]; - }; - - mailbox@32a00000 { - compatible = "phytium,mbox"; - reg = <0x00 0x32a00000 0x00 0x1000>; - interrupts = <0x00 0x16 0x04>; - #mbox-cells = <0x01>; - phandle = <0x02>; - }; - - rng@32a36000 { - compatible = "phytium,rng"; - reg = <0x00 0x32a36000 0x00 0x1000>; - status = "okay"; - }; - - sram@32a10000 { - compatible = "phytium,pe220x-sram-ns\0mmio-sram"; - reg = <0x00 0x32a10000 0x00 0x2000>; - #address-cells = <0x01>; - #size-cells = <0x01>; - ranges = <0x00 0x00 0x32a10000 0x2000>; - - scp-shmem@0 { - compatible = "arm,scmi-shmem"; - reg = <0x1000 0x400>; - }; - - scp-shmem@1 { - compatible = "arm,scmi-shmem"; - reg = <0x1400 0x400>; - phandle = <0x03>; - }; - }; - - gdma@32b34000 { - compatible = "phytium,gdma"; - dma-channels = <0x10>; - max-outstanding = <0x10>; - reg = <0x00 0x32b34000 0x00 0x1000>; - interrupts = <0x00 0xea 0x04>; - #dma-cells = <0x01>; - }; - - spinlock@32b36000 { - compatible = "phytium,hwspinlock"; - reg = <0x00 0x32b36000 0x00 0x1000>; - #hwlock-cells = <0x01>; - nr-locks = <0x20>; - status = "disabled"; - }; - - pcie@40000000 { - compatible = "pci-host-ecam-generic"; - device_type = "pci"; - #address-cells = <0x03>; - #size-cells = <0x02>; - #interrupt-cells = <0x01>; - reg = <0x00 0x40000000 0x00 0x10000000>; - msi-parent = <0x0f>; - bus-range = <0x00 0xff>; - interrupt-map-mask = <0x00 0x00 0x00 0x07>; - interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x07 0x04>; - ranges = <0x1000000 0x00 0x00 0x00 0x50000000 0x00 0xf00000 0x2000000 0x00 0x58000000 0x00 0x58000000 0x00 0x28000000 0x3000000 0x10 0x00 0x10 0x00 0x10 0x00>; - iommu-map = <0x00 0x10 0x00 0x10000>; - status = "okay"; - }; - - edac@32b28000 { - compatible = "phytium,pe220x-edac"; - reg = <0x00 0x32b28000 0x00 0x1000 0x00 0x31400000 0x00 0x1000 0x00 0x31401000 0x00 0x1000>; - interrupts = <0x00 0x00 0x04 0x00 0x01 0x04>; - status = "disabled"; - }; - - hda@28006000 { - compatible = "phytium,hda"; - reg = <0x00 0x28006000 0x00 0x1000>; - interrupts = <0x00 0x4e 0x04>; - status = "disabled"; - }; - - i2s@28009000 { - compatible = "phytium,i2s"; - reg = <0x00 0x28009000 0x00 0x1000 0x00 0x28005000 0x00 0x1000>; - interrupts = <0x00 0x4d 0x04>; - clocks = <0x0e>; - clock-names = "i2s_clk"; - status = "okay"; - #sound-dai-cells = <0x00>; - dai-name = "phytium-i2s-lsd"; - phandle = <0x16>; - }; - - can@2800a000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800a000 0x00 0x1000>; - interrupts = <0x00 0x51 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - can@2800b000 { - compatible = "phytium,canfd"; - reg = <0x00 0x2800b000 0x00 0x1000>; - interrupts = <0x00 0x52 0x04>; - clocks = <0x11>; - clock-names = "can_clk"; - tx-fifo-depth = <0x40>; - rx-fifo-depth = <0x40>; - status = "okay"; - }; - - keypad@2807a000 { - compatible = "phytium,keypad"; - reg = <0x00 0x2807a000 0x00 0x1000>; - interrupts = <0x00 0xbd 0x04>; - clocks = <0x0d>; - status = "disabled"; - }; - - usb3@31a08000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a08000 0x00 0x18000>; - interrupts = <0x00 0x10 0x04>; - status = "okay"; - }; - - usb3@31a28000 { - compatible = "phytium,pe220x-xhci"; - reg = <0x00 0x31a28000 0x00 0x18000>; - interrupts = <0x00 0x11 0x04>; - status = "okay"; - }; - - sata@31a40000 { - compatible = "generic-ahci"; - reg = <0x00 0x31a40000 0x00 0x1000>; - interrupts = <0x00 0x2a 0x04>; - status = "okay"; - }; - - sata@32014000 { - compatible = "generic-ahci"; - reg = <0x00 0x32014000 0x00 0x1000>; - interrupts = <0x00 0x2b 0x04>; - status = "okay"; - }; - - ethernet@3200c000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200c000 0x00 0x2000>; - interrupts = <0x00 0x37 0x04 0x00 0x38 0x04 0x00 0x39 0x04 0x00 0x3a 0x04 0x00 0x1c 0x04 0x00 0x1d 0x04 0x00 0x1e 0x04 0x00 0x1f 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - support-tsn; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@3200e000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x3200e000 0x00 0x2000>; - interrupts = <0x00 0x3b 0x04 0x00 0x3c 0x04 0x00 0x3d 0x04 0x00 0x3e 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "okay"; - phy-mode = "sgmii"; - use-mii; - }; - - ethernet@32010000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32010000 0x00 0x2000>; - interrupts = <0x00 0x40 0x04 0x00 0x41 0x04 0x00 0x42 0x04 0x00 0x43 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - ethernet@32012000 { - compatible = "cdns,phytium-gem-1.0"; - reg = <0x00 0x32012000 0x00 0x2000>; - interrupts = <0x00 0x44 0x04 0x00 0x45 0x04 0x00 0x46 0x04 0x00 0x47 0x04>; - clock-names = "pclk\0hclk\0tx_clk\0tsu_clk"; - clocks = <0x12 0x13 0x13 0x12>; - magic-packet; - status = "disabled"; - }; - - vpu@32b00000 { - compatible = "phytium,vpu"; - reg = <0x00 0x32b00000 0x00 0x20000>; - interrupts = <0x00 0x0c 0x04>; - status = "okay"; - }; - - i2c@28026000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28026000 0x00 0x1000>; - interrupts = <0x00 0x65 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28030000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28030000 0x00 0x1000>; - interrupts = <0x00 0x6a 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - - es8336@10 { - #sound-dai-cells = <0x00>; - compatible = "everest,es8336"; - reg = <0x10>; - phandle = <0x17>; - }; - }; - - uart@28014000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28014000 0x00 0x1000>; - interrupts = <0x00 0x5c 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - i2c@28016000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28016000 0x00 0x1000>; - interrupts = <0x00 0x5d 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - i2c@28024000 { - compatible = "phytium,i2c"; - reg = <0x00 0x28024000 0x00 0x1000>; - interrupts = <0x00 0x64 0x04>; - clocks = <0x0d>; - #address-cells = <0x01>; - #size-cells = <0x00>; - status = "okay"; - }; - - uart@2802A000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x2802a000 0x00 0x1000>; - interrupts = <0x00 0x67 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - - uart@28032000 { - compatible = "arm,pl011\0arm,primecell"; - reg = <0x00 0x28032000 0x00 0x1000>; - interrupts = <0x00 0x6b 0x04>; - clocks = <0x0d 0x0d>; - clock-names = "uartclk\0apb_pclk"; - status = "okay"; - }; - }; - - chosen { - bootargs = "console=ttyAMA1,115200 earlycon=pl011,0x2800d000 rootwait fsckfix root=/dev/sda2 rw cma=256m ;"; - stdout-path = "serial1:115200n8"; - }; - - memory { - device_type = "memory"; - reg = <0x20 0x40000000 0x00 0x40000000>; - }; - - leds { - compatible = "gpio-leds"; - - sysled { - label = "sysled"; - gpios = <0x14 0x05 0x00>; - linux,default-trigger = "none"; - }; - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "phytium,pe220x-i2s-audio"; - simple-audio-card,pin-switches = "mic-in"; - simple-audio-card,widgets = "Microphone\0mic-in\0Headphone\0Headphones"; - simple-audio-card,routing = "MIC2\0mic-in"; - simple-audio-card,hp-det-gpio = <0x15 0x0b 0x01>; - - simple-audio-card,cpu { - sound-dai = <0x16>; - }; - - simple-audio-card,codec { - sound-dai = <0x17>; - }; - }; -}; diff --git a/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.toml b/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.toml deleted file mode 100644 index e136765f6..000000000 --- a/os/axvisor/configs/vms/linux-aarch64-tac_e400-smp1.toml +++ /dev/null @@ -1,62 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x100] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x20_4008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "memory" -# The load address of the kernel image. -kernel_load_addr = 0x20_4008_0000 -## The file path of the kernel image. -kernel_path = "/path/to/Image" -## The file path of the device tree blob (DTB). -dtb_load_addr = 0x20_4000_0000 -#dtb_path = "/path/to/linux-aarch64-tac_e400-smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x20_4000_0000, 0x4000_0000, 0x7, 1], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -interrupt_mode = "passthrough" -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/linux-riscv64-qemu-smp1.dts b/os/axvisor/configs/vms/linux-riscv64-qemu-smp1.dts deleted file mode 100644 index 7a09d0979..000000000 --- a/os/axvisor/configs/vms/linux-riscv64-qemu-smp1.dts +++ /dev/null @@ -1,132 +0,0 @@ -/dts-v1/; - -/ { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "riscv-virtio"; - model = "riscv-virtio,qemu"; - - memory@90000000 { - device_type = "memory"; - reg = <0x00 0x90000000 0x00 0x40000000>; - }; - - cpus { - #address-cells = <0x01>; - #size-cells = <0x00>; - timebase-frequency = <0x989680>; - - cpu@0 { - phandle = <0x07>; - device_type = "cpu"; - reg = <0x00>; - status = "okay"; - compatible = "riscv"; - riscv,cbop-block-size = <0x40>; - riscv,cboz-block-size = <0x40>; - riscv,cbom-block-size = <0x40>; - riscv,isa-extensions = "i\0m\0a\0f\0d\0c"; - riscv,isa-base = "rv64i"; - riscv,isa = "rv64imafdc"; - mmu-type = "riscv,sv39"; - - interrupt-controller { - #interrupt-cells = <0x01>; - interrupt-controller; - compatible = "riscv,cpu-intc"; - phandle = <0x08>; - }; - }; - }; - - aliases { - serial0 = "/soc/serial@10000000"; - }; - - chosen { - bootargs = "earlycon=sbi console=ttyS0,115200 init=/init root=/dev/vda rw"; - stdout-path = "/soc/serial@10000000"; - }; - - soc { - #address-cells = <0x02>; - #size-cells = <0x02>; - compatible = "simple-bus"; - ranges; - - serial@10000000 { - interrupts = <0x0a>; - interrupt-parent = <0x09>; - clock-frequency = "\08@"; - reg = <0x00 0x10000000 0x00 0x100>; - compatible = "ns16550a"; - }; - - plic@c000000 { - phandle = <0x09>; - riscv,ndev = <0x5f>; - reg = <0x00 0xc000000 0x00 0x600000>; - interrupts-extended = <0x08 0x0b 0x08 0x09>; - interrupt-controller; - compatible = "sifive,plic-1.0.0\0riscv,plic0"; - #address-cells = <0x00>; - #interrupt-cells = <0x01>; - }; - - virtio_mmio@10008000 { - interrupts = <0x08>; - interrupt-parent = <0x09>; - reg = <0x00 0x10008000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10007000 { - interrupts = <0x07>; - interrupt-parent = <0x09>; - reg = <0x00 0x10007000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10006000 { - interrupts = <0x06>; - interrupt-parent = <0x09>; - reg = <0x00 0x10006000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10005000 { - interrupts = <0x05>; - interrupt-parent = <0x09>; - reg = <0x00 0x10005000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10004000 { - interrupts = <0x04>; - interrupt-parent = <0x09>; - reg = <0x00 0x10004000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10003000 { - interrupts = <0x03>; - interrupt-parent = <0x09>; - reg = <0x00 0x10003000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10002000 { - interrupts = <0x02>; - interrupt-parent = <0x09>; - reg = <0x00 0x10002000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - - virtio_mmio@10001000 { - interrupts = <0x01>; - interrupt-parent = <0x09>; - reg = <0x00 0x10001000 0x00 0x1000>; - compatible = "virtio,mmio"; - }; - }; -}; diff --git a/os/axvisor/configs/vms/linux-riscv64-qemu-smp1.toml b/os/axvisor/configs/vms/linux-riscv64-qemu-smp1.toml deleted file mode 100644 index 7aeacebbf..000000000 --- a/os/axvisor/configs/vms/linux-riscv64-qemu-smp1.toml +++ /dev/null @@ -1,67 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "linux-qemu" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu sets. -phys_cpu_ids = [0] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x9000_0000 -# The location of image: "memory" | "fs". -# load from memory. -image_location = "memory" -# The file path of the kernel image. -# kernel_path = "linux-6.6.62.bin" -kernel_path = "/path/tmp/images/qemu_riscv64_linux/qemu-riscv64" -# The load address of the kernel image. -kernel_load_addr = 0x9000_0000 -# The file path of the device tree blob (DTB). -dtb_path = "/path/tmp/configs/linux-riscv64-qemu-smp1.dtb" -# The load address of the device tree blob (DTB). -dtb_load_addr = 0x9300_0000 - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x9000_0000, 0x4000_0000, 0x7, 2], # System RAM 1G MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - # ["/soc/serial@10000000", 0x1000_0000, 0x1000_0000, 0x1000, 33], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - [0x1000_0000, 0x1000], # uart - [0x1000_1000, 0x8000], # virtio-mmio -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ -] - -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [ - ["plic", 0x0c00_0000, 0x60_0000, 0, 0x30, [2]], # [context_num] -] - -interrupt_mode = "passthrough" diff --git a/os/axvisor/configs/vms/nimbos-aarch64-qemu-smp1.toml b/os/axvisor/configs/vms/nimbos-aarch64-qemu-smp1.toml deleted file mode 100644 index e280c206b..000000000 --- a/os/axvisor/configs/vms/nimbos-aarch64-qemu-smp1.toml +++ /dev/null @@ -1,82 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "nimbos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu sets. -phys_cpu_ids = [0] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x4008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The file path of the kernel image. -kernel_path = "nimbos-aarch64.bin" -# kernel_path = "/home/debin/Codes/arm_tiny/build/arm_tiny.bin" -# The load address of the kernel image. -kernel_load_addr = 0x4008_0000 - -## The file path of the BIOS image. -# bios_path = "" -## The load address of the BIOS image. -# bios_load_addr = 0 -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x4000_0000, 0x100_0000, 0x7, 0], # Low RAM 16M 0b00111 R|W|EXECUTE -] - -# -# Device specifications -# -[devices] -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [ - # [ - # "vgicd-v2", - # 0x800_0000, - # 0x10_000, - # 25, - # 1, - # [2], - # ], # Vgicdv2 4k irq-25 EmuDeviceTGicdV2 config: vcpu_num -] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/pl011@9000000"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - #[0x28041000, 0x100_0000] - [0x0800_0000, 0x100_0000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - ["/pcie@10000000"], -] \ No newline at end of file diff --git a/os/axvisor/configs/vms/nimbos-riscv64-qemu-smp1.toml b/os/axvisor/configs/vms/nimbos-riscv64-qemu-smp1.toml deleted file mode 100644 index 5b08bc1d4..000000000 --- a/os/axvisor/configs/vms/nimbos-riscv64-qemu-smp1.toml +++ /dev/null @@ -1,59 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "nimbos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu sets. -phys_cpu_sets = [1] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x9020_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The file path of the kernel image. -kernel_path = "nimbos-riscv64.bin" -# The load address of the kernel image. -kernel_load_addr = 0x9020_0000 - -## The file path of the BIOS image. -# bios_path = "" -## The load address of the BIOS image. -# bios_load_addr = -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x9000_0000, 0x100_0000, 0xf, 0], # Low RAM 16M 0b1111 R|W|EXECUTE|U -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["PLIC@c000000", 0x0c00_0000, 0x0c00_0000, 0x21_0000, 0x1], - ["UART@10000000", 0x1000_0000, 0x1000_0000, 0x1000, 0x1], -] diff --git a/os/axvisor/configs/vms/nimbos-x86_64-qemu-smp1.toml b/os/axvisor/configs/vms/nimbos-x86_64-qemu-smp1.toml deleted file mode 100644 index f937a41b1..000000000 --- a/os/axvisor/configs/vms/nimbos-x86_64-qemu-smp1.toml +++ /dev/null @@ -1,80 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "nimbos" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# Guest vm physical cpu sets. -phys_cpu_sets = [1] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8000 -# The location of image: "memory" | "fs". -# Load kernel image into memory; path will be patched by CI script. -image_location = "memory" -# The file path of the kernel image. -kernel_path = "/tmp/.axvisor-images/qemu_x86_64_nimbos/qemu-x86_64" -# The load address of the kernel image. -kernel_load_addr = 0x20_0000 -# The file path of the BIOS image. -# NimbOS x86_64 needs axvm-bios at 0x8000 to bootstrap; path will be patched by setup script. -bios_path = "/tmp/.axvisor-images/qemu_x86_64_nimbos/axvm-bios.bin" -# The load address of the BIOS image. -bios_load_addr = 0x8000 - -## The file path of the ramdisk image. -# ramdisk_path = "" -## The load address of the ramdisk image. -# ramdisk_load_addr = 0 -## The path of the disk image. -# disk_path = "" - -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x0000_0000, 0x100_0000, 0x7, 0], # Low RAM 16M 0b111 R|W|EXECUTE -] - -# -# Device specifications -# -[devices] -interrupt_mode = "passthrough" -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - [ - "IO APIC", - 0xfec0_0000, - 0xfec0_0000, - 0x1000, - 0x1, - ], - [ - "Local APIC", - 0xfee0_0000, - 0xfee0_0000, - 0x1000, - 0x1, - ], - [ - "HPET", - 0xfed0_0000, - 0xfed0_0000, - 0x1000, - 0x1, - ], -] diff --git a/os/axvisor/configs/vms/rtthread-aarch64-e2000-smp1.toml b/os/axvisor/configs/vms/rtthread-aarch64-e2000-smp1.toml deleted file mode 100644 index c999c3404..000000000 --- a/os/axvisor/configs/vms/rtthread-aarch64-e2000-smp1.toml +++ /dev/null @@ -1,63 +0,0 @@ -# Vm base info configs -# -[base] -# Guest vm id. -id = 1 -# Guest vm name. -name = "rtthread" -# Virtualization type. -vm_type = 1 -# The number of virtual CPUs. -cpu_num = 1 -# The physical CPU ids. -phys_cpu_ids = [0x100] - -# -# Vm kernel configs -# -[kernel] -# The entry point of the kernel image. -entry_point = 0x8008_0000 -# The location of image: "memory" | "fs". -# Load from file system. -image_location = "fs" -# The load address of the kernel image. -kernel_load_addr = 0x8008_0000 -## The file path of the kernel image. -kernel_path = "/guest/rtthread/phytiumpi" -## The file path of the device tree blob (DTB). -# dtb_load_addr = 0x8000_0000 -#dtb_path = "/path/to/linux-aarch64-tac_e400-smp1.dtb" -# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`). -# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`. -memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 0], # System RAM MAP_IDENTICAL -] - -# -# Device specifications -# -[devices] -# Emu_devices. -# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig. -interrupt_mode = "passthrough" -emu_devices = [] - -# Pass-through devices. -# Name Base-Ipa Base-Pa Length Alloc-Irq. -passthrough_devices = [ - ["/"], - #["/timer"], -] - -# Passthrough addresses. -# Base-GPA Length. -passthrough_addresses = [ - # [0x28041000, 0x100_0000] - [0x32b30000, 0x1000] -] - -# Devices that are not desired to be passed through to the guest -excluded_devices = [ - # ["/gic-v3"], -] \ No newline at end of file diff --git a/os/axvisor/doc/FDT_Configuration_Guide.md b/os/axvisor/doc/FDT_Configuration_Guide.md deleted file mode 100644 index a9724ac15..000000000 --- a/os/axvisor/doc/FDT_Configuration_Guide.md +++ /dev/null @@ -1,274 +0,0 @@ -# AxVisor 设备树配置使用说明 - -本文档详细说明了在 AxVisor 中如何配置和使用设备树(FDT)来生成客户机 VM 的设备树。 - -本文档所述功能只在aarch64 架构下支持。 - -## 1. 概述 - -AxVisor 支持两种方式生成客户机 VM 的设备树: - -1. **使用预定义的设备树文件**:通过 [kernel] 部分的 `dtb_path` 指定设备树文件路径 -2. **动态生成设备树**:当 `dtb_path` 字段未使用时,根据配置文件中的参数动态生成设备树 - -无论采用哪种方式,CPU 节点和内存节点都会根据配置进行更新。 - -## 2. 配置文件结构 - -配置文件采用 TOML 格式,主要包含以下几个部分: - -```toml -[base] -# 基本配置信息 - -[kernel] -# 内核和设备树配置 - -[devices] -# 设备配置信息 -``` - -## 3. 设备树处理机制 - -### 3.1 使用预定义设备树文件 - -当 [kernel] 部分的 `dtb_path` 配置了设备树文件路径时: - -```toml -[kernel] -dtb_path = "/path/to/device-tree.dtb" -``` - -AxVisor 会优先使用提供的设备树文件,并根据以下配置更新其中的 CPU 节点和内存节点: - -- CPU 节点根据 [base] 部分的 `phys_cpu_ids` 更新 -- 内存节点根据 [kernel] 部分的 `memory_regions` 更新 - -注意:当使用预定义设备树文件时,[devices] 部分的 `passthrough_devices` 中如果有规范化的[Name,Base-Ipa,Base-Pa,Length,Alloc-Irq]设备配置,则axvisor会直接按照 `passthrough_devices`中的配置给guest映射设备内存,设备树中的解析则会被忽略,只是将更改过内存和cpu的预定义设备树文件直接传给guest。 - -### 3.2 动态生成设备树 - -当 [kernel] 部分的 `dtb_path` 未添加时: - -```toml -[kernel] -# dtb_path = "" -``` - -AxVisor 会根据配置文件中的参数动态生成客户机设备树: - -1. **CPU 节点**:根据 [base] 部分的 `phys_cpu_ids` 生成 -2. **内存节点**:根据 [kernel] 部分的 `memory_regions` 生成 -3. **其他设备节点**:根据 [devices] 部分的 `passthrough_devices` 和 `excluded_devices` 生成 - -## 4. 配置参数详解 - -### 4.1 基本配置 [base] - -```toml -[base] -id = 1 # 客户机 VM ID -name = "linux-qemu" # 客户机 VM 名称 -vm_type = 1 # 虚拟化类型 -cpu_num = 1 # 虚拟 CPU 数量 -phys_cpu_ids = [0] # 客户机 VM 物理 CPU 集合 -``` - -注意:配置文件中的 `phys_cpu_sets` 字段已不再需要手动配置。AxVisor 会根据主机设备树和 `phys_cpu_ids` 自动识别并生成相应的 CPU 集合掩码。 - -### 4.2 内核配置 [kernel] - -```toml -[kernel] -entry_point = 0x8020_0000 # 内核镜像入口点 -image_location = "memory" # 镜像位置 ("memory" | "fs") -kernel_path = "tmp/Image" # 内核镜像文件路径 -kernel_load_addr = 0x8020_0000 # 内核镜像加载地址 -dtb_path = "tmp/linux.dtb" # 设备树文件路径(空字符串表示动态生成) -dtb_load_addr = 0x8000_0000 # 设备树加载地址 - -# 内存区域配置,格式为 (基地址, 大小, 标志, 映射类型) -其中映射类型0为MAP_Alloc(由host负责,随机分配内存),1为Map_Identical(由host负责1:1给guest映射内存,但是起始地址随机),2为MAP_Reserved(由host负责,将host中一块标记为reserved的内存完全1:1映射给guest,起始地址和配置一致) -memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 0], # 系统 RAM 1G MAP_IDENTICAL -] -``` - -### 4.3 设备配置 [devices] - -```toml -[devices] -# 直通设备配置(仅在动态生成设备树时生效) -passthrough_devices = [ - ["/intc"], -] - -# 排除设备配置(仅在动态生成设备树时生效) -excluded_devices = [ - ["/intc"], -] -``` - -注意:直通设备配置已简化,现在只需要提供从根节点开始的完整路径即可,如 ["/intc"]。设备的地址、大小等信息会根据设备树自动识别并直通,无需手动填写。 - -## 5. 设备直通机制 - -### 5.1 直通设备配置 - -`passthrough_devices` 定义了需要直通给客户机的设备节点: - -```toml -passthrough_devices = [ - ["/"], # 直通根节点及其所有子节点 - ["/intc"], # 直通 /intc 节点及其子节点 -] -``` - -设备节点格式为从根节点开始的全局路径(如 `/intc`),在直通时会将以下节点包含在客户机设备树中: - -1. 指定的直通节点本身 -2. 直通节点的所有后代节点 -3. 与直通设备相关的依赖节点 - -注意: -1. 此配置仅在动态生成设备树时生效,当使用预定义设备树文件时将被忽略。 -2. 直通设备配置已简化,现在只需要提供从根节点开始的完整路径即可,设备的地址、大小等信息会根据设备树自动识别并直通。 - -### 5.2 排除设备配置 - -`excluded_devices` 定义了不希望直通给客户机的设备节点: - -```toml -excluded_devices = [ - ["/timer"], # 排除 /timer 节点及其子节点 -] -``` - -在查找所有直通节点后,会将排除的节点及其后代节点从最终的客户机设备树中移除。 - -注意:此配置仅在动态生成设备树时生效,当使用预定义设备树文件时将被忽略。 - -### 5.3 直通地址配置 - -`passthrough_addresses` 定义了直通给客户机使用的地址信息: - -``` -passthrough_addresses = [ - [0x28041000, 0x100_0000], -] -``` - -该字段定义的地址会直通给客户机使用,这在某些情况下非常有用,例如设备树文件为非标准设备树格式或客户机系统时定制linux。 - -## 6. 示例配置 - -### 6.1 使用预定义设备树文件的配置 - -```toml -[base] -id = 1 -name = "linux-qemu" -vm_type = 1 -cpu_num = 2 -phys_cpu_ids = [0, 1] -# phys_cpu_sets 不再需要手动配置,会自动根据 phys_cpu_ids 生成 - -[kernel] -entry_point = 0x8020_0000 -image_location = "memory" -kernel_path = "tmp/Image" -kernel_load_addr = 0x8020_0000 -dtb_path = "/home/user/device-tree.dtb" # 使用预定义设备树文件 -dtb_load_addr = 0x8000_0000 - -memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -[devices] -# 注意:以下配置在使用预定义设备树时将被忽略 -passthrough_devices = [ - ["/intc"], -] -# 直通地址配置 -passthrough_addresses = [ - [0x28041000, 0x100_0000], -] - -excluded_devices = [ - ["/timer"], -] -``` - -### 6.2 动态生成设备树的配置 - -```toml -[base] -id = 1 -name = "linux-qemu" -vm_type = 1 -cpu_num = 2 -phys_cpu_ids = [0, 1] -# phys_cpu_sets 不再需要手动配置,会自动根据 phys_cpu_ids 生成 - -[kernel] -entry_point = 0x8020_0000 -image_location = "memory" -kernel_path = "tmp/Image" -kernel_load_addr = 0x8020_0000 -# dtb_path = "" # 不使用该字段表示动态生成设备树 -dtb_load_addr = 0x8000_0000 - -memory_regions = [ - [0x8000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL -] - -[devices] -# 以下配置仅在动态生成设备树时生效 -# 注意:直通设备配置已简化,现在只需要提供从根节点开始的完整路径即可 -passthrough_devices = [ - ["/"], - ["/intc"], -] -# 直通地址配置 -passthrough_addresses = [ - [0x28041000, 0x100_0000], -] -excluded_devices = [ - ["/timer"], - ["/watchdog"], -] -``` - -## 7. 处理流程 - -1. **检查 dtb_path**: - - 如果使用 `dtb_path` 字段,则加载并使用预定义的设备树文件,此时 `passthrough_devices` 和 `excluded_devices` 配置将被忽略 - - 如果未使用 `dtb_path` 字段,则动态生成设备树,此时 `passthrough_devices` 和 `excluded_devices` 配置生效 - -2. **CPU 节点处理**: - - 根据 `phys_cpu_ids` 配置更新或生成 CPU 节点 - - 只包含配置中指定的 CPU - - 自动根据 `phys_cpu_ids` 生成 `phys_cpu_sets`,无需手动配置 - -3. **内存节点处理**: - - 根据 `memory_regions` 配置更新或生成内存节点 - - 按照指定的地址和大小创建内存区域 - -4. **设备节点处理**(仅在动态生成时): - - 根据 `passthrough_devices` 确定需要包含的设备节点 - - 包括直通节点、其后代节点以及相关依赖节点 - - 根据 `excluded_devices` 排除指定的设备节点及其后代节点 - -5. **生成最终设备树**: - - 将处理后的节点组合成完整的设备树 - - 存储在全局缓存中供后续使用 - -## 8. 特别配置 -1. **qemu 启动参数**: -``` - arceos_args = ["BUS=mmio", "BLK=y", "LOG=info", "SMP=4", "MEM=8g", - "QEMU_ARGS=\"-machine gic-version=3 -cpu cortex-a72 -append 'root=/dev/vda rw init=/init' \"", - "DISK_IMG=\"tmp/qemu/rootfs.img\"",] -``` -其中当不提供设备树时 `-append 'root=/dev/vda rw init=/init'`参数必须添加,目的是在主机设备树中添加chosen节点的bootargs属性。 \ No newline at end of file diff --git a/os/axvisor/doc/qemu-quickstart.md b/os/axvisor/doc/qemu-quickstart.md deleted file mode 100644 index 61d55096d..000000000 --- a/os/axvisor/doc/qemu-quickstart.md +++ /dev/null @@ -1,151 +0,0 @@ -# QEMU Quickstart Guide - -English | [中文](qemu-quickstart_cn.md) - -This guide covers how to set up the AxVisor development environment locally and run different guest operating systems on QEMU. - -## Prerequisites - -- **OS**: Linux (native or WSL2) -- **Architecture**: x86_64 host - -## 1. Install System Dependencies - -```bash -sudo apt update && sudo apt install -y \ - build-essential gcc libssl-dev libudev-dev pkg-config \ - qemu-system-x86 qemu-system-arm qemu-system-misc \ - git curl wget -``` - -## 2. Install Rust Toolchain - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source "$HOME/.cargo/env" -``` - -Once you enter the project directory, Rust will automatically install the required nightly toolchain, components, and cross-compilation targets based on `rust-toolchain.toml` — no manual configuration needed. - -Install additional Cargo tools: - -```bash -cargo install cargo-binutils -cargo +stable install ostool --version '^0.8' -``` - -- `cargo-binutils`: provides `rust-objcopy`, `rust-objdump`, etc. -- `ostool`: custom build runner for AxVisor - -## 3. KVM Setup (NimbOS x86_64 Only) - -NimbOS runs on x86_64 QEMU and requires KVM hardware acceleration. ArceOS and Linux use AArch64 QEMU (TCG mode) and do not need KVM — you can skip this section. - -Verify the KVM device exists: - -```bash -ls -la /dev/kvm -``` - -Add your user to the `kvm` group: - -```bash -sudo usermod -aG kvm $USER -``` - -Apply the group change in the current terminal without re-logging: - -```bash -newgrp kvm -``` - -Verify: - -```bash -id # output should include "kvm" -``` - -## 4. Running Guest OSes - -This branch provides a one-click setup script `scripts/setup_qemu.sh` that automatically downloads guest images, patches configuration paths, and prepares the rootfs. - -### ArceOS (AArch64) - -```bash -./scripts/setup_qemu.sh arceos - -cargo xtask qemu \ - --build-config configs/board/qemu-aarch64.toml \ - --qemu-config .github/workflows/qemu-aarch64.toml \ - --vmconfigs tmp/vmconfigs/arceos-aarch64-qemu-smp1.generated.toml -``` - -Success indicator: `Hello, world!` appears in the output. - -### Linux (AArch64) - -```bash -./scripts/setup_qemu.sh linux - -cargo xtask qemu \ - --build-config configs/board/qemu-aarch64.toml \ - --qemu-config .github/workflows/qemu-aarch64.toml \ - --vmconfigs tmp/vmconfigs/linux-aarch64-qemu-smp1.generated.toml -``` - -Success indicator: `test pass!` appears in the output. - -### NimbOS (x86_64, requires KVM) - -```bash -./scripts/setup_qemu.sh nimbos - -cargo xtask qemu \ - --build-config configs/board/qemu-x86_64.toml \ - --qemu-config .github/workflows/qemu-x86_64-kvm.toml \ - --vmconfigs tmp/vmconfigs/nimbos-x86_64-qemu-smp1.generated.toml -``` - -After booting, you will enter the Rust user shell (`>>` prompt). Type `usertests` to run the test suite. All tests passing will print `usertests passed!` - -> **Note**: NimbOS requires VT-x/KVM. If `/dev/kvm` does not exist or has insufficient permissions, you will get a `Permission denied` error. WSL2 requires nested virtualization support in the kernel to use KVM. - -## 5. What Does setup_qemu.sh Do? - -The script automates three steps, eliminating manual work: - -1. **Download images**: calls `cargo xtask image download` to fetch guest images to `/tmp/.axvisor-images/` -2. **Generate temp configs**: copies VM config templates to `tmp/vmconfigs/*.generated.toml`, then uses `sed` to update `kernel_path` (and `bios_path` for NimbOS) to actual image paths without modifying tracked files in `configs/vms/*.toml` -3. **Prepare rootfs**: copies `rootfs.img` to the project's `tmp/` directory for QEMU to use - -You can also perform these steps manually if you prefer not to use the script. - -## Troubleshooting - -### `Path tmp/Image not found` - -The `kernel_path` in the VM config points to a non-existent file. Run `./scripts/setup_qemu.sh ` to automatically fix the paths. - -### `Could not access KVM kernel module: Permission denied` - -Your user is not in the `kvm` group. See the "KVM Setup" section above. - -### `qemu-system-aarch64: command not found` - -QEMU is not installed. Run the `apt install` command from Step 1. - -### `Auto syncing from registry ... timed out` - -This usually indicates unstable access to GitHub Raw endpoints. `scripts/setup_qemu.sh` includes one built-in recovery attempt: when the first image download fails, it bootstraps a local registry and retries once automatically. The script also has a default fallback registry (currently pointing to `v0.0.22.toml`). - -If your network is unstable for specific registry URLs, you can override the fallback registry: - -```bash -export AXVISOR_REGISTRY_FALLBACK_URL="https://raw.githubusercontent.com/arceos-hypervisor/axvisor-guest/refs/heads/main/registry/v0.0.22.toml" -./scripts/setup_qemu.sh arceos -``` - -### First build is very slow - -This is expected. AxVisor has many dependencies, and the first compilation needs to download and build all crates. Subsequent incremental builds will be much faster. - diff --git a/os/axvisor/doc/qemu-quickstart_cn.md b/os/axvisor/doc/qemu-quickstart_cn.md deleted file mode 100644 index 8a3c25553..000000000 --- a/os/axvisor/doc/qemu-quickstart_cn.md +++ /dev/null @@ -1,151 +0,0 @@ -# QEMU 快速上手指南 - -[English](qemu-quickstart.md) | 中文 - -本文档介绍如何在本地搭建 AxVisor 的开发运行环境,并通过 QEMU 运行不同的客户机系统。 - -## 环境要求 - -- **操作系统**:Linux(原生 / WSL2 均可) -- **架构**:x86_64 宿主机 - -## 1. 安装系统依赖 - -```bash -sudo apt update && sudo apt install -y \ - build-essential gcc libssl-dev libudev-dev pkg-config \ - qemu-system-x86 qemu-system-arm qemu-system-misc \ - git curl wget -``` - -## 2. 安装 Rust 工具链 - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source "$HOME/.cargo/env" -``` - -进入项目目录后,Rust 会根据 `rust-toolchain.toml` 自动安装所需的 nightly 工具链、组件和交叉编译目标,无需手动配置。 - -安装额外的 Cargo 工具: - -```bash -cargo install cargo-binutils -cargo +stable install ostool --version '^0.8' -``` - -- `cargo-binutils`:提供 `rust-objcopy`、`rust-objdump` 等工具 -- `ostool`:AxVisor 的自定义构建运行器 - -## 3. KVM 配置(仅 NimbOS x86_64 需要) - -NimbOS 运行在 x86_64 QEMU 上并依赖 KVM 硬件加速。ArceOS 和 Linux 使用 AArch64 QEMU(TCG 模式),不需要 KVM,可跳过本节。 - -确认 KVM 设备存在: - -```bash -ls -la /dev/kvm -``` - -将当前用户加入 `kvm` 组: - -```bash -sudo usermod -aG kvm $USER -``` - -使组权限在当前终端立即生效(无需重新登录): - -```bash -newgrp kvm -``` - -验证: - -```bash -id # 输出应包含 "kvm" -``` - -## 4. 运行客户机 - -本分支提供了一键部署脚本 `scripts/setup_qemu.sh`,自动完成镜像下载、配置路径修改和 rootfs 准备。 - -### ArceOS(AArch64) - -```bash -./scripts/setup_qemu.sh arceos - -cargo xtask qemu \ - --build-config configs/board/qemu-aarch64.toml \ - --qemu-config .github/workflows/qemu-aarch64.toml \ - --vmconfigs tmp/vmconfigs/arceos-aarch64-qemu-smp1.generated.toml -``` - -启动成功标志:输出中出现 `Hello, world!` - -### Linux(AArch64) - -```bash -./scripts/setup_qemu.sh linux - -cargo xtask qemu \ - --build-config configs/board/qemu-aarch64.toml \ - --qemu-config .github/workflows/qemu-aarch64.toml \ - --vmconfigs tmp/vmconfigs/linux-aarch64-qemu-smp1.generated.toml -``` - -启动成功标志:输出中出现 `test pass!` - -### NimbOS(x86_64,需要 KVM) - -```bash -./scripts/setup_qemu.sh nimbos - -cargo xtask qemu \ - --build-config configs/board/qemu-x86_64.toml \ - --qemu-config .github/workflows/qemu-x86_64-kvm.toml \ - --vmconfigs tmp/vmconfigs/nimbos-x86_64-qemu-smp1.generated.toml -``` - -启动成功后会进入 Rust user shell(`>>` 提示符),输入 `usertests` 运行测试套件,全部通过后输出 `usertests passed!` - -> **注意**:NimbOS 依赖 VT-x/KVM。如果 `/dev/kvm` 不存在或权限不足,会报 `Permission denied` 错误。WSL2 需要内核支持嵌套虚拟化才能使用 KVM。 - -## 5. setup_qemu.sh 做了什么 - -该脚本自动完成以下三步,省去手动操作: - -1. **下载镜像**:调用 `cargo xtask image download` 将 Guest 镜像下载到 `/tmp/.axvisor-images/` -2. **生成临时配置**:复制模板 VM 配置到 `tmp/vmconfigs/*.generated.toml`,并用 `sed` 更新 `kernel_path`(以及 NimbOS 的 `bios_path`)到实际镜像路径,不修改仓库内 `configs/vms/*.toml` -3. **准备 rootfs**:将 `rootfs.img` 复制到项目的 `tmp/` 目录下供 QEMU 使用 - -如果不想使用脚本,也可以手动执行上述步骤。 - -## 常见问题 - -### `Path tmp/Image not found` - -VM 配置中的 `kernel_path` 指向了不存在的文件。运行 `./scripts/setup_qemu.sh ` 会自动修正路径。 - -### `Could not access KVM kernel module: Permission denied` - -当前用户不在 `kvm` 组中。参见上文「KVM 配置」一节。 - -### `qemu-system-aarch64: command not found` - -未安装 QEMU。执行第 1 步的 `apt install` 命令。 - -### `Auto syncing from registry ... timed out` - -这通常是访问 GitHub Raw 不稳定导致的。`scripts/setup_qemu.sh` 已内置一次自动恢复逻辑:首次下载失败后,会尝试自动引导本地 registry 并重试镜像下载。脚本内也提供了默认 fallback registry(当前指向 `v0.0.22.toml`)。 - -如果你所在网络环境对部分 URL 不稳定,可显式覆盖 fallback registry: - -```bash -export AXVISOR_REGISTRY_FALLBACK_URL="https://raw.githubusercontent.com/arceos-hypervisor/axvisor-guest/refs/heads/main/registry/v0.0.22.toml" -./scripts/setup_qemu.sh arceos -``` - -### 首次构建非常慢 - -正常现象。AxVisor 依赖较多,首次编译需要下载并编译所有 crate。后续增量编译会快很多。 - diff --git a/os/axvisor/doc/shell.md b/os/axvisor/doc/shell.md deleted file mode 100644 index e0c4188fd..000000000 --- a/os/axvisor/doc/shell.md +++ /dev/null @@ -1,1104 +0,0 @@ -# AxVisor Shell 模块详细介绍 - -## 概述 - -AxVisor Shell 模块是 AxVisor 虚拟化管理器中的一个重要组件,为用户提供了一个功能丰富的交互式命令行界面。该模块基于 Rust 语言实现,具有完整的命令解析、历史记录、终端控制和虚拟机管理功能。 - -``` -┌─────────────────────────────────────────────┐ -│ Shell Interface Layer │ -│ ┌─────────────┐ ┌─────────────┐ │ -│ │ Interactive │ │ Command CLI │ │ -│ │ Shell │ │ Parser │ │ -│ └─────────────┘ └─────────────┘ │ -├─────────────────────────────────────────────┤ -│ VM Management Facade │ -│ ┌─────────────┐ ┌──────────────────┐ │ -│ │ Controller │ │ Query & Monitor │ │ -│ └─────────────┘ └──────────────────┘ │ -├─────────────────────────────────────────────┤ -│ Existing VMM Components │ -│ VMList │ VCpu │ IVC │ Timer │ Config │ │ -└─────────────────────────────────────────────┘ -``` - -## 模块架构 - -### 目录结构 -``` -src/shell/ -├── mod.rs # 主模块,实现交互式shell界面 -└── command/ - ├── mod.rs # 命令框架和解析器 - ├── base.rs # 基础Unix命令实现 - ├── vm.rs # 虚拟机管理命令 - └── history.rs # 命令历史记录管理 -``` - -## 核心组件 - -### 1. 交互式Shell界面 ([shell/mod.rs](/src/shell/mod.rs)) - -#### 主要功能 -- **实时字符输入处理**: 支持逐字符读取和处理用户输入 -- **光标控制**: 支持左右箭头键移动光标位置 -- **行编辑功能**: 支持删除、插入字符等基本编辑操作 -- **历史记录导航**: 通过上下箭头键浏览命令历史 -- **转义序列处理**: 支持部分ANSI转义序列和特殊键处理 - -#### 关键特性 -```rust -const MAX_LINE_LEN: usize = 256; // 最大命令行长度 - -enum InputState { - Normal, // 正常输入状态 - Escape, // ESC键按下状态 - EscapeSeq, // 转义序列处理状态 -} -``` - -#### 支持的按键操作 -- **回车键 (CR/LF)**: 执行当前命令 -- **退格键 (BS/DEL)**: 删除光标前的字符 -- **ESC序列**: 处理箭头键和功能键 -- **上/下箭头**: 浏览命令历史 -- **左/右箭头**: 移动光标位置 - -### 2. 命令框架和解析器 ([command/mod.rs](/src/shell/command/mod.rs)) - -#### 命令树结构 -采用基于树状结构的命令系统,支持主命令和子命令的层次化组织: - -```rust -#[derive(Debug, Clone)] -pub struct CommandNode { - handler: Option, // 命令处理函数 - subcommands: BTreeMap, // 子命令映射 - description: &'static str, // 命令描述 - usage: Option<&'static str>, // 使用说明 - log_level: log::LevelFilter, // 日志级别 - options: Vec, // 命令选项 - flags: Vec, // 命令标志 -} -``` - -#### 命令解析功能 -- **智能分词**: 支持引号包围的参数和转义字符 -- **选项解析**: 支持短选项(-x)和长选项(--option) -- **参数验证**: 自动验证必需选项和参数格式 -- **错误处理**: 详细的错误信息和使用提示 -- **灵活格式**: 支持 `--option=value` 和 `--option value` 两种格式 - -#### 分词示例 - -```rust -// src/shell/command/mod.rs:186-215 -fn tokenize(input: &str) -> Vec { - // 支持引号包围的参数 - // 例: echo "hello world" -> ["echo", "hello world"] - - // 支持转义字符 - // 例: echo \"quoted\" -> ["echo", "\"quoted\""] - - // 自动处理空白符分隔 -} -``` - -#### 解析错误类型 -```rust -pub enum ParseError { - UnknownCommand(String), // 未知命令 - UnknownOption(String), // 未知选项 - MissingValue(String), // 缺少参数值 - MissingRequiredOption(String), // 缺少必需选项 - NoHandler(String), // 没有处理函数 -} -``` - -### 3. 基础Unix命令 ([command/base.rs](/src/shell/command/base.rs)) - -实现了部分Unix风格命令,包括: - -#### 文件系统操作命令 -- **ls**: 列出目录内容,支持 `-l`(详细信息) 和 `-a`(显示隐藏文件) 选项 -- **cat**: 显示文件内容,支持多文件连接输出 -- **mkdir**: 创建目录,支持 `-p`(创建父目录) 选项 -- **rm**: 删除文件和目录,支持 `-r`(递归)、`-f`(强制)、`-d`(删除空目录) 选项 -- **cp**: 复制文件和目录,支持 `-r`(递归复制) 选项 -- **mv**: 移动/重命名文件和目录 -- **touch**: 创建空文件 - -#### 系统信息命令 -- **pwd**: 显示当前工作目录 -- **cd**: 切换目录 -- **uname**: 显示系统信息,支持 `-a`(全部信息)、`-s`(内核名)、`-m`(架构) 选项 -- **echo**: 输出文本,支持 `-n`(不换行) 选项和文件重定向 - -#### 系统控制命令 -- **exit**: 退出shell,支持指定退出码 -- **log**: 控制日志级别 (off/error/warn/info/debug/trace) **有计划实现** - -#### 文件权限显示 -实现了完整的Unix风格文件权限显示: -```rust -fn file_type_to_char(ty: FileType) -> char { - match ty { - is_dir() => 'd', - is_file() => '-', - is_symlink() => 'l', - is_char_device() => 'c', - is_block_device() => 'b', - is_socket() => 's', - is_fifo() => 'p', - _ => '?' - } -} -``` - -### 4. 虚拟机管理命令 ([command/vm.rs](/src/shell/command/vm.rs)) - -提供完整的虚拟机生命周期管理功能: - -#### 主要子命令 -- **vm create**: 从配置文件创建虚拟机,支持批量创建多个VM -- **vm start**: 启动虚拟机 - - 不带参数:启动所有虚拟机 - - 指定VM ID:启动特定虚拟机 - - 支持 `--detach` 后台模式运行 - - 支持 `--console` 连接到控制台(计划实现) -- **vm stop**: 停止虚拟机 - - 必须指定VM ID - - 支持 `--force` 强制停止 - - 支持 `--graceful` 优雅关闭 -- **vm suspend**: 暂停(挂起)运行中的虚拟机 (功能不完善) - - 必须指定VM ID - - 所有VCpu将在下次VMExit时进入等待队列 - - VM状态转换为Suspended -- **vm resume**: 恢复已暂停的虚拟机 (功能不完善) - - 必须指定VM ID - - 唤醒所有VCpu任务,恢复执行 - - VM状态从Suspended转换回Running -- **vm restart**: 重启虚拟机,必须指定VM ID (功能不完善) - - 支持 `--force` 强制重启 - - 自动等待VM完全停止后再启动 -- **vm delete**: 删除虚拟机 - - 必须指定VM ID - - 需要 `--force` 确认删除 - - 支持 `--keep-data` 保留数据选项 -- **vm list**: 列出虚拟机 - - 显示所有已创建的虚拟机 - - `--format json` 支持JSON格式输出 - - 表格模式显示:ID、名称、状态、VCPU列表、内存、VCPU状态汇总 -- **vm show**: 显示虚拟机详细信息 - - 必须指定VM ID - - 默认模式:显示基本信息和摘要 - - `--full` / `-f`: 显示完整详细信息(内存区域、设备、配置等) - - `--config` / `-c`: 显示配置信息(入口点、中断模式、直通设备等) - - `--stats` / `-s`: 显示统计信息(EPT、内存区域、设备数量等) - -#### 功能特性 -``` rust -// 虚拟机状态显示 -let state = if vm.running() { - "🟢 running" -} else if vm.shutting_down() { - "🟡 stopping" -} else { - "🔴 stopped" -}; -``` - -#### 详细信息显示 -- **配置信息** (`--config`): - - BSP/AP入口点地址 - - 中断模式 (InterruptMode) - - 直通设备列表 (PassThrough Devices) - - 设备名称、GPA范围、HPA范围 - - 模拟设备列表 (Emulated Devices) -- **资源统计** (`--stats`): - - EPT根页表地址 - - 内存区域详细信息 (GPA范围、大小) - - VCPU数量和设备数量 -- **运行状态**: - - VCPU状态分布 (Free/Running/Blocked/Invalid/Created/Ready) - - CPU亲和性设置 (Physical CPU affinity mask) - - 虚拟机整体状态 (运行中/停止中/已停止) - -#### 支持的选项和标志 -- `--all` / `-a`: (vm list) 显示所有虚拟机(默认已包含所有VM) -- `--format json`: (vm list) JSON格式输出 -- `--full` / `-f`: (vm show) 显示完整详细信息 -- `--config` / `-c`: (vm show) 显示配置信息 -- `--stats` / `-s`: (vm show) 显示统计信息 -- `--force` / `-f`: (vm stop/delete/restart) 强制操作(无需确认) -- `--graceful` / `-g`: (vm stop) 优雅关闭 -- `--console` / `-c`: (vm start) 连接到控制台(计划实现) -- `--watch` / `-w`: (vm status) 实时监控(已移除,功能未实现) -- `--keep-data`: (vm delete) 保留VM数据(功能未实现) - -#### 输出格式示例 - -**Table格式** (默认): -``` -VM ID NAME STATUS VCPU MEMORY VCPU STATE ------- --------------- ------------ --------------- ---------- -------------------- -0 linux-vm Running 0,1 512MB Run:2 -1 test-vm Stopped 0 256MB Free:1 -``` - -**简化表格** (vm list 输出): -``` -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 linux-vm Running 2 512MB -1 test-vm Stopped 1 256MB -``` - -**JSON格式** (`--format json`): -``` json -{ - "vms": [ - { - "id": 0, - "name": "linux-vm", - "state": "running", - "vcpu": 2, - "memory": "512MB", - "interrupt_mode": "Emulated" - } - ] -} -``` - -### 5. 命令历史管理 ([command/history.rs](/src/shell/command/history.rs)) - -#### 核心功能 -```rust -pub struct CommandHistory { - history: Vec, // 历史命令列表 - current_index: usize, // 当前索引位置 - max_size: usize, // 最大历史记录数 -} -``` - -#### 关键特性 -- **去重处理**: 避免连续重复命令 -- **循环缓冲**: 超出最大容量时自动删除最旧记录 -- **导航功能**: 支持前进/后退浏览 -- **空命令过滤**: 自动忽略空白命令 - -#### 终端控制 -```rust -pub fn clear_line_and_redraw( - stdout: &mut dyn Write, - prompt: &str, - content: &str, - cursor_pos: usize, -) { - write!(stdout, "\r"); // 回到行首 - write!(stdout, "\x1b[2K"); // 清除整行 - write!(stdout, "{}{}", prompt, content); // 重绘内容 - // 调整光标位置 - if cursor_pos < content.len() { - write!(stdout, "\x1b[{}D", content.len() - cursor_pos); - } -} -``` - -## 内置命令 - -### 系统级内置命令 -- **help**: 显示可用命令列表 - - 列出所有顶级命令及其子命令 - - 包含内置命令和系统命令 -- **help ``**: 显示特定命令的详细帮助 - - 显示命令描述 - - 显示用法 (Usage) - - 列出所有选项 (Options) - - 列出所有标志 (Flags) - - 列出所有子命令 (Subcommands) -- **clear**: 清屏 (发送ANSI清屏序列 `\x1b[2J\x1b[H`) -- **exit/quit**: 退出shell - -### VM 管理命令列表 - -执行 `help vm` 可以看到完整的 VM 命令列表: - -``` -VM - virtual machine management - -Most commonly used vm commands: - create Create a new virtual machine - start Start a virtual machine - stop Stop a virtual machine - suspend Suspend (pause) a running virtual machine - resume Resume a suspended virtual machine - restart Restart a virtual machine - delete Delete a virtual machine - -Information commands: - list Show table of all VMs - show Show VM details (requires VM_ID) - - Default: basic information - - --full: complete detailed information - - --config: show configuration - - --stats: show statistics - -Use 'vm --help' for more information on a specific command. -``` - -### 错误处理 -Shell会对命令解析和执行错误提供友好的提示信息: -```bash -# 未知命令 -$ unknown_cmd -Error: Unknown command 'unknown_cmd' -Type 'help' to see available commands - -# 未知选项 -$ ls --invalid -Error: Unknown option '--invalid' - -# 缺少参数值 -$ vm create -Error: No VM configuration file specified -Usage: vm create [CONFIG_FILE] - -# 缺少必需选项 -$ vm stop -Error: No VM specified -Usage: vm stop [OPTIONS] -``` - -## VM 生命周期和状态管理 - -### VM 状态机 - -AxVisor 的 VM 状态遵循严格的状态机模型: - -``` - ┌──────────┐ - │ Loading │ (VM 正在创建/加载) - └────┬─────┘ - │ create complete - ▼ - ┌──────────┐ - ┌─────▶│ Loaded │ (VM 已加载,未启动) - │ └────┬─────┘ - │ │ start - │ ▼ - │ ┌──────────┐ - │ ┌───┤ Running │ (VM 正在运行) - │ │ └────┬─────┘ - │ │ │ - │ │ ├─── suspend ────▶ ┌───────────┐ - │ │ │ │ Suspended │ (VM 已暂停) - │ │ │ └─────┬─────┘ - │ │ │ │ resume - │ │ │ ◀──────────────────────┘ - │ │ │ - │ │ │ shutdown/stop - │ │ ▼ - │ │ ┌──────────┐ - │ │ │ Stopping │ (VM 正在关闭) - │ │ └────┬─────┘ - │ │ │ all vcpus exited - │ │ ▼ - │ │ ┌──────────┐ - │ └──▶│ Stopped │ (VM 已停止) - │ └────┬─────┘ - │ │ delete - │ ▼ - │ [Resources Freed] - │ │ - └───────────┘ restart -``` - -### VM 状态定义 - -```rust -pub enum VMStatus { - Loading, // VM 正在创建/加载 - Loaded, // VM 已加载但未启动 - Running, // VM 正在运行 - Suspended, // VM 已暂停(可恢复) - Stopping, // VM 正在关闭中 - Stopped, // VM 已完全停止 -} -``` - -#### 状态转换规则 - -| 当前状态 | 可执行操作 | 目标状态 | 说明 | -|---------|-----------|---------|------| -| Loading | - | Loaded | 创建完成后自动转换 | -| Loaded | `vm start` | Running | 启动 VCpu 任务开始执行 | -| Loaded | `vm delete` | Stopped | 直接删除未启动的 VM | -| Running | `vm stop` | Stopping | 发送关闭信号给所有 VCpu | -| Running | `vm suspend` | Suspended | 暂停所有 VCpu 执行 | -| Suspended | `vm resume` | Running | 恢复 VCpu 执行 | -| Suspended | `vm stop` | Stopping | 从暂停状态直接关闭 | -| Stopping | - | Stopped | 所有 VCpu 退出后自动转换 | -| Stopped | `vm delete` | [释放资源] | 清理并释放 VM 资源 | -| Stopped | `vm start` | Running | 重新启动已停止的 VM | - -### VCpu 生命周期 - -每个 VM 包含一个或多个 VCpu(虚拟 CPU),它们的生命周期与 VM 状态紧密关联: - -``` -VM Start - │ - ├─▶ 创建 VCpu 任务 (alloc_vcpu_task) - │ │ - │ ├─ 设置 CPU 亲和性 - │ ├─ 初始化 TaskExt (Weak 引用 VM) - │ └─ spawn_task 到调度器 - │ - ├─▶ VCpu 任务运行 (vcpu_run) - │ │ - │ ├─ 等待 VM Running 状态 - │ ├─ mark_vcpu_running() - │ └─ 进入运行循环 - │ │ - │ ├─ vm.run_vcpu() - 执行 Guest 代码 - │ ├─ 处理 VM Exit (hypercall, interrupt, halt...) - │ ├─ 检查 VM 暂停状态 - │ └─ 检查 VM 关闭状态 ──┐ - │ │ - │ ▼ vm.stopping() == true - ├─▶ VCpu 任务退出 │ - │ │◀───────────────────────┘ - │ ├─ mark_vcpu_exiting() - 递减运行计数 - │ ├─ 最后一个 VCpu 设置 VM 为 Stopped - │ └─ 任务函数返回,进入 Exited 状态 - │ - └─▶ VCpu 清理 (cleanup_vm_vcpus) - │ - ├─ 遍历所有 VCpu 任务 - ├─ 调用 task.join() 等待退出 - ├─ 释放 VM 的 Arc 引用 - └─ 清理等待队列资源 -``` - -#### VCpu 任务特性 - -1. **Weak 引用**:VCpu 任务通过 `TaskExt` 持有 VM 的 `Weak` 引用,避免循环引用 -2. **CPU 亲和性**:可配置 VCpu 绑定到特定物理 CPU -3. **协作式退出**:VCpu 检测到 `vm.stopping()` 后主动退出 -4. **引用计数管理**:退出前释放所有对 VM 的引用 - -#### VCpu 任务生命周期扩展 - -``` -VM Running - │ - ├─▶ VCpu 任务运行循环 - │ │ - │ ├─ vm.run_vcpu() - 执行 Guest 代码 - │ ├─ 处理 VM Exit - │ ├─ 检查 VM 状态 - │ │ │ - │ │ ├─ vm.stopping() == true ──▶ 退出循环 - │ │ │ - │ │ └─ vm.vm_status() == Suspended ──▶ 进入等待队列 - │ │ │ - │ │ │ wait for notify - │ │ │ - │ │ ▼ - │ │ 被唤醒 (resume) - │ │ │ - │ │ ◀────────────────────────────────────┘ - │ │ - │ └─ 继续执行 -``` - -### VM 删除流程详解 - -`vm delete` 命令执行完整的资源清理流程,确保没有资源泄漏: - -#### 删除流程步骤 - -``` -1. 状态检查和关闭信号 - ├─ 检查 VM 当前状态 - ├─ 如果 Running/Suspended/Stopping - │ ├─ 设置状态为 Stopping - │ └─ 调用 vm.shutdown() 通知 Guest - └─ 如果 Loaded - └─ 直接设置为 Stopped - -2. 从全局列表移除 - ├─ 调用 vm_list::remove_vm(vm_id) - ├─ 获得 VM 的 Arc 引用 - └─ 打印当前 Arc 引用计数 (调试信息) - -3. VCpu 任务清理 ⭐ (核心步骤) - ├─ 调用 cleanup_vm_vcpus(vm_id) - │ ├─ 从全局队列移除 VM 的 VCpu 列表 - │ ├─ 遍历所有 VCpu 任务 - │ │ ├─ task.join() - 阻塞等待任务退出 - │ │ └─ 释放 VCpu 持有的 VM Arc 引用 - │ └─ 清理等待队列资源 - └─ 打印清理后的 Arc 引用计数 - -4. 验证引用计数 - ├─ 期望:Arc count == 1 (仅剩当前函数持有) - ├─ 实际:检查并打印 Arc::strong_count(&vm) - └─ 如果 count > 1:警告可能的引用泄漏 - -5. 资源释放 - ├─ 函数返回时 vm (Arc) 被 drop - ├─ 如果 count == 1,触发 AxVM::drop() - │ ├─ 释放 EPT 页表 - │ ├─ 释放内存区域 - │ └─ 释放设备资源 - └─ VM 对象完全销毁 -``` - -#### 关键实现代码片段 - -```rust -// src/vmm/vcpus.rs:241-260 -pub(crate) fn cleanup_vm_vcpus(vm_id: usize) { - if let Some(vm_vcpus) = VM_VCPU_TASK_WAIT_QUEUE.remove(&vm_id) { - let task_count = vm_vcpus.vcpu_task_list.len(); - - info!("VM[{}] Joining {} VCpu tasks...", vm_id, task_count); - - // ⭐ 关键:真正 join 所有 VCpu 任务 - for (idx, task) in vm_vcpus.vcpu_task_list.iter().enumerate() { - debug!("VM[{}] Joining VCpu task[{}]: {}", vm_id, idx, task.id_name()); - if let Some(exit_code) = task.join() { - debug!("VM[{}] VCpu task[{}] exited with code: {}", vm_id, idx, exit_code); - } - } - - info!("VM[{}] VCpu resources cleaned up, {} VCpu tasks joined successfully", - vm_id, task_count); - } -} -``` - -#### 删除示例输出 - -```bash -$ vm delete 2 -Deleting stopped VM[2]... - [Debug] VM Arc strong_count: 2 -✓ VM[2] removed from VM list - Waiting for vCPU threads to exit... - [Debug] VM Arc count before cleanup: 1 - Cleaning up VCpu resources... -[ 67.812092 0:2 axvisor::vmm::vcpus:243] VM[2] Joining 1 VCpu tasks... -[ 67.819730 0:2 axvisor::vmm::vcpus:253] VM[2] VCpu resources cleaned up, 1 VCpu tasks joined successfully - [Debug] VM Arc count after final wait: 1 -✓ VM[2] deleted completely - [Debug] VM Arc strong_count: 1 - ✓ Perfect! VM will be freed immediately when function returns - VM[2] will be freed now -[ 67.848026 0:2 axvm::vm:884] Dropping VM[2] -[ 67.853407 0:2 axvm::vm:775] Cleaning up VM[2] resources... -[ 67.860698 0:2 axvm::vm:878] VM[2] resources cleanup completed -[ 67.867209 0:2 axvm::vm:889] VM[2] dropped -✓ VM[2] deletion completed -``` - -### 命令提示符 -```rust -pub fn print_prompt() { - #[cfg(feature = "fs")] - print!("axvisor:{}$ ", std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - print!("axvisor:$ "); - std::io::stdout().flush().unwrap(); -} -``` - -## 扩展性 - -### 添加新命令 - -1. 在对应的模块中实现命令处理函数 -2. 定义命令节点和选项/标志 -3. 在 `build_command_tree()` 中注册命令 - -### 命令定义示例 - -```rust -tree.insert( - "mycommand".to_string(), - CommandNode::new("My custom command") - .with_handler(my_command_handler) - .with_usage("mycommand [OPTIONS] ") - .with_option( - OptionDef::new("config", "Config file path") - .with_short('c') - .with_long("config") - .required() - ) - .with_flag( - FlagDef::new("verbose", "Verbose output") - .with_short('v') - .with_long("verbose") - ), -); -``` - -# 使用说明 - -## Shell功能特性 - -AxVisor Shell模块**默认启用**,但不同功能对features有不同要求: - -### 功能分层 - -#### 🟢 基础功能(无需额外feature) -- 交互式命令行界面 -- 命令历史记录(上下箭头导航) -- 光标移动和行编辑 -- 内置命令:`help`, `clear`, `exit` -- 系统命令:`uname`, `log` -- VM管理命令:`vm list`, `vm show`, `vm status`, `vm stop` 等 - -#### 🟡 文件系统功能(需要 `fs` feature) -- 文件操作命令:`ls`, `cat`, `mkdir`, `rm`, `cp`, `mv`, `touch`, `cd`, `pwd`, `echo` -- `vm create` - 从配置文件创建VM -- `vm /` - 从文件系统加载VM镜像启动 - -## vmconfigs 配置说明 - -`vmconfigs` 参数决定了 AxVisor 启动时是否自动创建和启动虚拟机: - -### 📌 配置行为 - -| vmconfigs 配置 | 启动行为 | 使用场景 | -|---------------|---------|---------| -| **有值**(指定配置文件)| ✅ 自动创建并启动VM | 预加载VM,启动后VM已运行 | -| **无值**(不指定)| ❌ 不创建VM,进入空Shell | 手动管理VM,通过Shell创建 | - -### 配置示例 - -#### 场景1:自动启动VM -```bash -# VM会在启动时自动创建并运行 -./axvisor.sh run \ - --plat aarch64-generic \ - --vmconfigs configs/vms/nimbos-aarch64-qemu-smp1.toml -``` - -**启动后**: -``` -Welcome to AxVisor Shell! -... -VMM starting, booting VMs... -VM[0] boot success - -axvisor:/$ vm list -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 nimbos-vm 🟢 running 1 512MB -``` - -#### 场景2:不自动启动VM(空Shell) -```bash -# 不指定 vmconfigs 参数 -./axvisor.sh run --plat aarch64-generic --features fs,ept-level-4 -``` - -**启动后**(需要手动创建VM): -``` -Welcome to AxVisor Shell! -... - -axvisor:/$ vm list -No virtual machines found. - -axvisor:/$ vm create /path/to/vm.toml -✓ Successfully created VM from config: /path/to/vm.toml - -axvisor:/$ vm start 0 -✓ VM[0] started successfully -``` - -### 配置方式 - -#### 命令行指定 -```bash -./axvisor.sh run --vmconfigs configs/vms/vm1.toml,configs/vms/vm2.toml -``` - -#### 配置文件指定 -在 `.hvconfig.toml` 中: -```toml -vmconfigs = [ - "configs/vms/nimbos-aarch64-qemu-smp1.toml", - "configs/vms/linux-aarch64-qemu.toml" -] -``` - -### 💡 使用建议 - -| 使用场景 | 推荐配置 | -|---------|---------| -| **生产环境** - 固定的VM配置 | 指定 `vmconfigs`,自动启动 | -| **开发调试** - 频繁修改VM配置 | 不指定 `vmconfigs`,Shell中手动创建 | -| **演示测试** - 需要快速启动 | 指定 `vmconfigs`,自动启动 | -| **交互式管理** - 动态创建多个VM | 不指定或只指定部分,其余手动创建 | - -## 启用方式 - -### 方式一:自动启动VM(指定 vmconfigs) - -指定 `--vmconfigs` 参数,AxVisor 会在启动时自动创建并启动虚拟机: - -```bash -# VM会自动启动 -./axvisor.sh run \ - --plat aarch64-generic \ - --vmconfigs configs/vms/nimbos-aarch64-qemu-smp1.toml -``` - -**启动后状态**: -- ✅ VM已创建并运行 -- ✅ Shell可直接管理VM -- ✅ 可执行 `vm list`, `vm status` 等命令 - -**可用功能**: -- VM状态查询和管理 -- 系统信息查看 -- 命令历史和行编辑 -- 日志级别控制 - -**不可用功能**(无 `fs` feature时): -- 文件操作命令 -- 从文件系统动态创建新VM - -### 方式二:空Shell模式(不指定 vmconfigs) - -不指定 `--vmconfigs`,AxVisor 启动后不会创建VM,提供纯净的Shell环境: - -```bash -# 启动时不创建VM,需要启用fs以便手动创建 -./axvisor.sh run --plat aarch64-generic --features fs,ept-level-4 -``` - -**启动后状态**: -- ❌ 无VM运行 -- ✅ Shell就绪,等待用户操作 -- ✅ 可通过 `vm create` 手动创建VM - -**使用场景**: -- 需要在Shell中动态创建多个VM -- 测试不同的VM配置 -- 交互式VM管理 - -### 方式三:完整Shell功能(带文件系统 + vmconfigs) - -结合文件系统和 vmconfigs,既可以自动启动预定义的VM,又可以使用文件操作和动态创建VM: - -#### 步骤1:准备磁盘镜像 - -```bash -# 创建磁盘镜像(以FAT32为例) -dd if=/dev/zero of=disk.img bs=1M count=64 -mkfs.vfat disk.img - -# 挂载并放入VM配置文件 -mkdir -p mnt -sudo mount disk.img mnt -sudo cp configs/vms/*.toml mnt/ -sudo umount mnt -``` - -#### 步骤2:运行AxVisor(完整功能) - -```bash -# 同时启用文件系统和自动启动VM -./axvisor.sh run \ - --plat aarch64-generic \ - --vmconfigs configs/vms/nimbos-aarch64-qemu-smp1.toml \ - --features fs,ept-level-4 \ - --arceos-args "BUS=mmio,BLK=y,DISK_IMG=disk.img,MEM=8g,LOG=info" -``` - -**启动后状态**: -- ✅ VM已自动创建并运行 -- ✅ 文件系统已挂载 -- ✅ 可执行所有Shell命令 -- ✅ 可从文件系统创建更多VM - -**完整功能**: -``` bash -axvisor:/$ vm list # 查看已启动的VM -axvisor:/$ ls -la # 浏览文件系统 -axvisor:/$ cat /vm2.toml # 查看其他配置文件 -axvisor:/$ vm create /vm2.toml # 创建更多VM -``` - -#### 文件系统类型选择 - -ArceOS 默认使用 **FAT32** 文件系统。如需使用其他文件系统,可通过 ArceOS 的构建参数指定: - -```bash -# 使用EXT4文件系统(需要创建ext4格式的磁盘镜像) -./axvisor.sh run \ - --plat aarch64-generic \ - --vmconfigs configs/vms/nimbos-aarch64-qemu-smp1.toml \ - --features fs,ept-level-4 \ - --arceos-features ext4fs \ - --arceos-args "BUS=mmio,BLK=y,DISK_IMG=disk-ext4.img,MEM=8g" -``` - -## 实际使用示例 - -### 示例1:NimbOS客户机(自动启动) - -使用 `--vmconfigs` 让 NimbOS 在启动时自动运行: - -```bash -# 1. 准备NimbOS镜像 -./scripts/nimbos.sh --arch aarch64 - -# 2. 启动AxVisor(VM会自动启动) -./axvisor.sh run \ - --plat aarch64-generic \ - --features fs,ept-level-4 \ - --vmconfigs configs/vms/nimbos-aarch64-qemu-smp1.toml \ - --arceos-args "BUS=mmio,BLK=y,DISK_IMG=tmp/nimbos-aarch64.img,LOG=info" - -# 3. 在Shell中操作(VM已运行) -# 查看VM状态 -axvisor:/$ vm list -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 nimbos-vm 🟢 running 1 512MB - -axvisor:/$ vm status 0 # 查看详细状态 -axvisor:/$ log debug # 调整日志级别 -``` - -### 示例2:交互式创建VM(手动管理) - -不使用 `--vmconfigs`,在Shell中手动创建和管理VM: - -```bash -# 1. 准备镜像和配置文件 -./scripts/nimbos.sh --arch aarch64 - -# 2. 启动AxVisor(不指定vmconfigs,不自动启动VM) -./axvisor.sh run \ - --plat aarch64-generic \ - --features fs,ept-level-4 \ - --arceos-args "BUS=mmio,BLK=y,DISK_IMG=tmp/nimbos-aarch64.img,LOG=info" - -# 3. 在Shell中手动创建和启动VM -axvisor:/$ vm list -No virtual machines found. - -axvisor:/$ ls / # 浏览文件系统 -nimbos-aarch64-qemu-smp1.toml -... - -axvisor:/$ vm create /nimbos-aarch64-qemu-smp1.toml -✓ Successfully created VM from config - -axvisor:/$ vm list -a -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 nimbos-vm 🔴 stopped 1 512MB - -axvisor:/$ vm start 0 -✓ VM[0] started successfully - -axvisor:/$ vm list -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 nimbos-vm 🟢 running 1 512MB -``` - -### 示例3:混合模式(部分自动,部分手动) - -自动启动一个VM,再手动创建更多: - -```bash -# 启动AxVisor,自动启动第一个VM -./axvisor.sh run \ - --plat aarch64-generic \ - --features fs,ept-level-4 \ - --vmconfigs configs/vms/vm1.toml \ - --arceos-args "BUS=mmio,BLK=y,DISK_IMG=disk.img,LOG=info" - -# Shell中查看和创建更多VM -axvisor:/$ vm list -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 vm1 🟢 running 2 1024MB - -axvisor:/$ vm create /configs/vm2.toml -✓ Successfully created VM from config - -axvisor:/$ vm start 1 -✓ VM[1] started successfully - -axvisor:/$ vm list -ID NAME STATE VCPU MEMORY ----- ----------- ------- ---- ------ -0 vm1 🟢 running 2 1024MB -1 vm2 🟢 running 1 512MB -``` - -### 代码层面说明 - -Shell模块在代码中的启用方式: - -```rust -// src/main.rs -fn main() { - // ... 初始化代码 ... - - // Shell总是被调用,无条件编译 - shell::console_init(); -} -``` - -```rust -// src/shell/command/base.rs -// 文件系统相关命令通过条件编译控制 -#[cfg(feature = "fs")] -fn do_ls(cmd: &ParsedCommand) { /* ... */ } - -#[cfg(feature = "fs")] -fn do_cat(cmd: &ParsedCommand) { /* ... */ } - -// 这些命令在构建命令树时也受条件编译控制 -pub fn build_base_cmd(tree: &mut BTreeMap) { - #[cfg(feature = "fs")] - tree.insert("ls".to_string(), /* ... */); - - #[cfg(feature = "fs")] - tree.insert("cat".to_string(), /* ... */); - - // 非文件系统命令始终可用 - tree.insert("uname".to_string(), /* ... */); - tree.insert("log".to_string(), /* ... */); -} -``` - -这种设计使得: -1. **Shell界面始终可用** - 提供基本的交互和VM管理能力 -2. **文件系统功能可选** - 仅在需要时启用,减少依赖 -3. **灵活的部署方式** - 支持从内存或文件系统加载VM - -## 快速开始 - -启动AxVisor后会自动进入Shell界面: -``` -Welcome to AxVisor Shell! -Type 'help' to see available commands -Use UP/DOWN arrows to navigate command history - -axvisor:/$ -``` - -### 基本操作 -- `help` - 查看所有命令 -- `help ` - 查看特定命令帮助 -- `clear` - 清屏 -- `exit` - 退出 - -### 键盘快捷键 -- **上/下箭头**: 浏览命令历史 -- **左/右箭头**: 移动光标 -- **退格键**: 删除字符 - -## 常用命令 - -### 文件操作 -```bash -ls -la # 列出文件(详细信息+隐藏文件) -cat file.txt # 查看文件内容 -mkdir -p dir/subdir # 创建目录 -cp -r source dest # 复制文件/目录 -mv old new # 移动/重命名 -rm -rf path # 删除文件/目录 -touch file.txt # 创建空文件 -``` - -### 虚拟机管理 -```bash -vm list # 列出所有虚拟机 -vm list --format json # JSON格式输出 -vm create config.toml # 创建虚拟机 -vm create vm1.toml vm2.toml # 批量创建虚拟机 -vm start # 启动所有虚拟机 -vm start 1 # 启动VM(ID=1) -vm start -d 1 # 后台启动VM -vm stop -f 1 # 强制停止VM -vm suspend 1 # 暂停VM(ID=1) -vm resume 1 # 恢复暂停的VM -vm restart 1 # 重启VM -vm restart -f 1 # 强制重启VM -vm delete -f 1 # 删除VM(需要确认) -vm status # 显示所有VM状态概览(已移除) -vm status 1 # 查看特定VM状态(已移除) -vm show 1 # 查看VM基本信息 -vm show -f 1 # 查看VM完整详细信息 -vm show -c 1 # 查看VM配置 -vm show -s 1 # 查看VM统计信息 -vm show -c -s 1 # 查看VM配置和统计信息 -``` - -### 系统信息 -```bash -pwd # 当前目录 -uname -a # 系统信息 -``` - -## 典型工作流 - -### 单虚拟机场景 -```bash -# 1. 检查环境 -ls -la -pwd - -# 2. 创建虚拟机 -vm create linux.toml - -# 3. 启动虚拟机 -vm start 1 - -# 4. 监控状态 -vm status 1 -vm show -c -s 1 # 查看详细配置和统计 - -# 5. 停止虚拟机 -vm stop 1 -``` - -### 多虚拟机场景 -```bash -# 1. 批量创建虚拟机 -vm create vm1.toml vm2.toml vm3.toml - -# 2. 查看所有虚拟机 -vm list -a - -# 3. 启动所有虚拟机 -vm start - -# 4. 查看整体状态 -vm status # 显示所有VM的状态概览 - -# 5. 停止特定虚拟机 -vm stop 2 - -# 6. 重启虚拟机 -vm restart 1 - -# 7. 删除虚拟机 -vm delete -f 3 -``` - -更多详细信息请使用 `help ` 查看具体命令的使用方法。 diff --git a/os/axvisor/doc/task.py-usage.md b/os/axvisor/doc/task.py-usage.md deleted file mode 100644 index 5aab2b5fa..000000000 --- a/os/axvisor/doc/task.py-usage.md +++ /dev/null @@ -1,271 +0,0 @@ -# task.py 使用说明 - -## 概述 - -`task.py` 是 Axvisor 项目的主要命令行工具,提供了项目的构建、运行和设置功能。它是一个统一的入口点,简化了开发和部署流程。 - -## 基本用法 - -```bash -# 激活虚拟环境 -source ./activate.sh - -./task.py [options] -``` - -## 可用命令 - -### 1. build - 构建项目 - -./scripts/task.py [options] -构建 Axvisor 项目。 - -```bash -./task.py build [options] -``` - -**功能**: - -- 自动设置 ArceOS 依赖(如果尚未设置) -./scripts/task.py build [options] -- 执行 make 构建 - -**示例**: - -```bash -# 使用 .hvconfig.toml 中的配置构建 -./scripts/task.py build - -# 指定平台构建 -./scripts/task.py build --plat aarch64-generic - -# 添加特性 -./scripts/task.py build --features "feature1,feature2" - -# 添加 ArceOS 特性 -./scripts/task.py build --arceos-features "page-alloc-64g,smp" - -# 添加 ArceOS Makefile 参数 -./scripts/task.py build --arceos-args "NET=y,BLK=y,MEM=8g,LOG=debug" - -# 指定 VM 配置文件 -./scripts/task.py build --vmconfigs "config1.toml,config2.toml" -``` - -### 3. run - 运行项目 - -构建并运行 Axvisor 项目。 - -```bash -./scripts/task.py run [options] -``` - -**功能**: - -- 首先执行构建步骤 -- 如果构建成功,则运行项目 -./scripts/task.py run - -**示例**: -./scripts/task.py run --plat aarch64-generic --arceos-args "MEM=4g,BUS=mmio,BLK=y,LOG=debug" --features "fs" - -```bash -# 使用 .hvconfig.toml 中的配置运行 -./task.py run - -# 使用特定配置运行,执行后会根据参数生成 .hvconfig.toml -./task.py run --plat aarch64-generic --arceos-args "MEM=4g,BUS=mmio,BLK=y,LOG=debug" --features "fs" -``` - -./scripts/task.py run --arceos-args "QEMU_ARGS=\"-smp 4 -m 2G -netdev user,id=net0\"" - -### 通用参数 - -以下参数适用于 `build` 和 `run` 命令: - -| 参数 | 类型 | 默认值 | 说明 | -|------|------|--------|------| -| `--plat` | string | aarch64-generic | 指定目标平台 | -| `--arch` | string | 自动检测 | 指定目标架构 | -| `--package` | string | 自动检测 | 指定平台包名 | -cp .hvconfig.dev.toml .hvconfig.toml && ./scripts/task.py build -cp .hvconfig.prod.toml .hvconfig.toml && ./scripts/task.py run -| `--features` | string | 无 | Hypervisor 特性(逗号分隔) | -| `--arceos-features` | string | 无 | ArceOS 特性(逗号分隔) | -| `--arceos-args` | string | 无 | ArceOS 参数(逗号分隔) | -| `--vmconfigs` | string | 无 | VM 配置文件路径(逗号分隔) | - -### 参数详解 - -#### --plat (平台) - -指定目标平台,系统会自动从 `platform/{plat}/axconfig.toml` 读取对应的架构和包配置。 - -```bash ---plat aarch64-generic ---plat x86-qemu-q35 -``` - -**架构特定的 QEMU 参数**: - -- `aarch64`: `-machine virtualization=on,gic-version=3` -- `x86_64`: `-enable-kvm -cpu host` -- `riscv64`: `-machine virt -cpu rv64` - -#### --features (Hypervisor 特性) - -指定 Hypervisor 的特性,多个特性用逗号分隔。 - -```bash ---features "fs" -``` - -#### --arceos-features (ArceOS 特性) - -指定 ArceOS 的特性,多个特性用逗号分隔。 - -```bash ---arceos-features "page-alloc-64g" ---arceos-features "smp,net,blk" -``` - -#### --arceos-args (ArceOS 参数) - -指定传递给 ArceOS 的参数,支持键值对和标志,多个参数用逗号分隔。 - -```bash ---arceos-args "NET=y,BLK=y,MEM=8g" ---arceos-args "SMP=4,LOG=debug" -``` - -#### --vmconfigs (VM 配置文件) - -指定 VM 配置文件的路径,多个文件用逗号分隔。 - -```bash ---vmconfigs "vm1.toml" ---vmconfigs "vm1.toml,vm2.toml,vm3.toml" -``` - -## 配置文件 - -`task.py` 支持通过 `.hvconfig.toml` 配置文件设置默认参数,命令行参数会覆盖配置文件中的设置。 - -### 配置文件示例 - -创建 `.hvconfig.toml` 文件: - -```toml -plat = "aarch64-generic" -arceos_args = [ "BUS=mmio", "BLK=y", "MEM=8g", "LOG=debug", "QEMU_ARGS=\"-machine gic-version=3\""] -vmconfigs = [ "tmp/arceos-aarch64.toml",] -``` - -### 配置优先级 - -1. **命令行参数** (最高优先级) -2. **配置文件** (.hvconfig.toml) - -## 高级用法 - -### 1. 自定义 QEMU 参数 - -```bash -# 添加自定义 QEMU 参数,会与架构特定参数合并 -./task.py run --arceos-args "QEMU_ARGS=\"-smp 4 -m 2G -netdev user,id=net0\"" - -# 对于 aarch64,最终的 QEMU_ARGS 会是: -# "-smp 4 -m 2G -netdev user,id=net0 -machine virtualization=on" -``` - -### 4. 批量配置 - -创建多个配置文件用于不同的开发场景: - -```bash -# 开发配置 -cp .hvconfig.toml .hvconfig.dev.toml -# 编辑 .hvconfig.dev.toml 添加调试参数 - -# 生产配置 -cp .hvconfig.toml .hvconfig.prod.toml -# 编辑 .hvconfig.prod.toml 优化性能参数 - -# 使用不同配置 -cp .hvconfig.dev.toml .hvconfig.toml && ./task.py build -cp .hvconfig.prod.toml .hvconfig.toml && ./task.py run -``` - -## 故障排除 - -### 常见问题 - -1. **构建失败** - - ```bash - # 清理并重新设置 - rm -rf .arceos - ./task.py clean - ./task.py build - ``` - -2. **平台配置找不到** - - ```text - 警告:平台配置文件 platform/xxx/axconfig.toml 不存在 - ``` - - - 检查平台名称是否正确 - - 确保对应的平台目录存在 - -3. **TOML 库缺失** - - ```text - 警告:需要安装 toml 库来读取配置文件 - ``` - - ```bash - pip install toml - ``` - -4. **权限问题** - - ```bash - # 确保 task.py 有执行权限 - chmod +x task.py - ``` - -### 调试技巧 - -1. **查看生成的构建命令** - - ```python - from scripts.config import AxvisorConfig - config = AxvisorConfig() - print(config.format_make_command("build")) - ``` - -2. **检查配置合并结果** - - ```python - from scripts.config import AxvisorConfig - config = AxvisorConfig(plat="aarch64-generic") - print(f"Platform: {config.plat}") - print(f"Architecture: {config.arch}") - print(f"Package: {config.package}") - print(f"Make vars: {config.get_make_variables()}") - ``` - -## 开发扩展 - -如果需要添加新的命令或功能: - -1. **添加新命令**: - - 在 `scripts/` 目录下创建新的 Python 模块 - - 在 `task.py` 中添加对应的子解析器 - - 实现 `main(args)` 函数 - -2. **添加新参数**: - - 修改 `scripts/config.py` 中的 `add_common_arguments` 函数 - - 更新 `AxvisorConfig` 类的字段 - - 更新相关的处理逻辑 diff --git a/os/axvisor/platform/riscv64-qemu-virt/Cargo.toml b/os/axvisor/platform/riscv64-qemu-virt/Cargo.toml deleted file mode 100644 index 869095bea..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -edition = "2024" -name = "axplat-riscv64-qemu-virt" -version = "0.3.0" - -[features] -default = [ - "irq", - "smp", -] -fp-simd = ["axcpu/fp-simd"] -irq = ["axplat/irq"] -rtc = ["riscv_goldfish"] -smp = ["axplat/smp"] - -[dependencies] -kspin = "0.1" -lazyinit = "0.2" -log = "0.4" -riscv = "0.14.0" -riscv_goldfish = { version = "0.1", optional = true } -riscv_plic = "0.2" -sbi-rt = { version = "0.0.3", features = ["legacy"] } -uart_16550 = "0.4.0" - -axconfig-macros = "0.2" -axcpu = { version = "0.3.0-preview.8", features = ["arm-el2"] } -axplat = "0.3.1-pre.6" -axvisor_api = "0.3" -crate_interface = "0.3" - -[package.metadata.docs.rs] -targets = ["riscv64gc-unknown-none-elf"] diff --git a/os/axvisor/platform/riscv64-qemu-virt/axconfig.toml b/os/axvisor/platform/riscv64-qemu-virt/axconfig.toml deleted file mode 100644 index 581a4a741..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/axconfig.toml +++ /dev/null @@ -1,105 +0,0 @@ -# Architecture identifier. -arch = "riscv64" # str -# Platform identifier. -platform = "riscv64-qemu-virt" # str -# Platform package. -package = "axplat-riscv64-qemu-virt" # str - -# -# Platform configs -# -[plat] -# Number of CPUs. -cpu-num = 4 # uint -# Base address of the whole physical memory. -phys-memory-base = 0x8000_0000 # uint -# Size of the whole physical memory. (8GB) -phys-memory-size = 0x1_0000_0000 # uint -# Base physical address of the kernel image. -kernel-base-paddr = 0x8020_0000 # uint -# Base virtual address of the kernel image. -kernel-base-vaddr = "0xffff_ffc0_8020_0000" # uint -# Linear mapping offset, for quick conversions between physical and virtual -# addresses. -phys-virt-offset = "0xffff_ffc0_0000_0000" # uint -# Offset of bus address and phys address. some boards, the bus address is -# different from the physical address. -phys-bus-offset = 0 # uint -# Kernel address space base. -kernel-aspace-base = "0xffff_ffc0_0000_0000" # uint -# Kernel address space size. -kernel-aspace-size = "0x0000_003f_ffff_f000" # uint -# Stack size on bootstrapping. (256K) -boot-stack-size = 0x40000 # uint - -# -# Device specifications -# -[devices] -# MMIO ranges with format (`base_paddr`, `size`). -mmio-ranges = [ - [0x0010_1000, 0x1000], # RTC - [0x0c00_0000, 0x21_0000], # PLIC - [0x1000_0000, 0x1000], # UART - [0x1000_1000, 0x8000], # VirtIO - [0x3000_0000, 0x1000_0000], # PCI config space - [0x4000_0000, 0x4000_0000], # PCI memory ranges (ranges 1: 32-bit MMIO space) - [0x2_7fe0_0000, 0x1000_0000] -] # [(uint, uint)] -# VirtIO MMIO ranges with format (`base_paddr`, `size`). -virtio-mmio-ranges = [ - [0x1000_1000, 0x1000], - [0x1000_2000, 0x1000], - [0x1000_3000, 0x1000], - [0x1000_4000, 0x1000], - [0x1000_5000, 0x1000], - [0x1000_6000, 0x1000], - [0x1000_7000, 0x1000], - [0x1000_8000, 0x1000], -] # [(uint, uint)] -# Base physical address of the PCIe ECAM space. -pci-ecam-base = 0x3000_0000 # uint -# End PCI bus number (`bus-range` property in device tree). -pci-bus-end = 0xff # uint -# PCI device memory ranges (`ranges` property in device tree). -pci-ranges = [ - [0x0300_0000, 0x1_0000], # PIO space - [0x4000_0000, 0x4000_0000], # 32-bit MMIO space - [0x4_0000_0000, 0x4_0000_0000], # 64-bit MMIO space -] # [(uint, uint)] - -# Timer interrupt frequency in Hz. -timer-frequency = 10_000_000 # uint -# Timer interrupt num. -timer-irq = "0x8000_0000_0000_0005" # uint -# IPI interrupt num -ipi-irq = "0x8000_0000_0000_0001" # uint - -# rtc@101000 { -# interrupts = <0x0b>; -# interrupt-parent = <0x03>; -# reg = <0x00 0x101000 0x00 0x1000>; -# compatible = "google,goldfish-rtc"; -# }; -# RTC (goldfish) Address -rtc-paddr = 0x10_1000 # uint - -# plic@c000000 { -# phandle = <0x03>; -# riscv,ndev = <0x5f>; -# reg = <0x00 0xc000000 0x00 0x600000>; -# interrupts-extended = <0x02 0x0b 0x02 0x09>; -# interrupt-controller; -# compatible = "sifive,plic-1.0.0\0riscv,plic0"; -# }; -plic-paddr = 0x0c00_0000 # uint - -# serial@10000000 { -# interrupts = <0x0a>; -# interrupt-parent = <0x03>; -# clock-frequency = "\08@"; -# reg = <0x00 0x10000000 0x00 0x100>; -# compatible = "ns16550a"; -# }; -uart-paddr = 0x1000_0000 # uint -uart-irq = 0x0a # uint \ No newline at end of file diff --git a/os/axvisor/platform/riscv64-qemu-virt/build.rs b/os/axvisor/platform/riscv64-qemu-virt/build.rs deleted file mode 100644 index 80a4c120e..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -fn main() { - println!("cargo:rerun-if-env-changed=AXVISOR_SMP"); - println!("cargo:rerun-if-changed=linker.lds.S"); - - let mut smp = 1; - if let Ok(s) = std::env::var("AXVISOR_SMP") { - smp = s.parse::().unwrap_or(1); - } - - let ld_content = include_str!("linker.lds.S"); - let ld_content = ld_content.replace("%ARCH%", "riscv"); - let ld_content = ld_content.replace( - "%KERNEL_BASE%", - &format!("{:#x}", 0xffff_ffc0_8020_0000usize), - ); - let ld_content = ld_content.replace("%SMP%", &format!("{smp}",)); - - // target///build/axvisor-xxxx/out - let out_dir = std::env::var("OUT_DIR").unwrap(); - let out_path = std::path::Path::new(&out_dir).join("link.x"); - println!("cargo:rustc-link-search={out_dir}"); - std::fs::write(out_path, ld_content).unwrap(); -} diff --git a/os/axvisor/platform/riscv64-qemu-virt/linker.lds.S b/os/axvisor/platform/riscv64-qemu-virt/linker.lds.S deleted file mode 100644 index 439284c71..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/linker.lds.S +++ /dev/null @@ -1,99 +0,0 @@ -OUTPUT_ARCH(%ARCH%) - -BASE_ADDRESS = %KERNEL_BASE%; -SMP = %SMP%; - -ENTRY(_start) -SECTIONS -{ - . = BASE_ADDRESS; - _skernel = .; - - .text : ALIGN(4K) { - _stext = .; - *(.text.boot) - *(.text .text.*) - . = ALIGN(4K); - _etext = .; - } - - .rodata : ALIGN(4K) { - _srodata = .; - *(.rodata .rodata.*) - *(.srodata .srodata.*) - *(.sdata2 .sdata2.*) - . = ALIGN(4K); - _erodata = .; - } - - .data : ALIGN(4K) { - _sdata = .; - *(.data.boot_page_table) - . = ALIGN(4K); - __sdriver_register = .; - KEEP(*(.driver.register*)) - __edriver_register = .; - - *(.data .data.*) - *(.sdata .sdata.*) - *(.got .got.*) - } - - .tdata : ALIGN(0x10) { - _stdata = .; - *(.tdata .tdata.*) - _etdata = .; - } - - .tbss : ALIGN(0x10) { - _stbss = .; - *(.tbss .tbss.*) - *(.tcommon) - _etbss = .; - } - - . = ALIGN(4K); - _percpu_start = .; - _percpu_end = _percpu_start + SIZEOF(.percpu); - .percpu 0x0 : AT(_percpu_start) { - _percpu_load_start = .; - *(.percpu .percpu.*) - _percpu_load_end = .; - . = _percpu_load_start + ALIGN(64) * SMP; - } - . = _percpu_end; - - . = ALIGN(4K); - _edata = .; - - .bss : ALIGN(4K) { - boot_stack = .; - *(.bss.stack) - . = ALIGN(4K); - boot_stack_top = .; - - _sbss = .; - *(.bss .bss.*) - *(.sbss .sbss.*) - *(COMMON) - . = ALIGN(4K); - _ebss = .; - } - - _ekernel = .; - - /DISCARD/ : { - *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) - } -} - -SECTIONS { - linkme_IRQ : { *(linkme_IRQ) } - linkm2_IRQ : { *(linkm2_IRQ) } - linkme_PAGE_FAULT : { *(linkme_PAGE_FAULT) } - linkm2_PAGE_FAULT : { *(linkm2_PAGE_FAULT) } - linkme_SYSCALL : { *(linkme_SYSCALL) } - linkm2_SYSCALL : { *(linkm2_SYSCALL) } - axns_resource : { *(axns_resource) } -} -INSERT AFTER .tbss; diff --git a/os/axvisor/platform/riscv64-qemu-virt/src/boot.rs b/os/axvisor/platform/riscv64-qemu-virt/src/boot.rs deleted file mode 100644 index e3a690a82..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/src/boot.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::config::plat::{BOOT_STACK_SIZE, PHYS_VIRT_OFFSET}; -use axplat::mem::{Aligned4K, pa}; - -#[unsafe(link_section = ".bss.stack")] -static mut BOOT_STACK: [u8; BOOT_STACK_SIZE] = [0; BOOT_STACK_SIZE]; - -#[unsafe(link_section = ".data")] -static mut BOOT_PT_SV39: Aligned4K<[u64; 512]> = Aligned4K::new([0; 512]); - -#[allow(clippy::identity_op)] // (0x0 << 10) here makes sense because it's an address -unsafe fn init_boot_page_table() { - unsafe { - // 0x0000_0000..0x4000_0000, VRWX_GAD, 1G block - BOOT_PT_SV39[0] = (0x0 << 10) | 0xef; - // 0x8000_0000..0xc000_0000, VRWX_GAD, 4G block - BOOT_PT_SV39[2] = (0x80000 << 10) | 0xef; - BOOT_PT_SV39[3] = (0xC0000 << 10) | 0xef; - BOOT_PT_SV39[4] = (0x100000 << 10) | 0xef; - BOOT_PT_SV39[5] = (0x140000 << 10) | 0xef; - // 0xffff_ffc0_0000_0000..0xffff_ffc0_4000_0000, VRWX_GAD, 1G block - BOOT_PT_SV39[0x100] = (0x0 << 10) | 0xef; - // 0xffff_ffc0_8000_0000..0xffff_ffc0_c000_0000, VRWX_GAD, 1G block - BOOT_PT_SV39[0x102] = (0x80000 << 10) | 0xef; - BOOT_PT_SV39[0x103] = (0xC0000 << 10) | 0xef; - BOOT_PT_SV39[0x104] = (0x100000 << 10) | 0xef; - BOOT_PT_SV39[0x105] = (0x140000 << 10) | 0xef; - } -} - -unsafe fn init_mmu() { - unsafe { - axcpu::asm::write_kernel_page_table(pa!(&raw const BOOT_PT_SV39 as usize)); - axcpu::asm::flush_tlb(None); - } -} - -/// The earliest entry point for the primary CPU. -#[unsafe(naked)] -#[unsafe(no_mangle)] -#[unsafe(link_section = ".text.boot")] -unsafe extern "C" fn _start() -> ! { - // PC = 0x8020_0000 - // a0 = hartid - // a1 = dtb - core::arch::naked_asm!(" - mv s0, a0 // save hartid - mv s1, a1 // save DTB pointer - la sp, {boot_stack} - li t0, {boot_stack_size} - add sp, sp, t0 // setup boot stack - - call {init_boot_page_table} - call {init_mmu} // setup boot page table and enabel MMU - - li s2, {phys_virt_offset} // fix up virtual high address - add sp, sp, s2 - - mv a0, s0 - mv a1, s1 - la a2, {entry} - add a2, a2, s2 - jalr a2 // call_main(cpu_id, dtb) - j .", - phys_virt_offset = const PHYS_VIRT_OFFSET, - boot_stack_size = const BOOT_STACK_SIZE, - boot_stack = sym BOOT_STACK, - init_boot_page_table = sym init_boot_page_table, - init_mmu = sym init_mmu, - entry = sym axplat::call_main, - ) -} - -/// The earliest entry point for secondary CPUs. -#[cfg(feature = "smp")] -#[unsafe(naked)] -pub(crate) unsafe extern fn _start_secondary() -> ! { - // a0 = hartid - // a1 = SP - core::arch::naked_asm!(" - mv s0, a0 // save hartid - mv sp, a1 // set SP - - call {init_mmu} // setup boot page table and enabel MMU - - li s1, {phys_virt_offset} // fix up virtual high address - add a1, a1, s1 - add sp, sp, s1 - - mv a0, s0 - la a1, {entry} - add a1, a1, s1 - jalr a1 // call_secondary_main(cpu_id) - j .", - phys_virt_offset = const PHYS_VIRT_OFFSET, - init_mmu = sym init_mmu, - entry = sym axplat::call_secondary_main, - ) -} diff --git a/os/axvisor/platform/riscv64-qemu-virt/src/console.rs b/os/axvisor/platform/riscv64-qemu-virt/src/console.rs deleted file mode 100644 index 037f5cc6a..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/src/console.rs +++ /dev/null @@ -1,54 +0,0 @@ -use axplat::console::ConsoleIf; -use kspin::SpinNoIrq; -use lazyinit::LazyInit; -use uart_16550::MmioSerialPort; - -use crate::config::{devices::UART_PADDR, plat::PHYS_VIRT_OFFSET}; - -static UART: LazyInit> = LazyInit::new(); - -pub(crate) fn init_early() { - UART.init_once({ - let mut uart = unsafe { MmioSerialPort::new(UART_PADDR + PHYS_VIRT_OFFSET) }; - uart.init(); - SpinNoIrq::new(uart) - }); -} - -struct ConsoleIfImpl; - -#[impl_plat_interface] -impl ConsoleIf for ConsoleIfImpl { - /// Writes bytes to the console from input u8 slice. - fn write_bytes(bytes: &[u8]) { - for &c in bytes { - let mut uart = UART.lock(); - match c { - b'\n' => { - uart.send_raw(b'\r'); - uart.send_raw(b'\n'); - } - c => uart.send_raw(c), - } - } - } - - /// Reads bytes from the console into the given mutable slice. - /// Returns the number of bytes read. - fn read_bytes(bytes: &mut [u8]) -> usize { - let mut uart = UART.lock(); - for (i, byte) in bytes.iter_mut().enumerate() { - match uart.try_receive() { - Ok(c) => *byte = c, - Err(_) => return i, - } - } - bytes.len() - } - - /// Returns the IRQ number for the console, if applicable. - #[cfg(feature = "irq")] - fn irq_num() -> Option { - Some(crate::config::devices::UART_IRQ) - } -} diff --git a/os/axvisor/platform/riscv64-qemu-virt/src/init.rs b/os/axvisor/platform/riscv64-qemu-virt/src/init.rs deleted file mode 100644 index f90db4e90..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/src/init.rs +++ /dev/null @@ -1,40 +0,0 @@ -use axplat::init::InitIf; - -struct InitIfImpl; - -#[impl_plat_interface] -impl InitIf for InitIfImpl { - /// This function should be called immediately after the kernel has booted, - /// and performed earliest platform configuration and initialization (e.g., - /// early console, clocking). - fn init_early(_cpu_id: usize, _mbi: usize) { - axcpu::init::init_trap(); - crate::console::init_early(); - crate::time::init_early(); - } - - /// Initializes the platform at the early stage for secondary cores. - #[cfg(feature = "smp")] - fn init_early_secondary(_cpu_id: usize) { - axcpu::init::init_trap(); - } - - /// Initializes the platform at the later stage for the primary core. - /// - /// This function should be called after the kernel has done part of its - /// initialization (e.g, logging, memory management), and finalized the rest of - /// platform configuration and initialization. - fn init_later(_cpu_id: usize, _arg: usize) { - #[cfg(feature = "irq")] - crate::irq::init_percpu(); - crate::time::init_percpu(); - } - - /// Initializes the platform at the later stage for secondary cores. - #[cfg(feature = "smp")] - fn init_later_secondary(_cpu_id: usize) { - #[cfg(feature = "irq")] - crate::irq::init_percpu(); - crate::time::init_percpu(); - } -} diff --git a/os/axvisor/platform/riscv64-qemu-virt/src/irq.rs b/os/axvisor/platform/riscv64-qemu-virt/src/irq.rs deleted file mode 100644 index 6e06037dc..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/src/irq.rs +++ /dev/null @@ -1,264 +0,0 @@ -use core::{ - num::NonZeroU32, - ptr::NonNull, - sync::atomic::{AtomicPtr, Ordering}, -}; - -use axplat::{ - irq::{HandlerTable, IpiTarget, IrqHandler, IrqIf}, - percpu::this_cpu_id, -}; -use kspin::SpinNoIrq; -use riscv::register::sie; -use riscv_plic::Plic; -use sbi_rt::HartMask; - -use crate::config::{devices::PLIC_PADDR, plat::PHYS_VIRT_OFFSET}; - -/// Use call_interface with the trait path as known to crate_interface -/// Interface for injecting virtual interrupts to guest VMs. -/// This trait is defined and implemented in the kernel (axvisor). -#[crate_interface::def_interface] -pub trait InjectIrqIf { - fn inject_virtual_interrupt(irq: usize); -} - -/// `Interrupt` bit in `scause` -pub(super) const INTC_IRQ_BASE: usize = 1 << (usize::BITS - 1); - -/// Supervisor software interrupt in `scause` -#[allow(unused)] -pub(super) const S_SOFT: usize = INTC_IRQ_BASE + 1; - -/// Supervisor timer interrupt in `scause` -pub(super) const S_TIMER: usize = INTC_IRQ_BASE + 5; - -/// Supervisor external interrupt in `scause` -pub(super) const S_EXT: usize = INTC_IRQ_BASE + 9; - -static TIMER_HANDLER: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut()); - -static IPI_HANDLER: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut()); - -/// The maximum number of IRQs. -pub const MAX_IRQ_COUNT: usize = 1024; - -static IRQ_HANDLER_TABLE: HandlerTable = HandlerTable::new(); - -static PLIC: SpinNoIrq = SpinNoIrq::new(unsafe { - Plic::new(NonNull::new((PHYS_VIRT_OFFSET + PLIC_PADDR) as *mut _).unwrap()) -}); - -fn this_context() -> usize { - let hart_id = this_cpu_id(); - hart_id * 2 + 1 // supervisor context -} - -pub(super) fn init_percpu() { - // enable soft interrupts, timer interrupts, and external interrupts - unsafe { - sie::set_ssoft(); - sie::set_stimer(); - sie::set_sext(); - } - PLIC.lock().init_by_context(this_context()); -} - -macro_rules! with_cause { - ($cause: expr, @S_TIMER => $timer_op: expr, @S_SOFT => $ipi_op: expr, @S_EXT => $ext_op: expr, @EX_IRQ => $plic_op: expr $(,)?) => { - match $cause { - S_TIMER => $timer_op, - S_SOFT => $ipi_op, - S_EXT => $ext_op, - other => { - if other & INTC_IRQ_BASE == 0 { - // Device-side interrupts read from PLIC - $plic_op - } else { - // Other CPU-side interrupts - panic!("Unknown IRQ cause: {other}"); - } - } - } - }; -} - -struct IrqIfImpl; - -#[impl_plat_interface] -impl IrqIf for IrqIfImpl { - /// Enables or disables the given IRQ. - fn set_enable(irq: usize, enabled: bool) { - with_cause!( - irq, - @S_TIMER => { - unsafe { - if enabled { - sie::set_stimer(); - } else { - sie::clear_stimer(); - } - } - }, - @S_SOFT => {}, - @S_EXT => {}, - @EX_IRQ => { - let Some(irq) = NonZeroU32::new(irq as _) else { - return; - }; - trace!("PLIC set enable: {irq} {enabled}"); - let mut plic = PLIC.lock(); - if enabled { - plic.set_priority(irq, 6); - plic.enable(irq, this_context()); - } else { - plic.disable(irq, this_context()); - } - } - ); - } - - /// Registers an IRQ handler for the given IRQ. - /// - /// It also enables the IRQ if the registration succeeds. It returns `false` if - /// the registration failed. - /// - /// The `irq` parameter has the following semantics - /// 1. If its highest bit is 1, it means it is an interrupt on the CPU side. Its - /// value comes from `scause`, where [`S_SOFT`] represents software interrupt - /// and [`S_TIMER`] represents timer interrupt. If its value is [`S_EXT`], it - /// means it is an external interrupt, and the real IRQ number needs to - /// be obtained from PLIC. - /// 2. If its highest bit is 0, it means it is an interrupt on the device side, - /// and its value is equal to the IRQ number provided by PLIC. - fn register(irq: usize, handler: IrqHandler) -> bool { - with_cause!( - irq, - @S_TIMER => TIMER_HANDLER.compare_exchange(core::ptr::null_mut(), handler as *mut _, Ordering::AcqRel, Ordering::Acquire).is_ok(), - @S_SOFT => IPI_HANDLER.compare_exchange(core::ptr::null_mut(), handler as *mut _, Ordering::AcqRel, Ordering::Acquire).is_ok(), - @S_EXT => { - warn!("External IRQ should be got from PLIC, not scause"); - false - }, - @EX_IRQ => { - if IRQ_HANDLER_TABLE.register_handler(irq, handler) { - Self::set_enable(irq, true); - true - } else { - warn!("register handler for External IRQ {irq} failed"); - false - } - } - ) - } - - /// Unregisters the IRQ handler for the given IRQ. - /// - /// It also disables the IRQ if the unregistration succeeds. It returns the - /// existing handler if it is registered, `None` otherwise. - fn unregister(irq: usize) -> Option { - with_cause!( - irq, - @S_TIMER => { - let handler = TIMER_HANDLER.swap(core::ptr::null_mut(), Ordering::AcqRel); - if !handler.is_null() { - Some(unsafe { core::mem::transmute::<*mut (), IrqHandler>(handler) }) - } else { - None - } - }, - @S_SOFT => { - let handler = IPI_HANDLER.swap(core::ptr::null_mut(), Ordering::AcqRel); - if !handler.is_null() { - Some(unsafe { core::mem::transmute::<*mut (), IrqHandler>(handler) }) - } else { - None - } - }, - @S_EXT => { - warn!("External IRQ should be got from PLIC, not scause"); - None - }, - @EX_IRQ => IRQ_HANDLER_TABLE.unregister_handler(irq).inspect(|_| Self::set_enable(irq, false)) - ) - } - - /// Handles the IRQ. - /// - /// It is called by the common interrupt handler. It should look up in the - /// IRQ handler table and calls the corresponding handler. If necessary, it - /// also acknowledges the interrupt controller after handling. - fn handle(irq: usize) -> Option { - with_cause!( - irq, - @S_TIMER => { - trace!("IRQ: timer"); - let handler = TIMER_HANDLER.load(Ordering::Acquire); - if !handler.is_null() { - // SAFETY: The handler is guaranteed to be a valid function pointer. - unsafe { core::mem::transmute::<*mut (), IrqHandler>(handler)() }; - } - Some(irq) - }, - @S_SOFT => { - trace!("IRQ: IPI"); - let handler = IPI_HANDLER.load(Ordering::Acquire); - if !handler.is_null() { - // SAFETY: The handler is guaranteed to be a valid function pointer. - unsafe { core::mem::transmute::<*mut (), IrqHandler>(handler)() }; - } - Some(irq) - }, - @S_EXT => { - // TODO: judge irq's ownership before handling (axvisor or any vm). - // Maybe later it will be done by registering all irqs IQR_HANDLER_TABLE. - - let mut plic = PLIC.lock(); - let Some(irq) = plic.claim(this_context()) else { - debug!("Spurious external IRQ"); - return None; - }; - drop(plic); - // Inject the virtual interrupt to the guest VM - crate_interface::call_interface!(InjectIrqIf::inject_virtual_interrupt, irq.get() as usize); - - // trace!("IRQ: external {irq}"); - // IRQ_HANDLER_TABLE.handle(irq.get() as usize); - // Only for irqs that belong to axvisor, complete the IRQ. - // plic.complete(this_context(), irq); - Some(irq.get() as usize) - }, - @EX_IRQ => { - unreachable!("Device-side IRQs should be handled by triggering the External Interrupt."); - } - ) - } - - /// Sends an inter-processor interrupt (IPI) to the specified target CPU or all CPUs. - fn send_ipi(_irq_num: usize, target: IpiTarget) { - match target { - IpiTarget::Current { cpu_id } => { - let res = sbi_rt::send_ipi(HartMask::from_mask_base(1 << cpu_id, 0)); - if res.is_err() { - warn!("send_ipi failed: {res:?}"); - } - } - IpiTarget::Other { cpu_id } => { - let res = sbi_rt::send_ipi(HartMask::from_mask_base(1 << cpu_id, 0)); - if res.is_err() { - warn!("send_ipi failed: {res:?}"); - } - } - IpiTarget::AllExceptCurrent { cpu_id, cpu_num } => { - for i in 0..cpu_num { - if i != cpu_id { - let res = sbi_rt::send_ipi(HartMask::from_mask_base(1 << i, 0)); - if res.is_err() { - warn!("send_ipi_all_others failed: {res:?}"); - } - } - } - } - } - } -} diff --git a/os/axvisor/platform/riscv64-qemu-virt/src/lib.rs b/os/axvisor/platform/riscv64-qemu-virt/src/lib.rs deleted file mode 100644 index 32d538179..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/src/lib.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![no_std] - -#[macro_use] -extern crate log; -#[macro_use] -extern crate axplat; - -#[cfg(feature = "irq")] -pub mod irq; - -mod boot; -mod console; -mod init; -mod mem; -mod power; -mod time; - -pub mod config { - //! Platform configuration module. - //! - //! If the `AX_CONFIG_PATH` environment variable is set, it will load the configuration from the specified path. - //! Otherwise, it will fall back to the `axconfig.toml` file in the current directory and generate the default configuration. - //! - //! If the `PACKAGE` field in the configuration does not match the package name, it will panic with an error message. - axconfig_macros::include_configs!(path_env = "AX_CONFIG_PATH", fallback = "axconfig.toml"); - assert_str_eq!( - PACKAGE, - env!("CARGO_PKG_NAME"), - "`PACKAGE` field in the configuration does not match the Package name. Please check your configuration file." - ); -} - -pub const fn cpu_count() -> usize { - config::plat::CPU_NUM -} - -pub const fn plic_base() -> usize { - config::devices::PLIC_PADDR -} diff --git a/os/axvisor/platform/riscv64-qemu-virt/src/mem.rs b/os/axvisor/platform/riscv64-qemu-virt/src/mem.rs deleted file mode 100644 index 13000df04..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/src/mem.rs +++ /dev/null @@ -1,58 +0,0 @@ -use axplat::mem::{MemIf, PhysAddr, RawRange, VirtAddr, pa, va}; - -use crate::config::devices::MMIO_RANGES; -use crate::config::plat::{ - KERNEL_BASE_PADDR, PHYS_MEMORY_BASE, PHYS_MEMORY_SIZE, PHYS_VIRT_OFFSET, -}; - -struct MemIfImpl; - -#[impl_plat_interface] -impl MemIf for MemIfImpl { - /// Returns all physical memory (RAM) ranges on the platform. - /// - /// All memory ranges except reserved ranges (including the kernel loaded - /// range) are free for allocation. - fn phys_ram_ranges() -> &'static [RawRange] { - // TODO: paser dtb to get the available memory ranges - // We can't directly use `PHYS_MEMORY_BASE` here, because it may has been used by sbi. - &[( - KERNEL_BASE_PADDR, - PHYS_MEMORY_BASE + PHYS_MEMORY_SIZE - KERNEL_BASE_PADDR, - )] - } - - /// Returns all reserved physical memory ranges on the platform. - /// - /// Reserved memory can be contained in [`phys_ram_ranges`], they are not - /// allocatable but should be mapped to kernel's address space. - /// - /// Note that the ranges returned should not include the range where the - /// kernel is loaded. - fn reserved_phys_ram_ranges() -> &'static [RawRange] { - &[] - } - - /// Returns all device memory (MMIO) ranges on the platform. - fn mmio_ranges() -> &'static [RawRange] { - &MMIO_RANGES - } - - /// Translates a physical address to a virtual address. - fn phys_to_virt(paddr: PhysAddr) -> VirtAddr { - va!(paddr.as_usize() + PHYS_VIRT_OFFSET) - } - - /// Translates a virtual address to a physical address. - fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr { - pa!(vaddr.as_usize() - PHYS_VIRT_OFFSET) - } - - /// Returns the kernel address space base virtual address and size. - fn kernel_aspace() -> (VirtAddr, usize) { - ( - va!(crate::config::plat::KERNEL_ASPACE_BASE), - crate::config::plat::KERNEL_ASPACE_SIZE, - ) - } -} diff --git a/os/axvisor/platform/riscv64-qemu-virt/src/power.rs b/os/axvisor/platform/riscv64-qemu-virt/src/power.rs deleted file mode 100644 index db9e2e0c6..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/src/power.rs +++ /dev/null @@ -1,32 +0,0 @@ -use axplat::power::PowerIf; - -struct PowerImpl; - -#[impl_plat_interface] -impl PowerIf for PowerImpl { - /// Bootstraps the given CPU core with the given initial stack (in physical - /// address). - /// - /// Where `cpu_id` is the logical CPU ID (0, 1, ..., N-1, N is the number of - /// CPU cores on the platform). - #[cfg(feature = "smp")] - fn cpu_boot(cpu_id: usize, stack_top_paddr: usize) { - use axplat::mem::{va, virt_to_phys}; - if sbi_rt::probe_extension(sbi_rt::Hsm).is_unavailable() { - warn!("HSM SBI extension is not supported for current SEE."); - return; - } - let entry = virt_to_phys(va!(crate::boot::_start_secondary as *const () as usize)); - sbi_rt::hart_start(cpu_id, entry.as_usize(), stack_top_paddr); - } - - /// Shutdown the whole system. - fn system_off() -> ! { - info!("Shutting down..."); - sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::NoReason); - warn!("It should shutdown!"); - loop { - axcpu::asm::halt(); - } - } -} diff --git a/os/axvisor/platform/riscv64-qemu-virt/src/time.rs b/os/axvisor/platform/riscv64-qemu-virt/src/time.rs deleted file mode 100644 index 5448f73ec..000000000 --- a/os/axvisor/platform/riscv64-qemu-virt/src/time.rs +++ /dev/null @@ -1,71 +0,0 @@ -use riscv::register::time; - -use axplat::time::{NANOS_PER_SEC, TimeIf}; - -const NANOS_PER_TICK: u64 = NANOS_PER_SEC / crate::config::devices::TIMER_FREQUENCY as u64; -/// RTC wall time offset in nanoseconds at monotonic time base. -static mut RTC_EPOCHOFFSET_NANOS: u64 = 0; - -pub(super) fn init_early() { - #[cfg(feature = "rtc")] - use crate::config::{devices::RTC_PADDR, plat::PHYS_VIRT_OFFSET}; - - #[cfg(feature = "rtc")] - if RTC_PADDR != 0 { - use riscv_goldfish::Rtc; - - // Get the current time in microseconds since the epoch (1970-01-01) from the riscv RTC. - // Subtract the timer ticks to get the actual time when ArceOS was booted. - let epoch_time_nanos = - Rtc::new(RTC_PADDR + PHYS_VIRT_OFFSET).get_unix_timestamp() * 1_000_000_000; - - unsafe { - RTC_EPOCHOFFSET_NANOS = - epoch_time_nanos - TimeIfImpl::ticks_to_nanos(TimeIfImpl::current_ticks()); - } - } -} - -pub(super) fn init_percpu() { - #[cfg(feature = "irq")] - sbi_rt::set_timer(0); -} - -struct TimeIfImpl; - -#[impl_plat_interface] -impl TimeIf for TimeIfImpl { - /// Returns the current clock time in hardware ticks. - fn current_ticks() -> u64 { - time::read() as u64 - } - - /// Converts hardware ticks to nanoseconds. - fn ticks_to_nanos(ticks: u64) -> u64 { - ticks * NANOS_PER_TICK - } - - /// Converts nanoseconds to hardware ticks. - fn nanos_to_ticks(nanos: u64) -> u64 { - nanos / NANOS_PER_TICK - } - - /// Return epoch offset in nanoseconds (wall time offset to monotonic clock start). - fn epochoffset_nanos() -> u64 { - unsafe { RTC_EPOCHOFFSET_NANOS } - } - - /// Returns the IRQ number for the timer interrupt. - #[cfg(feature = "irq")] - fn irq_num() -> usize { - crate::config::devices::TIMER_IRQ - } - - /// Set a one-shot timer. - /// - /// A timer interrupt will be triggered at the specified monotonic time deadline (in nanoseconds). - #[cfg(feature = "irq")] - fn set_oneshot_timer(deadline_ns: u64) { - sbi_rt::set_timer(Self::nanos_to_ticks(deadline_ns)); - } -} diff --git a/os/axvisor/rust-toolchain.toml b/os/axvisor/rust-toolchain.toml deleted file mode 100644 index 4d58874be..000000000 --- a/os/axvisor/rust-toolchain.toml +++ /dev/null @@ -1,5 +0,0 @@ -[toolchain] -profile = "minimal" -channel = "nightly-2025-12-12" -components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] -targets = ["x86_64-unknown-none", "riscv64gc-unknown-none-elf", "aarch64-unknown-none-softfloat"] diff --git a/os/axvisor/scripts/ci_run_qemu_nimbos.py b/os/axvisor/scripts/ci_run_qemu_nimbos.py deleted file mode 100755 index 0e93e6e50..000000000 --- a/os/axvisor/scripts/ci_run_qemu_nimbos.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2025 The Axvisor Team -# -# Wrapper for CI: runs `cargo xtask qemu` for NimbOS and automatically sends -# "usertests\n" to the guest when the shell prompt appears, so the test can -# complete without interactive input. -# -# Uses a PTY so the child sees a real TTY; with subprocess.PIPE the child -# may treat stdin as non-interactive and not forward input to QEMU. - -import os -import select -import sys -import subprocess - -# Trigger strings (try in order; first match sends usertests) -SEND_AFTER = (b"Rust user shell", b">>") -SEND_LINE = b"usertests\n" -SUCCESS_MARKERS = (b"usertests passed!",) - - -def main(): - try: - sep = sys.argv.index("--") - except ValueError: - print("Usage: ci_run_qemu_nimbos.py -- [args...]", file=sys.stderr) - sys.exit(2) - cmd = sys.argv[sep + 1 :] - if not cmd: - print("No command after --", file=sys.stderr) - sys.exit(2) - - import pty - - master, slave = pty.openpty() - try: - proc = subprocess.Popen( - cmd, - stdin=slave, - stdout=slave, - stderr=slave, - close_fds=True, - ) - finally: - os.close(slave) - - sent = False - saw_success = False - buffer = b"" - try: - while True: - r, _, _ = select.select([master], [], [], 0.1) - if r: - try: - chunk = os.read(master, 4096) - except OSError: - break - if not chunk: - break - sys.stdout.buffer.write(chunk) - sys.stdout.buffer.flush() - buffer = (buffer + chunk)[-1024:] - if not saw_success and any(marker in buffer for marker in SUCCESS_MARKERS): - saw_success = True - if not sent and any(trigger in buffer for trigger in SEND_AFTER): - try: - os.write(master, SEND_LINE) - sent = True - except OSError: - pass - if proc.poll() is not None: - while True: - r, _, _ = select.select([master], [], [], 0.05) - if not r: - break - try: - chunk = os.read(master, 4096) - except OSError: - break - if not chunk: - break - sys.stdout.buffer.write(chunk) - sys.stdout.buffer.flush() - break - finally: - os.close(master) - - if saw_success: - sys.exit(0) - sys.exit(proc.returncode if proc.returncode is not None else 1) - - -if __name__ == "__main__": - main() diff --git a/os/axvisor/scripts/lds/linker.lds.S b/os/axvisor/scripts/lds/linker.lds.S deleted file mode 100644 index 2196ebe0e..000000000 --- a/os/axvisor/scripts/lds/linker.lds.S +++ /dev/null @@ -1,100 +0,0 @@ -OUTPUT_ARCH(%ARCH%) - -BASE_ADDRESS = %KERNEL_BASE%; -SMP = %SMP%; - -ENTRY(_start) -SECTIONS -{ - . = BASE_ADDRESS; - _skernel = .; - - .text : ALIGN(4K) { - _stext = .; - *(.text.boot) - *(.text .text.*) - . = ALIGN(4K); - _etext = .; - } - - .rodata : ALIGN(4K) { - _srodata = .; - *(.rodata .rodata.*) - *(.srodata .srodata.*) - *(.sdata2 .sdata2.*) - . = ALIGN(4K); - _erodata = .; - } - - .data : ALIGN(4K) { - _sdata = .; - *(.data.boot_page_table) - . = ALIGN(4K); - - __sdriver_register = .; - KEEP(*(.driver.register*)) - __edriver_register = .; - - *(.data .data.*) - *(.sdata .sdata.*) - *(.got .got.*) - } - - .tdata : ALIGN(0x10) { - _stdata = .; - *(.tdata .tdata.*) - _etdata = .; - } - - .tbss : ALIGN(0x10) { - _stbss = .; - *(.tbss .tbss.*) - *(.tcommon) - _etbss = .; - } - - . = ALIGN(4K); - _percpu_start = .; - _percpu_end = _percpu_start + SIZEOF(.percpu); - .percpu 0x0 : AT(_percpu_start) { - _percpu_load_start = .; - *(.percpu .percpu.*) - _percpu_load_end = .; - . = _percpu_load_start + ALIGN(64) * SMP; - } - . = _percpu_end; - - . = ALIGN(4K); - _edata = .; - - .bss : ALIGN(4K) { - boot_stack = .; - *(.bss.stack) - . = ALIGN(4K); - boot_stack_top = .; - - _sbss = .; - *(.bss .bss.*) - *(.sbss .sbss.*) - *(COMMON) - . = ALIGN(4K); - _ebss = .; - } - - _ekernel = .; - - /DISCARD/ : { - *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) - } -} - -SECTIONS { - linkme_IRQ : { *(linkme_IRQ) } - linkm2_IRQ : { *(linkm2_IRQ) } - linkme_PAGE_FAULT : { *(linkme_PAGE_FAULT) } - linkm2_PAGE_FAULT : { *(linkm2_PAGE_FAULT) } - linkme_SYSCALL : { *(linkme_SYSCALL) } - linkm2_SYSCALL : { *(linkm2_SYSCALL) } - scope_local : { *(scope_local) } -} -INSERT AFTER .tbss; diff --git a/os/axvisor/scripts/ostool/qemu-aarch64.toml b/os/axvisor/scripts/ostool/qemu-aarch64.toml deleted file mode 100644 index defd03d8d..000000000 --- a/os/axvisor/scripts/ostool/qemu-aarch64.toml +++ /dev/null @@ -1,21 +0,0 @@ -args = [ - "-nographic", - "-cpu", - "cortex-a72", - "-machine", - "virt,virtualization=on,gic-version=3", - "-smp", - "4", - "-device", - "virtio-blk-device,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-append", - "root=/dev/vda rw init=/init", - "-m", - "8g", -] -fail_regex = [] -success_regex = [] -to_bin = true -uefi = false diff --git a/os/axvisor/scripts/ostool/qemu-riscv64.toml b/os/axvisor/scripts/ostool/qemu-riscv64.toml deleted file mode 100644 index 5fcd64120..000000000 --- a/os/axvisor/scripts/ostool/qemu-riscv64.toml +++ /dev/null @@ -1,23 +0,0 @@ -args = [ - "-nographic", - "-cpu", - "rv64", - "-machine", - "virt", - "-bios", - "default", - "-smp", - "4", - "-device", - "virtio-blk-device,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-append", - "root=/dev/vda rw init=/init", - "-m", - "4g", -] -fail_regex = [] -success_regex = [] -to_bin = true -uefi = false diff --git a/os/axvisor/scripts/ostool/qemu-x86_64.toml b/os/axvisor/scripts/ostool/qemu-x86_64.toml deleted file mode 100644 index 11e3d15ce..000000000 --- a/os/axvisor/scripts/ostool/qemu-x86_64.toml +++ /dev/null @@ -1,21 +0,0 @@ -args = [ - "-nographic", - "-cpu", - "host", - "-machine", - "q35", - "-smp", - "1", - "-accel", - "kvm", - "-device", - "virtio-blk-pci,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", - "-m", - "128M", -] -fail_regex = [] -success_regex = [] -to_bin = false -uefi = false diff --git a/os/axvisor/scripts/quick-start.sh b/os/axvisor/scripts/quick-start.sh deleted file mode 100755 index 6dda5f6b3..000000000 --- a/os/axvisor/scripts/quick-start.sh +++ /dev/null @@ -1,904 +0,0 @@ -#!/bin/bash -# -# AxVisor Environment Setup and Launch Script -# Supported platforms: qemu-aarch64, qemu-x86_64, phytiumpi, roc-rk3568-pc -# Documentation: https://arceos-hypervisor.github.io/axvisorbook/docs/quickstart -# - -set -e # Exit on error - -# Color output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -info() { - echo -e "${GREEN}[INFO]${NC} $1" -} - -warn() { - echo -e "${YELLOW}[WARN]${NC} $1" -} - -error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -# Execute command and display it -run_cmd() { - echo -e "${BLUE}$@${NC}" - "$@" -} - -# Check if running in AxVisor root directory -check_root_dir() { - if [ ! -f "Cargo.toml" ]; then - error "Cargo.toml not found. Please run this script in the AxVisor root directory." - exit 1 - fi - - if [ ! -d "configs" ]; then - error "configs directory not found. Please run this script in the AxVisor root directory." - exit 1 - fi - - # Check if package name in Cargo.toml is axvisor - if ! grep -q '^\[package\]' Cargo.toml; then - error "Invalid Cargo.toml format. Please run this script in the AxVisor root directory." - exit 1 - fi - - # Extract package name and check - local package_name - package_name=$(awk '/^\[package\]/{flag=1} flag && /^name\s*=/ {gsub(/["'\'']/, "", $3); print $3; exit}' Cargo.toml) - if [ "$package_name" != "axvisor" ]; then - error "Current project is not AxVisor (package name: $package_name). Please run this script in the AxVisor root directory." - exit 1 - fi -} - -# Show usage help -show_help() { - cat << EOF -Usage: $0 [options] - -AxVisor Environment Setup and Launch Script - -Platforms: - qemu-aarch64 QEMU AArch64 (ArceOS/Linux) - qemu-x86_64 QEMU x86_64 (NimbOS) - phytiumpi Phytium Pi Board (ArceOS/Linux) - roc-rk3568-pc ROC-RK3568-PC Board (ArceOS/Linux) - -Commands: - setup Prepare environment (download images, config files, etc.) - run Launch AxVisor (requires setup first) - start One-step setup + launch (recommended) - -Setup Options (for setup/start commands): - --serial Specify serial device - Required for board platforms (phytiumpi, roc-rk3568-pc) - If not specified, setup will prepare config but NOT launch - -Launch Options (for run/start commands): - QEMU AArch64: - -a, --arceos Launch single ArceOS guest (default) - -l, --linux Launch single Linux guest - -m, --multi Launch multiple guests (ArceOS+Linux) - QEMU x86_64: - -n, --nimbos Launch single NimbOS guest (default) - Phytium Pi: - -a, --arceos Launch single ArceOS guest (default) - -l, --linux Launch single Linux guest - -m, --multi Launch multiple guests (ArceOS+Linux) - ROC-RK3568-PC: - -a, --arceos Launch single ArceOS guest (default) - -l, --linux Launch single Linux guest - -m, --multi Launch multiple guests (ArceOS+Linux) - -h, --help Show this help message - -Examples: - # QEMU AArch64 - $0 qemu-aarch64 start --arceos # One-step: prepare + launch ArceOS - $0 qemu-aarch64 start --linux # One-step: prepare + launch Linux - $0 qemu-aarch64 start --multi # One-step: prepare + launch multiple guests - - # QEMU x86_64 - $0 qemu-x86_64 start --nimbos # One-step: prepare + launch NimbOS - $0 qemu-x86_64 start # Same as above (default nimbos) - - # Phytium Pi (requires --serial for start) - $0 phytiumpi setup # Prepare environment only - $0 phytiumpi setup --serial /dev/ttyUSB1 # Prepare with custom serial device - $0 phytiumpi run --arceos # Launch ArceOS (serial must be set in config) - $0 phytiumpi start --serial /dev/ttyUSB0 --arceos # One-step: prepare + launch - $0 phytiumpi start --serial /dev/ttyUSB1 --linux # One-step with custom serial device - - # ROC-RK3568-PC (requires --serial for start) - $0 roc-rk3568-pc setup # Prepare environment only - $0 roc-rk3568-pc setup --serial /dev/ttyACM0 # Prepare with custom serial device - $0 roc-rk3568-pc run --arceos # Launch ArceOS (serial must be set in config) - $0 roc-rk3568-pc start --serial /dev/ttyUSB0 --arceos # One-step: prepare + launch - $0 roc-rk3568-pc start --serial /dev/ttyACM0 --multi # One-step with custom serial device - - # Step-by-step execution - $0 qemu-aarch64 setup # Prepare environment only - $0 qemu-aarch64 run --arceos # Launch only - -EOF -} - -# ============================================================================ -# QEMU AArch64 Architecture Setup -# ============================================================================ - -setup_qemu_aarch64() { - info "=== QEMU AArch64 Preparation ===" - - run_cmd mkdir -p tmp/{configs,images} - - info "Downloading ArceOS image..." - run_cmd cargo xtask image download qemu_aarch64_arceos --output-dir tmp/images - - info "Downloading Linux image..." - run_cmd cargo xtask image download qemu_aarch64_linux --output-dir tmp/images - - info "Preparing board config file..." - run_cmd cp configs/board/qemu-aarch64.toml tmp/configs/ - - info "Preparing guest config files..." - run_cmd cp configs/vms/arceos-aarch64-qemu-smp1.toml tmp/configs/ - run_cmd cp configs/vms/linux-aarch64-qemu-smp1.toml tmp/configs/ - - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/qemu_aarch64_arceos/qemu-aarch64"|g' tmp/configs/arceos-aarch64-qemu-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/arceos-aarch64-qemu-smp1.toml - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/qemu_aarch64_linux/qemu-aarch64"|g' tmp/configs/linux-aarch64-qemu-smp1.toml - run_cmd sed -i 's/^id = 1$/id = 2/' tmp/configs/linux-aarch64-qemu-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/linux-aarch64-qemu-smp1.toml - - info "Preparing QEMU config file..." - run_cmd cp .github/workflows/qemu-aarch64.toml tmp/configs/qemu-aarch64-runtime.toml - - ROOTFS_PATH="$(pwd)/tmp/images/qemu_aarch64_linux/rootfs.img" - run_cmd sed -i 's|file=${workspaceFolder}/tmp/rootfs.img|file='"$ROOTFS_PATH"'|g' tmp/configs/qemu-aarch64-runtime.toml - run_cmd sed -i '/success_regex = \[/,/\]/c\success_regex = []' tmp/configs/qemu-aarch64-runtime.toml - - info "=== QEMU AArch64 Preparation Complete ===" -} - -run_qemu_aarch64_arceos() { - info "=== Launching QEMU AArch64 ArceOS Guest ===" - run_cmd cargo xtask qemu \ - --build-config tmp/configs/qemu-aarch64.toml \ - --qemu-config tmp/configs/qemu-aarch64-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-qemu-smp1.toml -} - -run_qemu_aarch64_linux() { - info "=== Launching QEMU AArch64 Linux Guest ===" - run_cmd cargo xtask qemu \ - --build-config tmp/configs/qemu-aarch64.toml \ - --qemu-config tmp/configs/qemu-aarch64-runtime.toml \ - --vmconfigs tmp/configs/linux-aarch64-qemu-smp1.toml -} - -run_qemu_aarch64_multi() { - info "=== Launching QEMU AArch64 Multiple Guests (ArceOS + Linux) ===" - run_cmd cargo xtask qemu \ - --build-config tmp/configs/qemu-aarch64.toml \ - --qemu-config tmp/configs/qemu-aarch64-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-qemu-smp1.toml \ - --vmconfigs tmp/configs/linux-aarch64-qemu-smp1.toml -} - -# ============================================================================ -# QEMU x86_64 Architecture Setup -# ============================================================================ - -setup_qemu_x86_64() { - info "=== QEMU x86_64 Preparation ===" - - run_cmd mkdir -p tmp/{configs,images} - - info "Downloading NimbOS image..." - run_cmd cargo xtask image download qemu_x86_64_nimbos --output-dir tmp/images - - info "Preparing board config file..." - run_cmd cp configs/board/qemu-x86_64.toml tmp/configs/ - - info "Preparing guest config file..." - run_cmd cp configs/vms/nimbos-x86_64-qemu-smp1.toml tmp/configs/ - - info "Preparing QEMU config file..." - run_cmd cp .github/workflows/qemu-x86_64.toml tmp/configs/qemu-x86_64-runtime.toml - - ROOTFS_PATH="$(pwd)/tmp/images/qemu_x86_64_nimbos/rootfs.img" - run_cmd sed -i 's|file=${workspaceFolder}/tmp/rootfs.img|file='"$ROOTFS_PATH"'|g' tmp/configs/qemu-x86_64-runtime.toml - - info "=== QEMU x86_64 Preparation Complete ===" -} - -run_qemu_x86_64_nimbos() { - info "=== Launching QEMU x86_64 NimbOS Guest ===" - run_cmd cargo xtask qemu \ - --build-config tmp/configs/qemu-x86_64.toml \ - --qemu-config tmp/configs/qemu-x86_64-runtime.toml \ - --vmconfigs tmp/configs/nimbos-x86_64-qemu-smp1.toml -} - -# ============================================================================ -# Phytium Pi Board Setup -# ============================================================================ - -setup_phytiumpi() { - local serial_device="" - local serial_specified=false - - # Parse arguments - while [[ $# -gt 0 ]]; do - case $1 in - --serial) - serial_device="$2" - serial_specified=true - shift 2 - ;; - *) - error "Unknown option: $1" - echo "" - echo "Phytium Pi setup supports the following options:" - echo " --serial Specify serial device" - exit 1 - ;; - esac - done - - info "=== Phytium Pi Board Preparation ===" - - run_cmd mkdir -p tmp/{configs,images} - - info "Checking/installing ostool..." - if ! command -v ostool &> /dev/null; then - info "ostool not installed, installing..." - cargo install ostool - else - info "ostool already installed" - fi - - info "Downloading ArceOS image..." - run_cmd cargo xtask image download phytiumpi_arceos --output-dir tmp/images - - info "Downloading Linux image (including device tree)..." - run_cmd cargo xtask image download phytiumpi_linux --output-dir tmp/images - - info "Preparing board config file..." - run_cmd cp configs/board/phytiumpi.toml tmp/configs/ - - info "Preparing guest config files..." - run_cmd cp configs/vms/arceos-aarch64-e2000-smp1.toml tmp/configs/ - run_cmd cp configs/vms/linux-aarch64-e2000-smp1.toml tmp/configs/ - - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/phytiumpi_arceos/phytiumpi"|g' tmp/configs/arceos-aarch64-e2000-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/arceos-aarch64-e2000-smp1.toml - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/phytiumpi_linux/phytiumpi"|g' tmp/configs/linux-aarch64-e2000-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/linux-aarch64-e2000-smp1.toml - - info "Preparing uboot config file..." - run_cmd cp .github/workflows/uboot.toml tmp/configs/phytiumpi-runtime.toml - - # Remove unnecessary commands - run_cmd sed -i '/^board_power_off_cmd = "\${env:BOARD_POWER_OFF}"/d' tmp/configs/phytiumpi-runtime.toml - run_cmd sed -i '/^board_reset_cmd = "\${env:BOARD_POWER_RESET}"/d' tmp/configs/phytiumpi-runtime.toml - - # Remove [net] config section - run_cmd sed -i '/^\[net\]/d' tmp/configs/phytiumpi-runtime.toml - run_cmd sed -i '/^interface = "\${env:BOARD_COMM_NET_IFACE}"/d' tmp/configs/phytiumpi-runtime.toml - run_cmd sed -i '/^tftp_dir = "\${env:TFTP_DIR}"/d' tmp/configs/phytiumpi-runtime.toml - - # Set baud rate - run_cmd sed -i 's|^baud_rate = "\${env:BOARD_COMM_UART_BAUD}"|baud_rate = "115200"|g' tmp/configs/phytiumpi-runtime.toml - - # Set serial device only if specified - if [ "$serial_specified" = true ]; then - info "Setting serial device to: $serial_device" - run_cmd sed -i 's|^serial = "\${env:BOARD_COMM_UART}"|serial = "'"$serial_device"'"|g' tmp/configs/phytiumpi-runtime.toml - else - # Remove serial line to keep it as environment variable - run_cmd sed -i '/^serial = "\${env:BOARD_COMM_UART}"/d' tmp/configs/phytiumpi-runtime.toml - fi - - info "Adding device tree file path to uboot config..." - DTB_PATH="$(pwd)/tmp/images/phytiumpi_linux/phytiumpi.dtb" - run_cmd sed -i 's|^dtb_file = "\${env:BOARD_DTB}"|dtb_file = "'"$DTB_PATH"'"|g' tmp/configs/phytiumpi-runtime.toml - - info "=== Phytium Pi Board Preparation Complete ===" - if [ "$serial_specified" = true ]; then - info "Serial device set to: $serial_device" - else - warn "IMPORTANT: Please set the correct serial device in tmp/configs/phytiumpi-runtime.toml" - warn "Example: serial = \"/dev/ttyUSB0\"" - warn "Then run: $0 phytiumpi run --arceos" - fi -} - -run_phytiumpi_arceos() { - info "=== Launching Phytium Pi ArceOS Guest ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/phytiumpi.toml \ - --uboot-config tmp/configs/phytiumpi-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-e2000-smp1.toml -} - -run_phytiumpi_linux() { - info "=== Launching Phytium Pi Linux Guest ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/phytiumpi.toml \ - --uboot-config tmp/configs/phytiumpi-runtime.toml \ - --vmconfigs tmp/configs/linux-aarch64-e2000-smp1.toml -} - -run_phytiumpi_multi() { - info "=== Launching Phytium Pi Multiple Guests (ArceOS + Linux) ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/phytiumpi.toml \ - --uboot-config tmp/configs/phytiumpi-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-e2000-smp1.toml \ - --vmconfigs tmp/configs/linux-aarch64-e2000-smp1.toml -} - -# ============================================================================ -# ROC-RK3568-PC Board Setup -# ============================================================================ - -setup_roc_rk3568_pc() { - local serial_device="" - local serial_specified=false - - # Parse arguments - while [[ $# -gt 0 ]]; do - case $1 in - --serial) - serial_device="$2" - serial_specified=true - shift 2 - ;; - *) - error "Unknown option: $1" - echo "" - echo "ROC-RK3568-PC setup supports the following options:" - echo " --serial Specify serial device" - exit 1 - ;; - esac - done - - info "=== ROC-RK3568-PC Board Preparation ===" - - run_cmd mkdir -p tmp/{configs,images} - - info "Checking/installing ostool..." - if ! command -v ostool &> /dev/null; then - info "ostool not installed, installing..." - cargo install ostool - else - info "ostool already installed" - fi - - info "Downloading ArceOS image..." - run_cmd cargo xtask image download roc-rk3568-pc_arceos --output-dir tmp/images - - info "Downloading Linux image (including device tree)..." - run_cmd cargo xtask image download roc-rk3568-pc_linux --output-dir tmp/images - - info "Preparing board config file..." - run_cmd cp configs/board/roc-rk3568-pc.toml tmp/configs/ - - info "Preparing guest config files..." - run_cmd cp configs/vms/arceos-aarch64-rk3568-smp1.toml tmp/configs/ - run_cmd cp configs/vms/linux-aarch64-rk3568-smp1.toml tmp/configs/ - - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/roc-rk3568-pc_arceos/roc-rk3568-pc"|g' tmp/configs/arceos-aarch64-rk3568-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/arceos-aarch64-rk3568-smp1.toml - run_cmd sed -i 's|^kernel_path = .*|kernel_path = "../images/roc-rk3568-pc_linux/roc-rk3568-pc"|g' tmp/configs/linux-aarch64-rk3568-smp1.toml - run_cmd sed -i 's|^image_location = "fs"|image_location = "memory"|g' tmp/configs/linux-aarch64-rk3568-smp1.toml - - info "Preparing uboot config file..." - run_cmd cp .github/workflows/uboot.toml tmp/configs/roc-rk3568-pc-runtime.toml - - # Remove unnecessary commands - run_cmd sed -i '/^board_power_off_cmd = "\${env:BOARD_POWER_OFF}"/d' tmp/configs/roc-rk3568-pc-runtime.toml - run_cmd sed -i '/^board_reset_cmd = "\${env:BOARD_POWER_RESET}"/d' tmp/configs/roc-rk3568-pc-runtime.toml - - # Remove [net] config section - run_cmd sed -i '/^\[net\]/d' tmp/configs/roc-rk3568-pc-runtime.toml - run_cmd sed -i '/^interface = "\${env:BOARD_COMM_NET_IFACE}"/d' tmp/configs/roc-rk3568-pc-runtime.toml - run_cmd sed -i '/^tftp_dir = "\${env:TFTP_DIR}"/d' tmp/configs/roc-rk3568-pc-runtime.toml - - info "Setting baud rate to 1500000..." - run_cmd sed -i 's|baud_rate\s*=.*|baud_rate = "1500000"|g' tmp/configs/roc-rk3568-pc-runtime.toml - - # Set serial device only if specified - if [ "$serial_specified" = true ]; then - info "Setting serial device to: $serial_device" - run_cmd sed -i 's|^serial = "\${env:BOARD_COMM_UART}"|serial = "'"$serial_device"'"|g' tmp/configs/roc-rk3568-pc-runtime.toml - else - # Remove serial line to keep it as environment variable - run_cmd sed -i '/^serial = "\${env:BOARD_COMM_UART}"/d' tmp/configs/roc-rk3568-pc-runtime.toml - fi - - info "Adding device tree file path to uboot config..." - DTB_PATH="$(pwd)/tmp/images/roc-rk3568-pc_linux/roc-rk3568-pc.dtb" - run_cmd sed -i 's|^dtb_file = "\${env:BOARD_DTB}"|dtb_file = "'"$DTB_PATH"'"|g' tmp/configs/roc-rk3568-pc-runtime.toml - - info "=== ROC-RK3568-PC Board Preparation Complete ===" - info "Baud rate has been set to 1500000" - if [ "$serial_specified" = true ]; then - info "Serial device set to: $serial_device" - else - warn "IMPORTANT: Please set the correct serial device in tmp/configs/roc-rk3568-pc-runtime.toml" - warn "Example: serial = \"/dev/ttyUSB0\"" - warn "Then run: $0 roc-rk3568-pc run --arceos" - fi -} - -run_roc_rk3568_pc_arceos() { - info "=== Launching ROC-RK3568-PC ArceOS Guest ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/roc-rk3568-pc.toml \ - --uboot-config tmp/configs/roc-rk3568-pc-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-rk3568-smp1.toml -} - -run_roc_rk3568_pc_linux() { - info "=== Launching ROC-RK3568-PC Linux Guest ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/roc-rk3568-pc.toml \ - --uboot-config tmp/configs/roc-rk3568-pc-runtime.toml \ - --vmconfigs tmp/configs/linux-aarch64-rk3568-smp1.toml -} - -run_roc_rk3568_pc_multi() { - info "=== Launching ROC-RK3568-PC Multiple Guests (ArceOS + Linux) ===" - run_cmd cargo xtask uboot \ - --build-config tmp/configs/roc-rk3568-pc.toml \ - --uboot-config tmp/configs/roc-rk3568-pc-runtime.toml \ - --vmconfigs tmp/configs/arceos-aarch64-rk3568-smp1.toml \ - --vmconfigs tmp/configs/linux-aarch64-rk3568-smp1.toml -} - -# ============================================================================ -# QEMU AArch64 Command Handling -# ============================================================================ - -cmd_setup_qemu_aarch64() { - setup_qemu_aarch64 -} - -cmd_run_qemu_aarch64() { - local mode="$1" - - case "$mode" in - -a|--arceos|"") - run_qemu_aarch64_arceos - ;; - -l|--linux) - run_qemu_aarch64_linux - ;; - -m|--multi) - run_qemu_aarch64_multi - ;; - -n|--nimbos) - error "Unsupported combination: QEMU AArch64 does not support NimbOS" - echo "" - echo "QEMU AArch64 platform supports the following guest systems:" - echo " - ArceOS (use --arceos)" - echo " - Linux (use --linux)" - echo " - Multiple guests (use --multi)" - echo "" - echo "To run NimbOS, please use QEMU x86_64 platform:" - echo " $0 qemu-x86_64 start --nimbos" - exit 1 - ;; - *) - error "Unknown option: $mode" - echo "" - echo "QEMU AArch64 platform supports the following options:" - echo " -a, --arceos Launch ArceOS guest" - echo " -l, --linux Launch Linux guest" - echo " -m, --multi Launch multiple guests (ArceOS + Linux)" - exit 1 - ;; - esac -} - -cmd_start_qemu_aarch64() { - local mode="$1" - # Validate parameters first - case "$mode" in - -a|--arceos|-l|--linux|-m|--multi|"") - # Valid parameters, continue - ;; - *) - # Invalid parameters, report error without executing setup - cmd_run_qemu_aarch64 "$mode" - return - ;; - esac - setup_qemu_aarch64 - echo "" - cmd_run_qemu_aarch64 "$mode" -} - -# ============================================================================ -# QEMU x86_64 Command Handling -# ============================================================================ - -cmd_setup_qemu_x86_64() { - setup_qemu_x86_64 -} - -cmd_run_qemu_x86_64() { - local mode="$1" - - case "$mode" in - -n|--nimbos|"") - run_qemu_x86_64_nimbos - ;; - -a|--arceos) - error "Unsupported combination: QEMU x86_64 does not support ArceOS" - echo "" - echo "QEMU x86_64 platform only supports the following guest system:" - echo " - NimbOS (use --nimbos)" - echo "" - echo "To run ArceOS, please use one of the following platforms:" - echo " $0 qemu-aarch64 start --arceos" - echo " $0 phytiumpi setup && $0 phytiumpi run --arceos" - echo " $0 roc-rk3568-pc setup && $0 roc-rk3568-pc run --arceos" - exit 1 - ;; - -l|--linux) - error "Unsupported combination: QEMU x86_64 does not support Linux" - echo "" - echo "QEMU x86_64 platform only supports the following guest system:" - echo " - NimbOS (use --nimbos)" - echo "" - echo "To run Linux, please use one of the following platforms:" - echo " $0 qemu-aarch64 start --linux" - echo " $0 phytiumpi setup && $0 phytiumpi run --linux" - echo " $0 roc-rk3568-pc setup && $0 roc-rk3568-pc run --linux" - exit 1 - ;; - -m|--multi) - error "Unsupported combination: QEMU x86_64 does not support multi-guest mode" - echo "" - echo "QEMU x86_64 platform only supports the following guest system:" - echo " - NimbOS (use --nimbos)" - echo "" - echo "To run multiple guests, please use one of the following platforms:" - echo " $0 qemu-aarch64 start --multi" - echo " $0 phytiumpi setup && $0 phytiumpi run --multi" - echo " $0 roc-rk3568-pc setup && $0 roc-rk3568-pc run --multi" - exit 1 - ;; - *) - error "Unknown option: $mode" - echo "" - echo "QEMU x86_64 platform supports the following options:" - echo " -n, --nimbos Launch NimbOS guest" - exit 1 - ;; - esac -} - -cmd_start_qemu_x86_64() { - local mode="$1" - # Validate parameters first - case "$mode" in - -n|--nimbos|"") - # Valid parameters, continue - ;; - *) - # Invalid parameters, report error without executing setup - cmd_run_qemu_x86_64 "$mode" - return - ;; - esac - setup_qemu_x86_64 - echo "" - cmd_run_qemu_x86_64 "$mode" -} - -# ============================================================================ -# Phytium Pi Command Handling -# ============================================================================ - -cmd_setup_phytiumpi() { - setup_phytiumpi "$@" -} - -cmd_start_phytiumpi() { - local serial_args=() - local guest_mode="" - local serial_specified=false - - # Parse arguments to separate --serial from guest mode - while [[ $# -gt 0 ]]; do - case $1 in - --serial) - serial_args+=("$1" "$2") - serial_specified=true - shift 2 - ;; - -a|--arceos|-l|--linux|-m|--multi|"") - guest_mode="$1" - shift - ;; - *) - error "Unknown option: $1" - exit 1 - ;; - esac - done - - # First run setup with serial args - setup_phytiumpi "${serial_args[@]}" - - # If serial not specified, stop after setup - if [ "$serial_specified" = false ]; then - return - fi - - echo "" - # Then run with guest mode - cmd_run_phytiumpi "$guest_mode" -} - -cmd_run_phytiumpi() { - local mode="$1" - - case "$mode" in - -a|--arceos|"") - run_phytiumpi_arceos - ;; - -l|--linux) - run_phytiumpi_linux - ;; - -m|--multi) - run_phytiumpi_multi - ;; - -n|--nimbos) - error "Unsupported combination: Phytium Pi board does not support NimbOS" - echo "" - echo "Phytium Pi board supports the following guest systems:" - echo " - ArceOS (use --arceos)" - echo " - Linux (use --linux)" - echo " - Multiple guests (use --multi)" - echo "" - echo "To run NimbOS, please use QEMU x86_64 platform:" - echo " $0 qemu-x86_64 start --nimbos" - exit 1 - ;; - *) - error "Unknown option: $mode" - echo "" - echo "Phytium Pi board supports the following options:" - echo " -a, --arceos Launch ArceOS guest" - echo " -l, --linux Launch Linux guest" - echo " -m, --multi Launch multiple guests (ArceOS + Linux)" - exit 1 - ;; - esac -} - -# ============================================================================ -# ROC-RK3568-PC Command Handling -# ============================================================================ - -cmd_setup_roc_rk3568_pc() { - setup_roc_rk3568_pc "$@" -} - -cmd_start_roc_rk3568_pc() { - local serial_args=() - local guest_mode="" - local serial_specified=false - - # Parse arguments to separate --serial from guest mode - while [[ $# -gt 0 ]]; do - case $1 in - --serial) - serial_args+=("$1" "$2") - serial_specified=true - shift 2 - ;; - -a|--arceos|-l|--linux|-m|--multi|"") - guest_mode="$1" - shift - ;; - *) - error "Unknown option: $1" - exit 1 - ;; - esac - done - - # First run setup with serial args - setup_roc_rk3568_pc "${serial_args[@]}" - - # If serial not specified, stop after setup - if [ "$serial_specified" = false ]; then - return - fi - - echo "" - # Then run with guest mode - cmd_run_roc_rk3568_pc "$guest_mode" -} - -cmd_run_roc_rk3568_pc() { - local mode="$1" - - case "$mode" in - -a|--arceos|"") - run_roc_rk3568_pc_arceos - ;; - -l|--linux) - run_roc_rk3568_pc_linux - ;; - -m|--multi) - run_roc_rk3568_pc_multi - ;; - -n|--nimbos) - error "Unsupported combination: ROC-RK3568-PC board does not support NimbOS" - echo "" - echo "ROC-RK3568-PC board supports the following guest systems:" - echo " - ArceOS (use --arceos)" - echo " - Linux (use --linux)" - echo " - Multiple guests (use --multi)" - echo "" - echo "To run NimbOS, please use QEMU x86_64 platform:" - echo " $0 qemu-x86_64 start --nimbos" - exit 1 - ;; - *) - error "Unknown option: $mode" - echo "" - echo "ROC-RK3568-PC board supports the following options:" - echo " -a, --arceos Launch ArceOS guest" - echo " -l, --linux Launch Linux guest" - echo " -m, --multi Launch multiple guests (ArceOS + Linux)" - exit 1 - ;; - esac -} - -# ============================================================================ -# Main Program -# ============================================================================ - -check_root_dir - -if [ $# -eq 0 ]; then - # Default to QEMU AArch64 ArceOS - cmd_start_qemu_aarch64 "" - exit 0 -fi - -PLATFORM="$1" -shift - -case "$PLATFORM" in - qemu-aarch64) - if [ $# -eq 0 ]; then - show_help - exit 0 - fi - CMD="$1" - shift - case "$CMD" in - setup) - cmd_setup_qemu_aarch64 - ;; - run) - cmd_run_qemu_aarch64 "$@" - ;; - start) - cmd_start_qemu_aarch64 "$@" - ;; - *) - error "Unknown command: $CMD" - show_help - exit 1 - ;; - esac - ;; - qemu-x86_64) - if [ $# -eq 0 ]; then - show_help - exit 0 - fi - CMD="$1" - shift - case "$CMD" in - setup) - cmd_setup_qemu_x86_64 - ;; - run) - cmd_run_qemu_x86_64 "$@" - ;; - start) - cmd_start_qemu_x86_64 "$@" - ;; - *) - error "Unknown command: $CMD" - show_help - exit 1 - ;; - esac - ;; - phytiumpi) - if [ $# -eq 0 ]; then - show_help - exit 0 - fi - CMD="$1" - shift - case "$CMD" in - setup) - cmd_setup_phytiumpi "$@" - ;; - run) - cmd_run_phytiumpi "$@" - ;; - start) - cmd_start_phytiumpi "$@" - ;; - *) - error "Unknown command: $CMD" - echo "" - echo "Phytium Pi board supports the following commands:" - echo " setup [--serial ] Prepare board environment (download images, prepare config files)" - echo " run [--arceos|--linux|--multi] Launch guest" - echo " start [--serial ] [--arceos|--linux|--multi] One-step setup + launch" - exit 1 - ;; - esac - ;; - roc-rk3568-pc) - if [ $# -eq 0 ]; then - show_help - exit 0 - fi - CMD="$1" - shift - case "$CMD" in - setup) - cmd_setup_roc_rk3568_pc "$@" - ;; - run) - cmd_run_roc_rk3568_pc "$@" - ;; - start) - cmd_start_roc_rk3568_pc "$@" - ;; - *) - error "Unknown command: $CMD" - echo "" - echo "ROC-RK3568-PC board supports the following commands:" - echo " setup [--serial ] Prepare board environment (download images, prepare config files)" - echo " run [--arceos|--linux|--multi] Launch guest" - echo " start [--serial ] [--arceos|--linux|--multi] One-step setup + launch" - exit 1 - ;; - esac - ;; - -h|--help) - show_help - ;; - *) - error "Unknown platform: $PLATFORM" - show_help - exit 1 - ;; -esac diff --git a/os/axvisor/scripts/setup_qemu.sh b/os/axvisor/scripts/setup_qemu.sh deleted file mode 100755 index 730f7fe55..000000000 --- a/os/axvisor/scripts/setup_qemu.sh +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Unified QEMU guest setup script for AxVisor testing. -# Usage: -# ./scripts/setup_qemu.sh [--guest] -# ./scripts/setup_qemu.sh arceos -# ./scripts/setup_qemu.sh --guest linux -# ./scripts/setup_qemu.sh nimbos -# -# Supported guests: arceos, linux, nimbos - -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -IMAGE_STORAGE_ROOT="/tmp/.axvisor-images" -DEFAULT_REGISTRY_URL="https://raw.githubusercontent.com/arceos-hypervisor/axvisor-guest/refs/heads/main/registry/default.toml" -# Keep this version aligned with the guest release used in this branch. -BUILTIN_FALLBACK_REGISTRY_URL="https://raw.githubusercontent.com/arceos-hypervisor/axvisor-guest/refs/heads/main/registry/v0.0.22.toml" -IMAGE_DOWNLOAD_MAX_ATTEMPTS=2 - -bootstrap_image_registry() { - local storage_dir="${IMAGE_STORAGE_ROOT}" - local default_registry_url="${DEFAULT_REGISTRY_URL}" - # Built-in fallback keeps setup resilient when default/include URLs are flaky. - local fallback_registry_url="${AXVISOR_REGISTRY_FALLBACK_URL:-${BUILTIN_FALLBACK_REGISTRY_URL}}" - local default_registry_file - local include_url - local source_kind - local source_url - - mkdir -p "${storage_dir}" - if [ -f "${storage_dir}/images.toml" ]; then - return 0 - fi - - default_registry_file="$(mktemp)" - if ! curl -4 --retry 5 --retry-delay 2 -fL "${default_registry_url}" -o "${default_registry_file}"; then - rm -f "${default_registry_file}" - echo " -> Warning: failed to fetch default registry: ${default_registry_url}" >&2 - source_kind="fallback registry" - source_url="${fallback_registry_url}" - else - include_url="$(sed -n 's/^[[:space:]]*url[[:space:]]*=[[:space:]]*"\([^"]*\)".*$/\1/p' "${default_registry_file}" | sed -n '1p')" - rm -f "${default_registry_file}" - if [ -n "${include_url}" ]; then - source_kind="included registry from default.toml" - source_url="${include_url}" - else - source_kind="default registry" - source_url="${default_registry_url}" - fi - fi - - echo " -> Bootstrapping local image registry from ${source_kind}: ${source_url}" - if ! curl -4 --retry 5 --retry-delay 2 -fL "${source_url}" -o "${storage_dir}/images.toml"; then - if [ "${source_url}" != "${fallback_registry_url}" ]; then - echo " -> Warning: failed to fetch ${source_kind}, retrying fallback registry: ${fallback_registry_url}" >&2 - curl -4 --retry 5 --retry-delay 2 -fL "${fallback_registry_url}" -o "${storage_dir}/images.toml" - else - echo " -> Error: failed to bootstrap local image registry from fallback registry." >&2 - return 1 - fi - fi - date +%s > "${storage_dir}/.last_sync" || true -} - -usage() { - echo "Usage: $0 [--guest] " - echo "" - echo " arceos - aarch64 ArceOS guest" - echo " linux - aarch64 Linux guest" - echo " nimbos - x86_64 NimbOS guest (requires VT-x/KVM)" - echo "" - echo "Examples:" - echo " $0 arceos" - echo " $0 --guest linux" - exit 1 -} - -GUEST="" -while [[ $# -gt 0 ]]; do - case "$1" in - --guest) - shift - [[ $# -gt 0 ]] || usage - GUEST="$1" - shift - break - ;; - arceos|linux|nimbos) - GUEST="$1" - shift - break - ;; - -h|--help) - usage - ;; - *) - echo "Unknown option: $1" >&2 - usage - ;; - esac -done - -[[ -n "${GUEST}" ]] || usage - -# Guest configuration: image_name|vmconfig|build_config|qemu_config|kernel_file|success_msg -case "$GUEST" in - arceos) CFG="qemu_aarch64_arceos|arceos-aarch64-qemu-smp1.toml|qemu-aarch64.toml|qemu-aarch64.toml|qemu-aarch64|Hello, world!" ;; - linux) CFG="qemu_aarch64_linux|linux-aarch64-qemu-smp1.toml|qemu-aarch64.toml|qemu-aarch64.toml|qemu-aarch64|test pass!" ;; - nimbos) CFG="qemu_x86_64_nimbos|nimbos-x86_64-qemu-smp1.toml|qemu-x86_64.toml|qemu-x86_64-kvm.toml|qemu-x86_64|usertests passed!" ;; - *) echo "Unknown guest: $GUEST" >&2; usage ;; -esac - -IFS='|' read -r IMAGE_NAME VMCONFIG BUILD_CONFIG QEMU_CONFIG KERNEL_FILE SUCCESS_MSG <<< "$CFG" -# NOTE: -# - `cargo xtask image download` 默认把镜像解压到 `/tmp/.axvisor-images/` -# - 这里直接使用该目录作为镜像来源,避免路径不一致 -IMAGE_DIR="${IMAGE_STORAGE_ROOT}/${IMAGE_NAME}" -VMCONFIG_TEMPLATE_PATH="${REPO_ROOT}/configs/vms/${VMCONFIG}" -VMCONFIG_TMP_DIR="${REPO_ROOT}/tmp/vmconfigs" -GENERATED_VMCONFIG_PATH="${VMCONFIG_TMP_DIR}/${VMCONFIG%.toml}.generated.toml" -ROOTFS_TARGET="${REPO_ROOT}/tmp/rootfs.img" -KERNEL_IMAGE="${IMAGE_DIR}/${KERNEL_FILE}" -ROOTFS_IMAGE="${IMAGE_DIR}/rootfs.img" -ABS_KERNEL_PATH="${IMAGE_DIR}/${KERNEL_FILE}" - -echo "[setup_qemu] Guest: ${GUEST} | Repo: ${REPO_ROOT}" - -echo "[setup_qemu] Step 1: ensure guest image is downloaded..." -if [ ! -d "${IMAGE_DIR}" ]; then - echo " -> Image directory ${IMAGE_DIR} not found, downloading via cargo xtask image..." - echo " -> Download attempt 1/${IMAGE_DOWNLOAD_MAX_ATTEMPTS}" - if ! (cd "${REPO_ROOT}" && cargo xtask image download "${IMAGE_NAME}"); then - echo " -> Attempt 1/${IMAGE_DOWNLOAD_MAX_ATTEMPTS} failed. Trying to bootstrap registry..." - bootstrap_image_registry - echo " -> Download attempt 2/${IMAGE_DOWNLOAD_MAX_ATTEMPTS}" - (cd "${REPO_ROOT}" && cargo xtask image download "${IMAGE_NAME}") - fi -else - echo " -> Found existing image directory: ${IMAGE_DIR}" -fi - -if [ ! -f "${KERNEL_IMAGE}" ]; then - echo "ERROR: kernel image not found at ${KERNEL_IMAGE}" >&2 - exit 1 -fi - -if [ ! -f "${ROOTFS_IMAGE}" ]; then - echo "ERROR: rootfs image not found at ${ROOTFS_IMAGE}" >&2 - exit 1 -fi - -# NimbOS x86_64 requires axvm-bios for bootstrapping -if [[ "$GUEST" == "nimbos" ]]; then - BIOS_IMAGE="${IMAGE_DIR}/axvm-bios.bin" - if [ ! -f "${BIOS_IMAGE}" ]; then - echo "ERROR: axvm-bios.bin not found at ${BIOS_IMAGE}" >&2 - echo " -> Please re-download the NimbOS image via 'cargo xtask image download qemu_x86_64_nimbos'." >&2 - exit 1 - fi -fi - -echo "[setup_qemu] Step 2: patch VM config kernel_path..." -if [ ! -f "${VMCONFIG_TEMPLATE_PATH}" ]; then - echo "ERROR: VM config file not found at ${VMCONFIG_TEMPLATE_PATH}" >&2 - exit 1 -fi - -mkdir -p "${VMCONFIG_TMP_DIR}" -cp "${VMCONFIG_TEMPLATE_PATH}" "${GENERATED_VMCONFIG_PATH}" -sed -i 's|^kernel_path *=.*|kernel_path = "'"${ABS_KERNEL_PATH}"'"|' "${GENERATED_VMCONFIG_PATH}" -echo " -> Generated VM config: ${GENERATED_VMCONFIG_PATH}" -echo " -> Updated kernel_path to ${ABS_KERNEL_PATH}" - -if [[ "$GUEST" == "nimbos" ]]; then - ABS_BIOS_PATH="${IMAGE_DIR}/axvm-bios.bin" - sed -i 's|^bios_path *=.*|bios_path = "'"${ABS_BIOS_PATH}"'"|' "${GENERATED_VMCONFIG_PATH}" - echo " -> Updated bios_path to ${ABS_BIOS_PATH}" -fi - -echo "[setup_qemu] Step 3: prepare rootfs..." -mkdir -p "${REPO_ROOT}/tmp" -cp "${ROOTFS_IMAGE}" "${ROOTFS_TARGET}" - -echo " -> Copied ${ROOTFS_IMAGE} -> ${ROOTFS_TARGET}" - -cat < usize { - memory_addr::PAGE_SIZE_4K - } - - unsafe fn map_single( - &self, - dma_mask: u64, - addr: NonNull, - size: core::num::NonZeroUsize, - align: usize, - _direction: rdif_block::dma_api::DmaDirection, - ) -> Result { - let layout = core::alloc::Layout::from_size_align(size.get(), align)?; - let dma_addr = - axvisor_api::memory::virt_to_phys((addr.as_ptr() as usize).into()).as_usize() as u64; - - if dma_addr > dma_mask || dma_addr.wrapping_add(size.get() as u64) > dma_mask { - return Err(rdif_block::dma_api::DmaError::DmaMaskNotMatch { - addr: dma_addr.into(), - mask: dma_mask, - }); - } - - if !dma_addr.is_multiple_of(align as u64) { - return Err(rdif_block::dma_api::DmaError::AlignMismatch { - required: align, - address: dma_addr.into(), - }); - } - - Ok(unsafe { rdif_block::dma_api::DmaMapHandle::new(addr, dma_addr.into(), layout, None) }) - } - - unsafe fn unmap_single(&self, _handle: rdif_block::dma_api::DmaMapHandle) {} - - unsafe fn alloc_coherent( - &self, - dma_mask: u64, - layout: core::alloc::Layout, - ) -> Option { - let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) }; - let cpu_addr = NonNull::new(ptr)?; - - let dma_addr = axvisor_api::memory::virt_to_phys((ptr as usize).into()).as_usize() as u64; - if dma_addr > dma_mask || dma_addr.wrapping_add(layout.size() as u64) > dma_mask { - unsafe { alloc::alloc::dealloc(cpu_addr.as_ptr(), layout) }; - return None; - } - - Some(unsafe { rdif_block::dma_api::DmaHandle::new(cpu_addr, dma_addr.into(), layout) }) - } - - unsafe fn dealloc_coherent(&self, handle: rdif_block::dma_api::DmaHandle) { - unsafe { alloc::alloc::dealloc(handle.as_ptr().as_ptr(), handle.layout()) } - } -} - -#[cfg(any(feature = "sdmmc", feature = "phytium-blk"))] -pub trait PlatformDeviceBlock { - fn register_block(self, dev: T); -} - -#[cfg(any(feature = "sdmmc", feature = "phytium-blk"))] -impl PlatformDeviceBlock for PlatformDevice { - fn register_block(self, dev: T) { - // Use rd_block::Block to wrap the Interface for axdriver compatibility - self.register(rd_block::Block::new(dev, &DmaImpl)); - } -} diff --git a/os/axvisor/src/driver/blk/phytium.rs b/os/axvisor/src/driver/blk/phytium.rs deleted file mode 100644 index a46fdcc90..000000000 --- a/os/axvisor/src/driver/blk/phytium.rs +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axklib::{mem::iomap, time::busy_wait}; - -use core::{ - cmp, - marker::{Send, Sync}, - ptr::NonNull, - time::Duration, -}; - -use log::{debug, info}; -use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo}; - -use phytium_mci::sd::SdCard; -use phytium_mci::{IoPad, PAD_ADDRESS, mci_host::err::MCIHostError}; -pub use phytium_mci::{Kernel, set_impl}; - -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use log::trace; - -use rdif_block::{BlkError, IQueue, Interface, Request, RequestId}; -use rdrive::DriverGeneric; - -use spin::Mutex; - -// pub use dma_api::{Direction, Impl as DmaImpl}; -// pub use dma_api::set_impl as set_dma_impl; - -const OFFSET: usize = 0x400_0000; -const BLOCK_SIZE: usize = 512; - -pub struct KernelImpl; - -impl Kernel for KernelImpl { - fn sleep(us: Duration) { - busy_wait(us); - } -} - -set_impl!(KernelImpl); - -use crate::driver::blk::PlatformDeviceBlock; - -module_driver!( - name: "Phytium SdCard", - level: ProbeLevel::PostKernel, - priority: ProbePriority::DEFAULT, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["phytium,mci"], - on_probe: probe_sdcard - } - ], -); - -fn probe_sdcard(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - info!("Probing Phytium SDCard..."); - let mci_reg = info - .node - .regs() - .into_iter() - .next() - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - info!( - "MCI reg: addr={:#x}, size={:#x}", - mci_reg.address as usize, - mci_reg.size.unwrap_or(0) - ); - - let mci_reg_base = iomap( - (mci_reg.address as usize).into(), - mci_reg.size.unwrap_or(0x10000) as usize, - ) - .expect("Failed to iomap mci reg"); - - let iopad_reg_base = - iomap((PAD_ADDRESS as usize).into(), 0x2000).expect("Failed to iomap iopad reg"); - - info!("MCI reg base mapped at {:#x}", mci_reg_base.as_usize()); - - let mci_reg = - NonNull::new(mci_reg_base.as_usize() as *mut u8).expect("Failed to create NonNull pointer"); - - let iopad_reg = NonNull::new(iopad_reg_base.as_usize() as *mut u8) - .expect("Failed to create NonNull pointer for iopad"); - - let iopad = IoPad::new(iopad_reg); - - info!("MCI reg mapped at {:p}", mci_reg); - - let sdcard = SdCardDriver::new(mci_reg, iopad); - plat_dev.register_block(sdcard); - - debug!("phytium block device registered successfully"); - - Ok(()) -} - -pub struct SdCardDriver { - sd_card: Arc>>, -} - -impl SdCardDriver { - pub fn new(sd_addr: NonNull, iopad: IoPad) -> Self { - let sd_card = Arc::new(Mutex::new(Box::new(SdCard::new(sd_addr, iopad)))); - SdCardDriver { sd_card } - } -} - -unsafe impl Send for SdCardDriver {} -unsafe impl Sync for SdCardDriver {} - -unsafe impl Send for SdCardQueue {} -unsafe impl Sync for SdCardQueue {} - -impl DriverGeneric for SdCardDriver { - fn name(&self) -> &str { - "phytium-sdcard" - } -} - -impl Interface for SdCardDriver { - fn create_queue(&mut self) -> Option> { - Some(Box::new(SdCardQueue { - sd_card: Arc::clone(&self.sd_card), - })) - } - - fn enable_irq(&mut self) { - todo!() - } - - fn disable_irq(&mut self) { - todo!() - } - - fn is_irq_enabled(&self) -> bool { - false - } - - fn handle_irq(&mut self) -> rdif_block::Event { - rdif_block::Event::none() - } -} - -pub struct SdCardQueue { - sd_card: Arc>>, -} - -impl IQueue for SdCardQueue { - /// Returns the number of blocks on the SD card. - fn num_blocks(&self) -> usize { - self.sd_card.lock().block_count() as usize - } - - /// Returns the block size in bytes. - fn block_size(&self) -> usize { - self.sd_card.lock().block_size() as usize - } - - fn id(&self) -> usize { - 0 - } - - fn buff_config(&self) -> rdif_block::BuffConfig { - rdif_block::BuffConfig { - dma_mask: u64::MAX, - align: 0x1000, - size: self.block_size(), - } - } - - fn submit_request(&mut self, request: Request<'_>) -> Result { - let actual_block_id = request.block_id + OFFSET / 512; - - match request.kind { - rdif_block::RequestKind::Read(mut buffer) => { - trace!("read block {}", actual_block_id); - - Self::validate_buffer(&buffer)?; - - let (_, aligned_buf, _) = unsafe { buffer.align_to_mut::() }; - let mut temp_buf: Vec = Vec::with_capacity(aligned_buf.len()); - - self.sd_card - .lock() - .read_blocks(&mut temp_buf, actual_block_id as u32, 1) - .map_err(|err| map_mci_error_to_blk_error(err))?; - - let copy_len = cmp::min(temp_buf.len(), aligned_buf.len()); - aligned_buf[..copy_len].copy_from_slice(&temp_buf[..copy_len]); - - Ok(RequestId::new(0)) - } - rdif_block::RequestKind::Write(buffer) => { - trace!("write block {}", actual_block_id); - - Self::validate_buffer(&buffer)?; - - let (_, aligned_buf, _) = unsafe { buffer.align_to::() }; - let mut write_buf: Vec = aligned_buf.to_vec(); - - self.sd_card - .lock() - .write_blocks(&mut write_buf, actual_block_id as u32, 1) - .map_err(|err| map_mci_error_to_blk_error(err))?; - - Ok(RequestId::new(0)) - } - } - } - - fn poll_request( - &mut self, - _request: rdif_block::RequestId, - ) -> Result<(), rdif_block::BlkError> { - Ok(()) - } -} - -impl SdCardQueue { - fn validate_buffer(buffer: &[u8]) -> Result<(), BlkError> { - if buffer.len() < BLOCK_SIZE { - return Err(BlkError::Other(Box::new(BufferError::InvalidSize { - expected: BLOCK_SIZE, - actual: buffer.len(), - }))); - } - - let (prefix, _, suffix) = unsafe { buffer.align_to::() }; - if !prefix.is_empty() || !suffix.is_empty() { - return Err(BlkError::Other(Box::new(BufferError::InvalidAlignment))); - } - - Ok(()) - } -} - -#[derive(Debug)] -enum BufferError { - InvalidSize { expected: usize, actual: usize }, - InvalidAlignment, -} - -impl core::fmt::Display for BufferError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - BufferError::InvalidSize { expected, actual } => { - write!( - f, - "Invalid buffer size: expected at least {}, got {}", - expected, actual - ) - } - BufferError::InvalidAlignment => { - write!(f, "Buffer is not properly aligned for u32 access") - } - } - } -} - -impl core::error::Error for BufferError {} - -#[derive(Debug)] -struct MCIErrorWrapper(MCIHostError); - -impl core::fmt::Display for MCIErrorWrapper { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "MCI Host Error: {:?}", self.0) - } -} - -impl core::error::Error for MCIErrorWrapper {} - -// 错误映射函数 -fn map_mci_error_to_blk_error(err: MCIHostError) -> BlkError { - match err { - MCIHostError::Timeout => BlkError::Retry, - - MCIHostError::OutOfRange | MCIHostError::InvalidArgument => { - BlkError::Other(Box::new(MCIErrorWrapper(err))) - } - - MCIHostError::CardDetectFailed | MCIHostError::CardInitFailed => BlkError::NotSupported, - - MCIHostError::InvalidVoltage - | MCIHostError::SwitchVoltageFail - | MCIHostError::SwitchVoltage18VFail33VSuccess => BlkError::NotSupported, - - MCIHostError::TransferFailed - | MCIHostError::StopTransmissionFailed - | MCIHostError::WaitWriteCompleteFailed => BlkError::Retry, - - // 其他所有错误包装为Other - _ => BlkError::Other(Box::new(MCIErrorWrapper(err))), - } -} diff --git a/os/axvisor/src/driver/blk/rockchip.rs b/os/axvisor/src/driver/blk/rockchip.rs deleted file mode 100644 index 14ec536e9..000000000 --- a/os/axvisor/src/driver/blk/rockchip.rs +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::{format, string::ToString, vec::Vec}; -use core::time::Duration; - -use rdif_clk::ClockId; -use rdrive::{ - Device, DriverGeneric, PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo, -}; -use sdmmc::emmc::{self, EMmcHost}; -use spin::Once; - -use crate::driver::{blk::PlatformDeviceBlock, iomap}; - -module_driver!( - name: "Rockchip sdhci", - level: ProbeLevel::PostKernel, - priority: ProbePriority::DEFAULT, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["rockchip,dwcmshc-sdhci"], - on_probe: probe - } - ], -); - -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - let base_reg = info - .node - .regs() - .into_iter() - .next() - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - let mmio_size = base_reg.size.unwrap_or(0x1000); - - let mmio_base = iomap(base_reg.address, mmio_size as usize)?; - - let clock: Vec<_> = info.node.clocks().into_iter().collect(); - - info!("perparing to init emmc with clock"); - - for clk in &clock { - info!( - "clock: phandle {}, name: {:?}, cells: {:?}", - clk.phandle, clk.name, clk.cells - ); - - if clk.name == Some("core".to_string()) { - let id = info - .phandle_to_device_id(clk.phandle) - .expect("no device id"); - - let clk_dev = rdrive::get::(id).expect("clk not found"); - - let clk_dev = ClkDev { - inner: clk_dev, - id: (clk.select().unwrap_or(0) as usize).into(), - // TODO: verify the id - // id: 300.into(), - }; - CLK_DEV.call_once(|| clk_dev); - - emmc::clock::init_global_clk(CLK_DEV.wait()); - } - } - - let mut emmc = EMmcHost::new(mmio_base.as_ptr() as usize); - emmc.init().map_err(|e| { - OnProbeError::other(format!( - "failed to initialize eMMC device at [PA:{:?}, SZ:0x{:x}): {e:?}", - base_reg.address, mmio_size - )) - })?; - let info = emmc.get_card_info().map_err(|e| { - OnProbeError::other(format!( - "failed to get eMMC card info at [PA:{:?}, SZ:0x{:x}): {e:?}", - base_reg.address, mmio_size - )) - })?; - info!("eMMC card info: {:#?}", info); - - let dev = BlockDivce { dev: Some(emmc) }; - plat_dev.register_block(dev); - debug!("virtio block device registered successfully"); - Ok(()) -} - -struct BlockDivce { - dev: Option, -} - -struct BlockQueue { - raw: EMmcHost, -} - -impl DriverGeneric for BlockDivce { - fn name(&self) -> &str { - "rockchip-emmc" - } -} - -impl rdif_block::Interface for BlockDivce { - fn create_queue(&mut self) -> Option> { - self.dev - .take() - .map(|dev| alloc::boxed::Box::new(BlockQueue { raw: dev }) as _) - } - - fn enable_irq(&mut self) { - todo!() - } - - fn disable_irq(&mut self) { - todo!() - } - - fn is_irq_enabled(&self) -> bool { - false - } - - fn handle_irq(&mut self) -> rdif_block::Event { - rdif_block::Event::none() - } -} - -impl rdif_block::IQueue for BlockQueue { - fn num_blocks(&self) -> usize { - self.raw.get_block_num() as _ - } - - fn block_size(&self) -> usize { - self.raw.get_block_size() - } - - fn id(&self) -> usize { - 0 - } - - fn buff_config(&self) -> rdif_block::BuffConfig { - rdif_block::BuffConfig { - dma_mask: u64::MAX, - align: 0x1000, - size: self.block_size(), - } - } - - fn submit_request( - &mut self, - request: rdif_block::Request<'_>, - ) -> Result { - let id = request.block_id; - match request.kind { - rdif_block::RequestKind::Read(mut buffer) => { - let blocks = buffer.len() / self.block_size(); - self.raw - .read_blocks(id as _, blocks as _, &mut buffer) - .map_err(maping_dev_err_to_blk_err)?; - Ok(rdif_block::RequestId::new(0)) - } - rdif_block::RequestKind::Write(items) => { - let blocks = items.len() / self.block_size(); - self.raw - .write_blocks(id as _, blocks as _, items) - .map_err(maping_dev_err_to_blk_err)?; - Ok(rdif_block::RequestId::new(0)) - } - } - } - - fn poll_request( - &mut self, - _request: rdif_block::RequestId, - ) -> Result<(), rdif_block::BlkError> { - Ok(()) - } -} - -fn maping_dev_err_to_blk_err(err: sdmmc::err::SdError) -> rdif_block::BlkError { - match err { - sdmmc::err::SdError::Timeout | sdmmc::err::SdError::DataTimeout => { - // transient timeout, ask caller to retry - rdif_block::BlkError::Retry - } - sdmmc::err::SdError::Crc - | sdmmc::err::SdError::DataCrc - | sdmmc::err::SdError::EndBit - | sdmmc::err::SdError::Index - | sdmmc::err::SdError::DataEndBit - | sdmmc::err::SdError::BadMessage - | sdmmc::err::SdError::InvalidResponse - | sdmmc::err::SdError::InvalidResponseType - | sdmmc::err::SdError::CommandError - | sdmmc::err::SdError::TransferError - | sdmmc::err::SdError::DataError - | sdmmc::err::SdError::CardError(..) => { - // CRC/response/transfer related errors => I/O error - rdif_block::BlkError::Other("SD/MMC I/O error".into()) - } - sdmmc::err::SdError::IoError => rdif_block::BlkError::Other("I/O error".into()), - sdmmc::err::SdError::NoCard | sdmmc::err::SdError::UnsupportedCard => { - // No card or unsupported card — treat as not supported - rdif_block::BlkError::NotSupported - } - sdmmc::err::SdError::BusPower - | sdmmc::err::SdError::Acmd12Error - | sdmmc::err::SdError::AdmaError - | sdmmc::err::SdError::CurrentLimit - | sdmmc::err::SdError::TuningFailed - | sdmmc::err::SdError::VoltageSwitchFailed - | sdmmc::err::SdError::BusWidth => { - rdif_block::BlkError::Other("SD/MMC controller error".into()) - } - sdmmc::err::SdError::InvalidArgument => { - rdif_block::BlkError::Other("Invalid argument".into()) - } - sdmmc::err::SdError::BufferOverflow | sdmmc::err::SdError::MemoryError => { - rdif_block::BlkError::NoMemory - } - } -} - -static CLK_DEV: Once = Once::new(); - -struct ClkDev { - inner: Device, - id: ClockId, -} - -impl emmc::clock::Clk for ClkDev { - fn emmc_get_clk(&self) -> Result { - let g = self.inner.lock().unwrap(); - g.get_rate(self.id) - .map_err(|_| emmc::clock::ClkError::InvalidPeripheralId) - } - - fn emmc_set_clk(&self, rate: u64) -> Result { - let mut g = self.inner.lock().unwrap(); - g.set_rate(self.id, rate) - .map_err(|_| emmc::clock::ClkError::InvalidPeripheralId)?; - g.get_rate(self.id) - .map_err(|_| emmc::clock::ClkError::InvalidPeripheralId) - } -} - -struct Osal {} - -impl sdmmc::Kernel for Osal { - fn sleep(us: u64) { - axklib::time::busy_wait(Duration::from_micros(us)); - } -} - -sdmmc::set_impl!(Osal); diff --git a/os/axvisor/src/driver/mod.rs b/os/axvisor/src/driver/mod.rs deleted file mode 100644 index 2e50ef78a..000000000 --- a/os/axvisor/src/driver/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Axvisor Driver Module -//! -//! This module provides hardware driver support for the Axvisor hypervisor. - -use core::ptr::NonNull; - -use rdrive::probe::OnProbeError; - -mod blk; -mod soc; -// mod serial; - -#[allow(unused)] -fn iomap(base: u64, size: usize) -> Result, OnProbeError> { - axklib::mem::iomap((base as usize).into(), size) - .map(|ptr| unsafe { NonNull::new_unchecked(ptr.as_mut_ptr()) }) - .map_err(|e| OnProbeError::Other(format!("{e}:?").into())) -} diff --git a/os/axvisor/src/driver/serial/mod.rs b/os/axvisor/src/driver/serial/mod.rs deleted file mode 100644 index 30d79ba8c..000000000 --- a/os/axvisor/src/driver/serial/mod.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::boxed::Box; - -use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo}; -use some_serial::{BSerial, ns16550, pl011}; - -use crate::driver::iomap; - -module_driver!( - name: "common serial", - level: ProbeLevel::PreKernel, - priority: ProbePriority::DEFAULT, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["arm,pl011", "snps,dw-apb-uart"], - on_probe: probe - } - ], -); - -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - info!("Probing serial device: {}", info.node.name()); - let base_reg = info - .node - .reg() - .and_then(|mut regs| regs.next()) - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - let mmio_size = base_reg.size.unwrap_or(0x1000); - let mmio_base = iomap(base_reg.address, mmio_size)?; - - let clock_freq = info.node.clock_frequency().unwrap_or(24_000_000); - - let mut serial: Option = None; - for c in info.node.compatibles() { - if c == "arm,pl011" { - serial = Some(Box::new(pl011::Pl011::new(mmio_base, clock_freq))); - break; - } - - if c == "snps,dw-apb-uart" { - serial = Some(Box::new(ns16550::Ns16550::new_mmio(mmio_base, clock_freq))); - break; - } - } - if let Some(s) = serial { - info!("Serial@{:#x} registered successfully", s.base()); - plat_dev.register(s); - } - - Ok(()) -} diff --git a/os/axvisor/src/driver/soc/mod.rs b/os/axvisor/src/driver/soc/mod.rs deleted file mode 100644 index 7b8ca49f8..000000000 --- a/os/axvisor/src/driver/soc/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod rockchip; diff --git a/os/axvisor/src/driver/soc/rockchip/clk/rk3568-clk.rs b/os/axvisor/src/driver/soc/rockchip/clk/rk3568-clk.rs deleted file mode 100644 index ed03517b9..000000000 --- a/os/axvisor/src/driver/soc/rockchip/clk/rk3568-clk.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axklib::mem::iomap; -use rdif_clk::{ClockId, Interface}; -use rdrive::{DriverGeneric, KError}; -use rk3568_clk::CRU; -use rk3568_clk::cru_clksel_con28_bits::*; - -use rdrive::{PlatformDevice, probe::OnProbeError}; -use rdrive::{module_driver, register::FdtInfo}; - -/// 频率常量 -const MHZ: u32 = 1_000_000; -const KHZ: u32 = 1_000; - -use core::convert::Into; -use core::result::Result::{self, *}; -use log::{debug, info, warn}; - -pub struct ClkDriver(CRU); - -pub const EMMC_CLK_ID: usize = 0x7c; - -impl ClkDriver { - pub fn new(cru_address: u64) -> Self { - ClkDriver(CRU::new(cru_address as *mut _)) - } -} - -impl DriverGeneric for ClkDriver { - fn name(&self) -> &str { - "rk3568-clk" - } -} - -impl Interface for ClkDriver { - fn perper_enable(&mut self) { - debug!("perper_enable"); - } - - fn get_rate(&self, id: ClockId) -> Result { - let rate = match id.into() { - EMMC_CLK_ID => { - let con = self.0.cru_clksel_get_cclk_emmc(); - con >> CRU_CLKSEL_CCLK_EMMC_POS - } - _ => { - warn!("Unsupported clock ID: {:?}", id); - Err(KError::InvalidArg { name: "clock_id" })? - } - }; - Ok(rate as u64) - } - - fn set_rate(&mut self, id: ClockId, rate: u64) -> Result<(), KError> { - match id.into() { - EMMC_CLK_ID => { - info!("Setting eMMC clock to {} Hz", rate); - let src_clk = match rate as u32 { - r if r == 24 * MHZ => CRU_CLKSEL_CCLK_EMMC_XIN_SOC0_MUX, - r if r == 52 * MHZ || r == 50 * MHZ => CRU_CLKSEL_CCLK_EMMC_CPL_DIV_50M, - r if r == 100 * MHZ => CRU_CLKSEL_CCLK_EMMC_CPL_DIV_100M, - r if r == 150 * MHZ => CRU_CLKSEL_CCLK_EMMC_GPL_DIV_150M, - r if r == 200 * MHZ => CRU_CLKSEL_CCLK_EMMC_GPL_DIV_200M, - r if r == 400 * KHZ || r == 375 * KHZ => CRU_CLKSEL_CCLK_EMMC_SOC0_375K, - _ => panic!("Unsupported eMMC clock rate: {} Hz", rate), - }; - self.0.cru_clksel_set_cclk_emmc(src_clk); - } - _ => { - warn!("Unsupported clock ID: {:?}", id); - return Err(KError::InvalidArg { name: "clock_id" }); - } - } - Ok(()) - } -} - -module_driver!( - name: "Rockchip CRU", - level: ProbeLevel::PostKernel, - priority: ProbePriority::CLK, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["rockchip,rk3568-cru"], - on_probe: probe - } - ], -); - -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - info!("Probing Rockchip RK3568 Clock..."); - - let cru_reg = info - .node - .regs() - .into_iter() - .next() - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - info!( - "CRU reg: addr={:#x}, size={:#x}", - cru_reg.address as usize, - cru_reg.size.unwrap_or(0) - ); - - let cru_reg_base = iomap( - (cru_reg.address as usize).into(), - cru_reg.size.unwrap_or(0x1000) as usize, - ) - .expect("Failed to iomap CRU"); - - let cru_address = cru_reg_base.as_ptr() as u64; - - debug!("cru address: {:#x}", cru_address); - - let clk = rdif_clk::Clk::new(ClkDriver::new(cru_address)); - - plat_dev.register(clk); - - Ok(()) -} diff --git a/os/axvisor/src/driver/soc/rockchip/clk/rk3588-clk.rs b/os/axvisor/src/driver/soc/rockchip/clk/rk3588-clk.rs deleted file mode 100644 index e6dd0c32b..000000000 --- a/os/axvisor/src/driver/soc/rockchip/clk/rk3588-clk.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use rdrive::{ - DriverGeneric, PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo, -}; -use rk3588_clk::Rk3588Cru; - -use crate::driver::iomap; - -module_driver!( - name: "Rockchip CRU", - level: ProbeLevel::PostKernel, - priority: ProbePriority::CLK, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["rockchip,rk3588-cru"], - on_probe: probe - } - ], -); - -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - let base_reg = info - .node - .reg() - .and_then(|mut regs| regs.next()) - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - let mmio_size = base_reg.size.unwrap_or(0x1000); - - let mmio_base = iomap(base_reg.address, mmio_size)?; - - let cru = Rk3588Cru::new(mmio_base); - let clk = rdif_clk::Clk::new(ClkDrv::new(cru)); - - plat_dev.register(clk); - info!("clk registered successfully"); - Ok(()) -} - -pub struct ClkDrv { - inner: Rk3588Cru, -} - -impl ClkDrv { - pub fn new(cru: Rk3588Cru) -> Self { - cru.init(); - Self { inner: cru } - } -} - -unsafe impl Send for ClkDrv {} - -impl DriverGeneric for ClkDrv { - fn name(&self) -> &str { - "rk3588-clk" - } -} - -impl rdif_clk::Interface for ClkDrv { - fn perper_enable(&mut self) { - // self.inner.npu_gate_enable(gate_id) - } - - fn get_rate(&self, id: rdif_clk::ClockId) -> Result { - let id: usize = id.into(); - let rate = self - .inner - .mmc_get_clk(id as _) - .map_err(|_| rdrive::KError::InvalidArg { name: "id" })?; - Ok(rate as _) - } - - fn set_rate(&mut self, id: rdif_clk::ClockId, rate: u64) -> Result<(), rdrive::KError> { - // todo!() - let id: usize = id.into(); - self.inner - .mmc_set_clk(id as _, rate as _) - .map_err(|_| rdrive::KError::InvalidArg { name: "id" })?; - Ok(()) - } -} diff --git a/os/axvisor/src/driver/soc/rockchip/mod.rs b/os/axvisor/src/driver/soc/rockchip/mod.rs deleted file mode 100644 index 5f7b79641..000000000 --- a/os/axvisor/src/driver/soc/rockchip/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[cfg(all(feature = "rk3588-clk", not(feature = "rk3568-clk")))] -#[path = "clk/rk3588-clk.rs"] -mod clk; - -#[cfg(all(feature = "rk3568-clk", not(feature = "rk3588-clk")))] -#[path = "clk/rk3568-clk.rs"] -mod clk; diff --git a/os/axvisor/src/driver/soc/rockchip/pm.rs b/os/axvisor/src/driver/soc/rockchip/pm.rs deleted file mode 100644 index 9e7256e58..000000000 --- a/os/axvisor/src/driver/soc/rockchip/pm.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo}; -use rockchip_pm::{RkBoard, RockchipPM}; - -use crate::driver::iomap; - -module_driver!( - name: "Rockchip Pm", - level: ProbeLevel::PostKernel, - priority: ProbePriority::CLK, - probe_kinds: &[ - ProbeKind::Fdt { - compatibles: &["rockchip,rk3588-pmu"], - on_probe: probe - } - ], -); - -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - let base_reg = info - .node - .reg() - .and_then(|mut regs| regs.next()) - .ok_or(OnProbeError::other(alloc::format!( - "[{}] has no reg", - info.node.name() - )))?; - - let mmio_size = base_reg.size.unwrap_or(0x1000); - let board = RkBoard::Rk3588; - - let mmio_base = iomap(base_reg.address, mmio_size)?; - - let pm = RockchipPM::new(mmio_base, board); - - plat_dev.register(pm); - info!("Rockchip power manager registered successfully"); - Ok(()) -} diff --git a/os/axvisor/src/hal/arch/aarch64/api.rs b/os/axvisor/src/hal/arch/aarch64/api.rs deleted file mode 100644 index 32ca2441d..000000000 --- a/os/axvisor/src/hal/arch/aarch64/api.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axvisor_api::arch::ArchIf; -use std::os::arceos::modules::axhal::{self, mem::virt_to_phys}; - -struct ArchImpl; - -#[axvisor_api::api_impl] -impl ArchIf for ArchImpl { - fn hardware_inject_virtual_interrupt(irq: axvisor_api::vmm::InterruptVector) { - crate::hal::arch::inject_interrupt(irq as _); - } - - fn read_vgicd_typer() -> u32 { - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - return gic.typer_raw(); - } - - if let Some(gic) = gic.typed_mut::() { - // Use the GICv3 driver to read the typer register - return gic.typer_raw(); - } - panic!("No GIC driver found"); - } - - fn read_vgicd_iidr() -> u32 { - // use axstd::os::arceos::modules::axhal::irq::MyVgic; - // MyVgic::get_gicd().lock().get_iidr() - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - return gic.iidr_raw(); - } - - if let Some(gic) = gic.typed_mut::() { - // Use the GICv3 driver to read the typer register - return gic.iidr_raw(); - } - - panic!("No GIC driver found"); - } - - fn get_host_gicd_base() -> memory_addr::PhysAddr { - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - let ptr: *mut u8 = gic.gicd_addr().as_ptr(); - return virt_to_phys((ptr as usize).into()); - } - - if let Some(gic) = gic.typed_mut::() { - let ptr: *mut u8 = gic.gicd_addr().as_ptr(); - // Use the GICv3 driver to read the typer register - return virt_to_phys((ptr as usize).into()); - } - panic!("No GIC driver found"); - } - - fn get_host_gicr_base() -> memory_addr::PhysAddr { - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - let ptr: *mut u8 = gic.gicr_addr().as_ptr(); - return virt_to_phys((ptr as usize).into()); - } - panic!("No GICv3 driver found"); - } - - fn fetch_irq() -> u64 { - /// TODO: better implementation - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - return u32::from(gic.cpu_interface().ack()) as _; - } - if let Some(gic) = gic.typed_mut::() { - return gic.cpu_interface().ack1().to_u32() as _; - } - panic!("No GIC driver found"); - } - - fn handle_irq() { - axhal::irq::irq_handler(0); - } -} diff --git a/os/axvisor/src/hal/arch/aarch64/cache.rs b/os/axvisor/src/hal/arch/aarch64/cache.rs deleted file mode 100644 index 2407557fa..000000000 --- a/os/axvisor/src/hal/arch/aarch64/cache.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use memory_addr::VirtAddr; - -use crate::hal::CacheOp; - -impl From for aarch64_cpu_ext::cache::CacheOp { - fn from(op: CacheOp) -> Self { - match op { - CacheOp::Clean => aarch64_cpu_ext::cache::CacheOp::Clean, - CacheOp::Invalidate => aarch64_cpu_ext::cache::CacheOp::Invalidate, - CacheOp::CleanAndInvalidate => aarch64_cpu_ext::cache::CacheOp::CleanAndInvalidate, - } - } -} - -pub fn dcache_range(op: CacheOp, addr: VirtAddr, size: usize) { - aarch64_cpu_ext::cache::dcache_range(op.into(), addr.as_usize(), size); -} diff --git a/os/axvisor/src/hal/arch/aarch64/mod.rs b/os/axvisor/src/hal/arch/aarch64/mod.rs deleted file mode 100644 index e78ceb5bd..000000000 --- a/os/axvisor/src/hal/arch/aarch64/mod.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use aarch64_cpu_ext::registers::*; - -mod api; -pub mod cache; - -pub fn inject_interrupt(irq: usize) { - debug!("Injecting virtual interrupt: {irq}"); - - let mut gic = rdrive::get_one::() - .expect("Failed to get GIC driver") - .lock() - .unwrap(); - if let Some(gic) = gic.typed_mut::() { - use arm_gic_driver::{ - IntId, - v2::{VirtualInterruptConfig, VirtualInterruptState}, - }; - - let gich = gic.hypervisor_interface().expect("Failed to get GICH"); - gich.enable(); - gich.set_virtual_interrupt( - 0, - VirtualInterruptConfig::software( - unsafe { IntId::raw(irq as _) }, - None, - 0, - VirtualInterruptState::Pending, - false, - true, - ), - ); - return; - } - - if let Some(_gic) = gic.typed_mut::() { - inject_interrupt_gic_v3(irq as _); - return; - } - - panic!("no gic driver found") -} - -pub fn inject_interrupt_gic_v3(vector: usize) { - use arm_gic_driver::v3::*; - - debug!("Injecting virtual interrupt: vector={vector}"); - let elsr = ICH_ELRSR_EL2.read(ICH_ELRSR_EL2::STATUS); - let lr_num = ICH_VTR_EL2.read(ICH_VTR_EL2::LISTREGS) as usize + 1; - - let mut free_lr = -1_isize; - - // First, check if this interrupt is already pending/active - for i in 0..lr_num { - // find a free list register - if (1 << i) & elsr > 0 { - if free_lr == -1 { - free_lr = i as isize; - } - continue; - } - let lr_val = ich_lr_el2_get(i); - - if lr_val.read(ICH_LR_EL2::VINTID) == vector as u64 - && lr_val.matches_any(&[ICH_LR_EL2::STATE::Pending, ICH_LR_EL2::STATE::Active]) - { - debug!("Virtual interrupt {vector} already pending/active in LR{i}, skipping"); - // If the interrupt is already pending or active, we can skip injecting it again. - // This is important to avoid duplicate injections. - continue; - } - } - - debug!("use free lr {free_lr} to inject irq {vector}"); - - if free_lr == -1 { - warn!("No free list register to inject IRQ {vector}, checking ICH_HCR_EL2"); - - // Try to find and reuse an inactive LR - for i in 0..lr_num { - let lr_val = ich_lr_el2_get(i); - if lr_val.matches_any(&[ICH_LR_EL2::STATE::Invalid]) { - debug!("Reusing inactive LR{i} for IRQ {vector}"); - free_lr = i as isize; - - break; - } - } - - if free_lr == -1 { - panic!("No free list register to inject IRQ {}", vector); - } - } - - ich_lr_el2_write( - free_lr as _, - ICH_LR_EL2::VINTID.val(vector as u64) + ICH_LR_EL2::STATE::Pending + ICH_LR_EL2::GROUP::SET, - ); - - // Ensure the virtual interrupt interface is enabled - let en = ICH_HCR_EL2.is_set(ICH_HCR_EL2::EN); - if !en { - // Check EN bit - warn!("Virtual interrupt interface not enabled, enabling now"); - ICH_HCR_EL2.modify(ICH_HCR_EL2::EN::SET); - } - - debug!("Virtual interrupt {vector} injected successfully in LR{free_lr}"); -} - -pub fn hardware_check() { - let pa_bits = match ID_AA64MMFR0_EL1.read_as_enum(ID_AA64MMFR0_EL1::PARange) { - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_32) => 32, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_36) => 36, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_40) => 40, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_42) => 42, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_44) => 44, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_48) => 48, - Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_52) => 52, - _ => 32, - }; - - let level = match pa_bits { - 44.. => 4, - _ => 3, - }; - - #[cfg(feature = "ept-level-4")] - { - if level < 4 { - panic!( - "4-level EPT feature is enabled, but the hardware only supports {}-level page tables. Please disable the 4-level EPT feature or use hardware that supports 4-level page tables.", - level - ); - } - } - #[cfg(not(feature = "ept-level-4"))] - { - if level > 3 { - panic!( - "The hardware supports {}-level page tables, but the 4-level EPT feature is not enabled. Please enable the 4-level EPT feature to utilize the hardware's full capabilities.", - level - ); - } - } -} diff --git a/os/axvisor/src/hal/arch/riscv64/api.rs b/os/axvisor/src/hal/arch/riscv64/api.rs deleted file mode 100644 index e45c5704b..000000000 --- a/os/axvisor/src/hal/arch/riscv64/api.rs +++ /dev/null @@ -1,8 +0,0 @@ -struct VmInterruptIfImpl; - -#[crate_interface::impl_interface] -impl axplat_riscv64_qemu_virt::irq::InjectIrqIf for VmInterruptIfImpl { - fn inject_virtual_interrupt(irq: usize) { - crate::hal::arch::inject_interrupt(irq); - } -} diff --git a/os/axvisor/src/hal/arch/riscv64/cache.rs b/os/axvisor/src/hal/arch/riscv64/cache.rs deleted file mode 100644 index 1a905a9d0..000000000 --- a/os/axvisor/src/hal/arch/riscv64/cache.rs +++ /dev/null @@ -1,5 +0,0 @@ -use memory_addr::VirtAddr; - -use crate::hal::CacheOp; - -pub fn dcache_range(_op: CacheOp, _addr: VirtAddr, _size: usize) {} diff --git a/os/axvisor/src/hal/arch/riscv64/mod.rs b/os/axvisor/src/hal/arch/riscv64/mod.rs deleted file mode 100644 index 2c14c0155..000000000 --- a/os/axvisor/src/hal/arch/riscv64/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -mod api; -pub mod cache; - -use crate::vmm::vm_list::get_vm_by_id; -use axaddrspace::{GuestPhysAddr, device::AccessWidth}; -use axvisor_api::vmm::current_vm_id; - -pub fn hardware_check() { - // TODO: implement hardware checks for RISC-V64 - // check page table level like aarch64 -} - -pub fn inject_interrupt(irq_id: usize) { - debug!("injecting interrupt id: {}", irq_id); - - // Get the instance of the vplic, and then inject virtual interrupt. - let vplic = get_vm_by_id(current_vm_id()) - .unwrap() - .get_devices() - .find_mmio_dev(GuestPhysAddr::from_usize(axruntime::plic_base())) - .unwrap(); - - // Calulate the pending register offset and value. - let reg_offset = riscv_vplic::PLIC_PENDING_OFFSET + (irq_id / 32) * 4; - let addr = GuestPhysAddr::from_usize(axruntime::plic_base() + reg_offset); - let width = AccessWidth::Dword; - let val: u32 = 1 << (irq_id % 32); - - // Use a trick write to set the pending bit. - let _ = vplic.handle_write(addr, width, val as _); -} diff --git a/os/axvisor/src/hal/arch/x86_64/cache.rs b/os/axvisor/src/hal/arch/x86_64/cache.rs deleted file mode 100644 index d8b6985fd..000000000 --- a/os/axvisor/src/hal/arch/x86_64/cache.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use memory_addr::VirtAddr; - -use crate::hal::CacheOp; - -pub fn dcache_range(_op: CacheOp, _addr: VirtAddr, _size: usize) {} diff --git a/os/axvisor/src/hal/arch/x86_64/mod.rs b/os/axvisor/src/hal/arch/x86_64/mod.rs deleted file mode 100644 index 01e0ec5e7..000000000 --- a/os/axvisor/src/hal/arch/x86_64/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub mod cache; - -pub fn hardware_check() {} -pub fn inject_interrupt(_vector: u8) {} diff --git a/os/axvisor/src/hal/impl_host.rs b/os/axvisor/src/hal/impl_host.rs deleted file mode 100644 index 283aae9f5..000000000 --- a/os/axvisor/src/hal/impl_host.rs +++ /dev/null @@ -1,11 +0,0 @@ -use axvisor_api::host::HostIf; - -struct HostImpl; - -#[axvisor_api::api_impl] -impl HostIf for HostImpl { - fn get_host_cpu_num() -> usize { - // std::os::arceos::modules::axconfig::plat::CPU_NUM - std::os::arceos::modules::axhal::cpu_num() - } -} diff --git a/os/axvisor/src/hal/impl_memory.rs b/os/axvisor/src/hal/impl_memory.rs deleted file mode 100644 index 1479255f2..000000000 --- a/os/axvisor/src/hal/impl_memory.rs +++ /dev/null @@ -1,53 +0,0 @@ -use core::{alloc::Layout, ptr::NonNull}; - -use std::os::arceos; - -use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; -use axvisor_api::memory::MemoryIf; -use memory_addr::PAGE_SIZE_4K; - -use crate::hal::AxMmHalImpl; - -struct MemoryImpl; - -#[axvisor_api::api_impl] -impl MemoryIf for MemoryImpl { - fn alloc_frame() -> Option { - ::alloc_frame() - } - - fn alloc_contiguous_frames(num_frames: usize, frame_align_pow2: usize) -> Option { - arceos::modules::axalloc::global_allocator() - .alloc( - Layout::from_size_align( - num_frames * PAGE_SIZE_4K, - PAGE_SIZE_4K << frame_align_pow2, - ) - .unwrap(), - ) - // .alloc_pages(num_frames, PAGE_SIZE_4K << frame_align_pow2) - // .map(|vaddr| ::virt_to_phys(vaddr.into())) - .map(|vaddr| HostPhysAddr::from(vaddr.as_ptr() as usize)) - .ok() - } - - fn dealloc_frame(paddr: HostPhysAddr) { - ::dealloc_frame(paddr) - } - - fn dealloc_contiguous_frames(paddr: HostPhysAddr, num_frames: usize) { - // arceos::modules::axalloc::global_allocator().dealloc_pages(paddr.as_usize(), num_frames); - arceos::modules::axalloc::global_allocator().dealloc( - unsafe { NonNull::new_unchecked(paddr.as_usize() as _) }, - Layout::from_size_align(num_frames * PAGE_SIZE_4K, PAGE_SIZE_4K).unwrap(), - ); - } - - fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr { - ::phys_to_virt(paddr) - } - - fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr { - ::virt_to_phys(vaddr) - } -} diff --git a/os/axvisor/src/hal/impl_time.rs b/os/axvisor/src/hal/impl_time.rs deleted file mode 100644 index 2c2758992..000000000 --- a/os/axvisor/src/hal/impl_time.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::os::arceos::modules::axhal; - -use axvisor_api::time::{CancelToken, Nanos, Ticks, TimeIf, TimeValue}; - -use crate::vmm; - -struct TimeImpl; - -#[axvisor_api::api_impl] -impl TimeIf for TimeImpl { - fn current_ticks() -> Ticks { - axhal::time::current_ticks() - } - - fn ticks_to_nanos(ticks: Ticks) -> Nanos { - axhal::time::ticks_to_nanos(ticks) - } - - fn nanos_to_ticks(nanos: Nanos) -> Ticks { - axhal::time::nanos_to_ticks(nanos) - } - - fn register_timer( - deadline: TimeValue, - handler: alloc::boxed::Box, - ) -> CancelToken { - vmm::timer::register_timer(deadline.as_nanos() as u64, handler) - } - - fn cancel_timer(token: CancelToken) { - vmm::timer::cancel_timer(token) - } -} diff --git a/os/axvisor/src/hal/impl_vmm.rs b/os/axvisor/src/hal/impl_vmm.rs deleted file mode 100644 index 44717cc47..000000000 --- a/os/axvisor/src/hal/impl_vmm.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::os::arceos::modules::{axhal, axtask}; - -use axaddrspace::{HostPhysAddr, HostVirtAddr}; -use axerrno::{AxResult, ax_err_type}; -use axvisor_api::vmm::{InterruptVector, VCpuId, VCpuSet, VMId, VmmIf}; - -use crate::{task::AsVCpuTask, vmm}; - -struct VmmImpl; - -fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr { - axhal::mem::virt_to_phys(vaddr) -} - -fn current_time_nanos() -> u64 { - axhal::time::monotonic_time_nanos() -} - -fn current_vm_id() -> usize { - axtask::current().as_vcpu_task().vm().id() -} - -fn current_vcpu_id() -> usize { - axtask::current().as_vcpu_task().vcpu.id() -} - -fn current_pcpu_id() -> usize { - axhal::percpu::this_cpu_id() -} - -fn vcpu_resides_on(vm_id: usize, vcpu_id: usize) -> AxResult { - vmm::with_vcpu_task(vm_id, vcpu_id, |task| task.cpu_id() as usize) - .ok_or_else(|| ax_err_type!(NotFound)) -} - -fn inject_irq_to_vcpu(vm_id: usize, vcpu_id: usize, irq: usize) -> AxResult { - vmm::with_vm_and_vcpu_on_pcpu(vm_id, vcpu_id, move |_, vcpu| { - vcpu.inject_interrupt(irq).unwrap(); - }) -} - -#[axvisor_api::api_impl] -impl VmmIf for VmmImpl { - fn current_vm_id() -> usize { - axtask::current().as_vcpu_task().vm().id() - } - - fn current_vcpu_id() -> usize { - axtask::current().as_vcpu_task().vcpu.id() - } - - fn vcpu_num(vm_id: VMId) -> Option { - vmm::with_vm(vm_id, |vm| vm.vcpu_num()) - } - - fn active_vcpus(_vm_id: VMId) -> Option { - todo!("active_vcpus") - } - - fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector) { - let _ = vmm::with_vm_and_vcpu_on_pcpu(vm_id, vcpu_id, move |_, vcpu| { - vcpu.inject_interrupt(vector as usize).unwrap(); - }); - } - - fn inject_interrupt_to_cpus(_vm_id: VMId, _vcpu_set: VCpuSet, _vector: InterruptVector) { - todo!("inject_interrupt_to_cpus") - } - - fn notify_vcpu_timer_expired(_vm_id: VMId, _vcpu_id: VCpuId) { - todo!("notify_vcpu_timer_expired") - // vmm::timer::notify_timer_expired(vm_id, vcpu_id); - } -} diff --git a/os/axvisor/src/hal/mod.rs b/os/axvisor/src/hal/mod.rs deleted file mode 100644 index 183c7c794..000000000 --- a/os/axvisor/src/hal/mod.rs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::os::arceos::{self, modules::axhal::percpu::this_cpu_id}; - -use arceos::modules::axhal; -use axaddrspace::{AxMmHal, HostPhysAddr, HostVirtAddr}; -use axvm::AxVMPerCpu; -use page_table_multiarch::PagingHandler; - -#[cfg_attr(target_arch = "aarch64", path = "arch/aarch64/mod.rs")] -#[cfg_attr(target_arch = "x86_64", path = "arch/x86_64/mod.rs")] -#[cfg_attr(target_arch = "riscv64", path = "arch/riscv64/mod.rs")] -pub mod arch; - -use crate::{hal::arch::hardware_check, vmm}; - -#[allow(unused)] -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub enum CacheOp { - /// Write back to memory - Clean, - /// Invalidate cache - Invalidate, - /// Clean and invalidate - CleanAndInvalidate, -} - -pub struct AxMmHalImpl; - -impl AxMmHal for AxMmHalImpl { - fn alloc_frame() -> Option { - ::alloc_frame() - } - - fn dealloc_frame(paddr: HostPhysAddr) { - ::dealloc_frame(paddr) - } - - #[inline] - fn phys_to_virt(paddr: HostPhysAddr) -> HostVirtAddr { - ::phys_to_virt(paddr) - } - - fn virt_to_phys(vaddr: axaddrspace::HostVirtAddr) -> axaddrspace::HostPhysAddr { - std::os::arceos::modules::axhal::mem::virt_to_phys(vaddr) - } -} - -// pub struct AxVCpuHalImpl; - -// impl AxVCpuHal for AxVCpuHalImpl { -// type MmHal = AxMmHalImpl; - -// fn irq_hanlder() { -// axhal::irq::irq_handler(0); -// } -// } - -#[percpu::def_percpu] -static mut AXVM_PER_CPU: AxVMPerCpu = AxVMPerCpu::new_uninit(); - -/// Init hardware virtualization support in each core. -pub(crate) fn enable_virtualization() { - use core::sync::atomic::AtomicUsize; - use core::sync::atomic::Ordering; - - use std::thread; - - use arceos::api::task::{AxCpuMask, ax_set_current_affinity}; - - static CORES: AtomicUsize = AtomicUsize::new(0); - - info!("Enabling hardware virtualization support on all cores..."); - - hardware_check(); - - let cpu_count = std::os::arceos::modules::axhal::cpu_num(); - - for cpu_id in 0..cpu_count { - thread::spawn(move || { - info!("Core {cpu_id} is initializing hardware virtualization support..."); - // Initialize cpu affinity here. - assert!( - ax_set_current_affinity(AxCpuMask::one_shot(cpu_id)).is_ok(), - "Initialize CPU affinity failed!" - ); - - info!("Enabling hardware virtualization support on core {cpu_id}"); - - vmm::init_timer_percpu(); - - // SAFETY: We are initializing the percpu state for the first time - #[allow(static_mut_refs)] - let percpu = unsafe { AXVM_PER_CPU.current_ref_mut_raw() }; - percpu - .init(this_cpu_id()) - .expect("Failed to initialize percpu state"); - percpu - .hardware_enable() - .expect("Failed to enable virtualization"); - - info!("Hardware virtualization support enabled on core {cpu_id}"); - - let _ = CORES.fetch_add(1, Ordering::Release); - }); - } - - info!("Waiting for all cores to enable hardware virtualization..."); - - // Wait for all cores to enable virtualization. - while CORES.load(Ordering::Acquire) != cpu_count { - // Use `yield_now` instead of `core::hint::spin_loop` to avoid deadlock. - thread::yield_now(); - } - - info!("All cores have enabled hardware virtualization support."); -} - -mod impl_host; -mod impl_memory; -mod impl_time; -mod impl_vmm; diff --git a/os/axvisor/src/logo.rs b/os/axvisor/src/logo.rs deleted file mode 100644 index 31abaaa57..000000000 --- a/os/axvisor/src/logo.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::os::arceos::api::time::ax_wall_time; -use std::println; - -const LOGO: [&str; 2] = [ - r#" - d8888 888 888 d8b - d88888 888 888 Y8P - d88P888 888 888 - d88P 888 888 888 Y88b d88P 888 .d8888b .d88b. 888d888 - d88P 888 `Y8bd8P' Y88b d88P 888 88K d88""88b 888P" - d88P 888 X88K Y88o88P 888 "Y8888b. 888 888 888 - d8888888888 .d8""8b. Y888P 888 X88 Y88..88P 888 -d88P 888 888 888 Y8P 888 88888P' "Y88P" 888 -"#, - r#" - _ __ ___ - / \ __ _\ \ / (_)___ ___ _ __ - / _ \ \ \/ /\ \ / /| / __|/ _ \| '__| - / ___ \ > < \ V / | \__ \ (_) | | -/_/ \_\/_/\_\ \_/ |_|___/\___/|_| -"#, -]; - -/// Chooses a logo based on the current time. -fn choose_logo() -> &'static str { - let elapsed = ax_wall_time().as_micros() as usize; - LOGO[elapsed % LOGO.len()] -} - -/// Prints the logo to the console. -pub fn print_logo() { - println!(); - println!("{}", choose_logo()); - println!(); - println!("by AxVisor Team"); - println!(); -} diff --git a/os/axvisor/src/main.rs b/os/axvisor/src/main.rs deleted file mode 100644 index 99721034a..000000000 --- a/os/axvisor/src/main.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Axvisor Kernel -//! -//! The main kernel binary for the Axvisor hypervisor. - -#![no_std] -#![no_main] -#![feature(used_with_arg)] - -#[macro_use] -extern crate log; - -#[macro_use] -extern crate alloc; - -extern crate axstd as std; - -#[cfg(target_arch = "x86_64")] -extern crate axplat_x86_qemu_q35; - -mod driver; -mod hal; -mod logo; -mod shell; -mod task; -mod vmm; - -#[unsafe(no_mangle)] -fn main() { - logo::print_logo(); - - info!("Starting virtualization..."); - info!("Hardware support: {:?}", axvm::has_hardware_support()); - hal::enable_virtualization(); - - vmm::init(); - vmm::start(); - - info!("[OK] Default guest initialized"); - - shell::console_init(); -} diff --git a/os/axvisor/src/shell/command/base.rs b/os/axvisor/src/shell/command/base.rs deleted file mode 100644 index 6e302b747..000000000 --- a/os/axvisor/src/shell/command/base.rs +++ /dev/null @@ -1,796 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::collections::BTreeMap; -#[cfg(feature = "fs")] -use std::fs::{self, File, FileType}; -#[cfg(feature = "fs")] -use std::io::{self, Read, Write}; -use std::println; -use std::string::{String, ToString}; - -use crate::shell::command::{CommandNode, FlagDef, ParsedCommand}; - -#[cfg(feature = "fs")] -macro_rules! print_err { - ($cmd: literal, $msg: expr) => { - println!("{}: {}", $cmd, $msg); - }; - ($cmd: literal, $arg: expr, $err: expr) => { - println!("{}: {}: {}", $cmd, $arg, $err); - }; -} - -// Helper function: split whitespace -#[cfg(feature = "fs")] -fn split_whitespace(s: &str) -> (&str, &str) { - let s = s.trim(); - if let Some(pos) = s.find(char::is_whitespace) { - let (first, rest) = s.split_at(pos); - (first, rest.trim()) - } else { - (s, "") - } -} - -#[cfg(feature = "fs")] -fn do_ls(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let show_long = cmd.flags.get("long").unwrap_or(&false); - let show_all = cmd.flags.get("all").unwrap_or(&false); - - let _current_dir = std::env::current_dir().unwrap(); - - fn show_entry_info(path: &str, entry: &str, show_long: bool) -> io::Result<()> { - if show_long { - let metadata = fs::metadata(path)?; - let size = metadata.len(); - let file_type = metadata.file_type(); - let file_type_char = file_type_to_char(file_type); - let rwx = file_perm_to_rwx(metadata.permissions().mode()); - let rwx = unsafe { core::str::from_utf8_unchecked(&rwx) }; - println!("{}{} {:>8} {}", file_type_char, rwx, size, entry); - } else { - println!("{}", entry); - } - Ok(()) - } - - fn list_one(name: &str, print_name: bool, show_long: bool, show_all: bool) -> io::Result<()> { - use std::vec::Vec; - - let is_dir = fs::metadata(name)?.is_dir(); - if !is_dir { - return show_entry_info(name, name, show_long); - } - - if print_name { - println!("{}:", name); - } - - let mut entries = fs::read_dir(name)? - .filter_map(|e| e.ok()) - .map(|e| e.file_name()) - .filter(|name| show_all || !name.starts_with('.')) - .collect::>(); - entries.sort(); - - for entry in entries { - let path = format!("{name}/{entry}"); - if let Err(e) = show_entry_info(&path, &entry, show_long) { - print_err!("ls", path, e); - } - } - Ok(()) - } - - let targets = if args.is_empty() { - vec![".".to_string()] - } else { - args.clone() - }; - - for (i, name) in targets.iter().enumerate() { - if i > 0 { - println!(); - } - if let Err(e) = list_one(name, targets.len() > 1, *show_long, *show_all) { - print_err!("ls", name, e); - } - } -} - -#[cfg(feature = "fs")] -fn do_cat(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.is_empty() { - print_err!("cat", "no file specified"); - return; - } - - fn cat_one(fname: &str) -> io::Result<()> { - let mut buf = [0; 1024]; - let mut file = File::open(fname)?; - loop { - let n = file.read(&mut buf)?; - if n > 0 { - io::stdout().write_all(&buf[..n])?; - } else { - return Ok(()); - } - } - } - - for fname in args { - if let Err(e) = cat_one(fname) { - print_err!("cat", fname, e); - } - } -} - -#[cfg(feature = "fs")] -fn do_echo(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let no_newline = cmd.flags.get("no-newline").unwrap_or(&false); - - let args_str = args.join(" "); - - fn echo_file(fname: &str, text_list: &[&str]) -> io::Result<()> { - let mut file = File::create(fname)?; - for text in text_list { - file.write_all(text.as_bytes())?; - } - Ok(()) - } - - if let Some(pos) = args_str.rfind('>') { - let text_before = args_str[..pos].trim(); - let (fname, text_after) = split_whitespace(&args_str[pos + 1..]); - if fname.is_empty() { - print_err!("echo", "no file specified"); - return; - }; - - let text_list = [ - text_before, - if !text_after.is_empty() { " " } else { "" }, - text_after, - if !no_newline { "\n" } else { "" }, - ]; - if let Err(e) = echo_file(fname, &text_list) { - print_err!("echo", fname, e); - } - } else if *no_newline { - use std::print; - - print!("{}", args_str); - } else { - println!("{}", args_str); - } -} - -#[cfg(feature = "fs")] -fn do_mkdir(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let create_parents = cmd.flags.get("parents").unwrap_or(&false); - - if args.is_empty() { - print_err!("mkdir", "missing operand"); - return; - } - - fn mkdir_one(path: &str, create_parents: bool) -> io::Result<()> { - if create_parents { - fs::create_dir_all(path) - } else { - fs::create_dir(path) - } - } - - for path in args { - if let Err(e) = mkdir_one(path, *create_parents) { - print_err!("mkdir", format_args!("cannot create directory '{path}'"), e); - } - } -} - -#[cfg(feature = "fs")] -fn do_rm(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let rm_dir = cmd.flags.get("dir").unwrap_or(&false); - let recursive = cmd.flags.get("recursive").unwrap_or(&false); - let force = cmd.flags.get("force").unwrap_or(&false); - - if args.is_empty() { - print_err!("rm", "missing operand"); - return; - } - - fn rm_one(path: &str, rm_dir: bool, recursive: bool, force: bool) -> io::Result<()> { - let metadata = fs::metadata(path); - - if force && metadata.is_err() { - return Ok(()); // Ignore non-existent files when in force mode - } - - let metadata = metadata?; - - if metadata.is_dir() { - if recursive { - remove_dir_recursive(path, force) - } else if rm_dir { - fs::remove_dir(path) - } else { - Err(io::Error::Unsupported) - } - } else { - fs::remove_file(path) - } - } - - for path in args { - if let Err(e) = rm_one(path, *rm_dir, *recursive, *force) - && !force - { - print_err!("rm", format_args!("cannot remove '{path}'"), e); - } - } -} - -// Implementation of recursively deleting directories (manual recursion) -#[cfg(feature = "fs")] -fn remove_dir_recursive(path: &str, _force: bool) -> io::Result<()> { - // Read directory contents - let entries = fs::read_dir(path)?; - - // Remove all child items - for entry_result in entries { - let entry = entry_result?; - let entry_path = format!("{}/{}", path, entry.file_name()); - let metadata = entry.file_type(); - - if metadata.is_dir() { - // Recursively delete subdirectory - remove_dir_recursive(&entry_path, _force)?; - } else { - // Delete file - fs::remove_file(&entry_path)?; - } - } - - // Delete empty directory - fs::remove_dir(path) -} - -#[cfg(feature = "fs")] -fn do_cd(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - let target = if args.is_empty() { - "/" - } else if args.len() == 1 { - &args[0] - } else { - print_err!("cd", "too many arguments"); - return; - }; - - if let Err(e) = std::env::set_current_dir(target) { - print_err!("cd", target, e); - } -} - -#[cfg(feature = "fs")] -fn do_pwd(cmd: &ParsedCommand) { - let _logical = cmd.flags.get("logical").unwrap_or(&false); - - let pwd = std::env::current_dir().unwrap(); - println!("{}", pwd); -} - -fn do_uname(cmd: &ParsedCommand) { - let show_all = cmd.flags.get("all").unwrap_or(&false); - let show_kernel = cmd.flags.get("kernel-name").unwrap_or(&false); - let show_arch = cmd.flags.get("machine").unwrap_or(&false); - - let arch = option_env!("AX_ARCH").unwrap_or(""); - let platform = option_env!("AX_PLATFORM").unwrap_or(""); - let smp = match option_env!("AX_SMP") { - None | Some("1") => "", - _ => " SMP", - }; - let version = option_env!("CARGO_PKG_VERSION").unwrap_or("0.1.0"); - - if *show_all { - println!( - "ArceOS {ver}{smp} {arch} {plat}", - ver = version, - smp = smp, - arch = arch, - plat = platform, - ); - } else if *show_kernel { - println!("ArceOS"); - } else if *show_arch { - println!("{}", arch); - } else { - println!( - "ArceOS {ver}{smp} {arch} {plat}", - ver = version, - smp = smp, - arch = arch, - plat = platform, - ); - } -} - -fn do_exit(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let exit_code = if args.is_empty() { - 0 - } else { - args[0].parse::().unwrap_or(0) - }; - - println!("Bye~"); - std::process::exit(exit_code); -} - -fn do_log(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.is_empty() { - println!("Current log level: {:?}", log::max_level()); - return; - } - - match args[0].as_str() { - "on" | "enable" => log::set_max_level(log::LevelFilter::Info), - "off" | "disable" => log::set_max_level(log::LevelFilter::Off), - "error" => log::set_max_level(log::LevelFilter::Error), - "warn" => log::set_max_level(log::LevelFilter::Warn), - "info" => log::set_max_level(log::LevelFilter::Info), - "debug" => log::set_max_level(log::LevelFilter::Debug), - "trace" => log::set_max_level(log::LevelFilter::Trace), - level => { - println!("Unknown log level: {}", level); - println!("Available levels: off, error, warn, info, debug, trace"); - return; - } - } - println!("Log level set to: {:?}", log::max_level()); -} - -#[cfg(feature = "fs")] -fn do_mv(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.len() < 2 { - print_err!("mv", "missing operand"); - return; - } - - // If only two arguments, handle single file/dir move - if args.len() == 2 { - let source = &args[0]; - let dest = &args[1]; - - // Check if destination exists and is a directory - if let Ok(dest_meta) = fs::metadata(dest) - && dest_meta.is_dir() - { - // Move source into destination directory - let mut file_dir = fs::read_dir(dest).unwrap(); - let source_name = match file_dir.next() { - Some(name) => { - let dir_name = name.expect("Failed to read directory"); - let file = dir_name.file_name(); - format!("{dest}/{file}") - } - None => { - print_err!("mv", format_args!("invalid source path '{source}'")); - return; - } - }; - let dest_path = format!("{dest}/{source_name}"); - if let Err(e) = move_file_or_dir(source, &dest_path) { - print_err!( - "mv", - format_args!("cannot move '{source}' to '{dest_path}'"), - e - ); - } - return; - } - - // Direct rename/move - if let Err(e) = move_file_or_dir(source, dest) { - print_err!("mv", format_args!("cannot move '{source}' to '{dest}'"), e); - } - } else { - // Multiple sources - destination must be a directory - let dest = &args[args.len() - 1]; - let sources = &args[..args.len() - 1]; - - // Check if destination is a directory - match fs::metadata(dest) { - Ok(meta) if meta.is_dir() => { - // Move each source into destination directory - for source in sources { - let mut file_dir = fs::read_dir(source).unwrap(); - let source_name = match file_dir.next() { - Some(name) => { - let dir_name = name.expect("Failed to read directory"); - let file = dir_name.file_name(); - format!("{dest}/{file}") - } - None => { - print_err!("mv", format_args!("invalid source path '{source}'")); - return; - } - }; - let dest_path = format!("{dest}/{source_name}"); - if let Err(e) = move_file_or_dir(source, &dest_path) { - print_err!( - "mv", - format_args!("cannot move '{source}' to '{dest_path}'"), - e - ); - } - } - } - Ok(_) => { - print_err!("mv", format_args!("target '{dest}' is not a directory")); - } - Err(e) => { - print_err!("mv", format_args!("cannot access '{dest}'"), e); - } - } - } -} - -// Helper function to move file or directory (handles cross-filesystem moves) -#[cfg(feature = "fs")] -fn move_file_or_dir(source: &str, dest: &str) -> io::Result<()> { - // Try simple rename first (works within same filesystem) - match fs::rename(source, dest) { - Ok(()) => Ok(()), - Err(_) => { - // If rename fails, try copy + delete (for cross-filesystem moves) - let src_meta = fs::metadata(source)?; - - if src_meta.is_dir() { - // For directories, use recursive copy then remove - copy_dir_recursive(source, dest)?; - remove_dir_recursive(source, false)?; - } else { - // For files, copy then remove - copy_file(source, dest)?; - fs::remove_file(source)?; - } - Ok(()) - } - } -} - -#[cfg(feature = "fs")] -fn do_touch(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.is_empty() { - print_err!("touch", "missing operand"); - return; - } - - for filename in args { - if let Err(e) = File::create(filename) { - print_err!("touch", filename, e); - } - } -} - -#[cfg(feature = "fs")] -fn do_cp(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let recursive = cmd.flags.get("recursive").unwrap_or(&false); - - if args.len() < 2 { - print_err!("cp", "missing operand"); - return; - } - - let source = &args[0]; - let dest = &args[1]; - - // Check if source file/directory exists - let src_metadata = match fs::metadata(source) { - Ok(metadata) => metadata, - Err(e) => { - print_err!("cp", format_args!("cannot access '{source}'"), e); - return; - } - }; - - let result = if src_metadata.is_dir() { - if *recursive { - copy_dir_recursive(source, dest) - } else { - Err(io::Error::Unsupported) - } - } else { - copy_file(source, dest) - }; - - if let Err(e) = result { - print_err!("cp", format_args!("cannot copy '{source}' to '{dest}'"), e); - } -} - -// Manually implement file copy -#[cfg(feature = "fs")] -fn copy_file(src: &str, dst: &str) -> io::Result<()> { - let mut src_file = File::open(src)?; - let mut dst_file = File::create(dst)?; - - let mut buffer = [0; 4096]; - loop { - let bytes_read = src_file.read(&mut buffer)?; - if bytes_read == 0 { - break; - } - dst_file.write_all(&buffer[..bytes_read])?; - } - Ok(()) -} - -// Recursively copy directory -#[cfg(feature = "fs")] -fn copy_dir_recursive(src: &str, dst: &str) -> io::Result<()> { - // Create target directory - fs::create_dir(dst)?; - - // Read source directory contents - let entries = fs::read_dir(src)?; - - for entry_result in entries { - let entry = entry_result?; - let file_name = entry.file_name(); - let src_path = format!("{src}/{file_name}"); - let dst_path = format!("{dst}/{file_name}"); - - let metadata = entry.file_type(); - if metadata.is_dir() { - copy_dir_recursive(&src_path, &dst_path)?; - } else { - copy_file(&src_path, &dst_path)?; - } - } - Ok(()) -} - -#[cfg(feature = "fs")] -fn file_type_to_char(ty: FileType) -> char { - if ty.is_char_device() { - 'c' - } else if ty.is_block_device() { - 'b' - } else if ty.is_socket() { - 's' - } else if ty.is_fifo() { - 'p' - } else if ty.is_symlink() { - 'l' - } else if ty.is_dir() { - 'd' - } else if ty.is_file() { - '-' - } else { - '?' - } -} - -#[rustfmt::skip] -#[cfg(feature = "fs")] -const fn file_perm_to_rwx(mode: u32) -> [u8; 9] { - let mut perm = [b'-'; 9]; - macro_rules! set { - ($bit:literal, $rwx:literal) => { - if mode & (1 << $bit) != 0 { - perm[8 - $bit] = $rwx - } - }; - } - - set!(2, b'r'); set!(1, b'w'); set!(0, b'x'); - set!(5, b'r'); set!(4, b'w'); set!(3, b'x'); - set!(8, b'r'); set!(7, b'w'); set!(6, b'x'); - perm -} - -pub fn build_base_cmd(tree: &mut BTreeMap) { - // ls Command - #[cfg(feature = "fs")] - tree.insert( - "ls".to_string(), - CommandNode::new("List directory contents") - .with_handler(do_ls) - .with_usage("ls [OPTIONS] [DIRECTORY...]") - .with_flag( - FlagDef::new("long", "Use long listing format") - .with_short('l') - .with_long("long"), - ) - .with_flag( - FlagDef::new("all", "Show hidden files") - .with_short('a') - .with_long("all"), - ), - ); - - // cat Command - #[cfg(feature = "fs")] - tree.insert( - "cat".to_string(), - CommandNode::new("Display file contents") - .with_handler(do_cat) - .with_usage("cat [FILE2...]"), - ); - - // echo Command - #[cfg(feature = "fs")] - tree.insert( - "echo".to_string(), - CommandNode::new("Display text") - .with_handler(do_echo) - .with_usage("echo [OPTIONS] [TEXT...]") - .with_flag( - FlagDef::new("no-newline", "Do not output trailing newline") - .with_short('n') - .with_long("no-newline"), - ), - ); - - // mkdir Command - #[cfg(feature = "fs")] - tree.insert( - "mkdir".to_string(), - CommandNode::new("Create directories") - .with_handler(do_mkdir) - .with_usage("mkdir [OPTIONS] [DIRECTORY2...]") - .with_flag( - FlagDef::new("parents", "Create parent directories as needed") - .with_short('p') - .with_long("parents"), - ), - ); - - // rm Command - #[cfg(feature = "fs")] - tree.insert( - "rm".to_string(), - CommandNode::new("Remove files and directories") - .with_handler(do_rm) - .with_usage("rm [OPTIONS] [FILE2...]") - .with_flag( - FlagDef::new("dir", "Remove empty directories") - .with_short('d') - .with_long("dir"), - ) - .with_flag( - FlagDef::new("recursive", "Remove directories recursively") - .with_short('r') - .with_long("recursive"), - ) - .with_flag( - FlagDef::new("force", "Force removal, ignore nonexistent files") - .with_short('f') - .with_long("force"), - ), - ); - - // cd Command - #[cfg(feature = "fs")] - tree.insert( - "cd".to_string(), - CommandNode::new("Change directory") - .with_handler(do_cd) - .with_usage("cd [DIRECTORY]"), - ); - - // pwd Command - #[cfg(feature = "fs")] - tree.insert( - "pwd".to_string(), - CommandNode::new("Print working directory") - .with_handler(do_pwd) - .with_usage("pwd [OPTIONS]") - .with_flag( - FlagDef::new("logical", "Use logical path") - .with_short('L') - .with_long("logical"), - ), - ); - - // uname Command - tree.insert( - "uname".to_string(), - CommandNode::new("System information") - .with_handler(do_uname) - .with_usage("uname [OPTIONS]") - .with_flag( - FlagDef::new("all", "Show all information") - .with_short('a') - .with_long("all"), - ) - .with_flag( - FlagDef::new("kernel-name", "Show kernel name") - .with_short('s') - .with_long("kernel-name"), - ) - .with_flag( - FlagDef::new("machine", "Show machine architecture") - .with_short('m') - .with_long("machine"), - ), - ); - - // exit Command - tree.insert( - "exit".to_string(), - CommandNode::new("Exit the shell") - .with_handler(do_exit) - .with_usage("exit [EXIT_CODE]"), - ); - - // log Command - tree.insert( - "log".to_string(), - CommandNode::new("Change log level") - .with_handler(do_log) - .with_usage("log [LEVEL]"), - ); - - // touch Command - #[cfg(feature = "fs")] - tree.insert( - "touch".to_string(), - CommandNode::new("Create empty files") - .with_handler(do_touch) - .with_usage("touch [FILE2...]"), - ); - - // cp Command - #[cfg(feature = "fs")] - tree.insert( - "cp".to_string(), - CommandNode::new("Copy files") - .with_handler(do_cp) - .with_usage("cp [OPTIONS] ") - .with_flag( - FlagDef::new("recursive", "Copy directories recursively") - .with_short('r') - .with_long("recursive"), - ), - ); - - // mv Command - #[cfg(feature = "fs")] - tree.insert( - "mv".to_string(), - CommandNode::new("Move/rename files") - .with_handler(do_mv) - .with_usage("mv | mv [SOURCE2...] "), - ); -} diff --git a/os/axvisor/src/shell/command/history.rs b/os/axvisor/src/shell/command/history.rs deleted file mode 100644 index 37257d394..000000000 --- a/os/axvisor/src/shell/command/history.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::io::prelude::*; -use std::{string::String, vec::Vec}; - -pub struct CommandHistory { - history: Vec, - current_index: usize, - max_size: usize, -} - -impl CommandHistory { - pub fn new(max_size: usize) -> Self { - Self { - history: Vec::new(), - current_index: 0, - max_size, - } - } - - pub fn add_command(&mut self, cmd: String) { - if !cmd.trim().is_empty() && self.history.last() != Some(&cmd) { - if self.history.len() >= self.max_size { - self.history.remove(0); - } - self.history.push(cmd); - } - self.current_index = self.history.len(); - } - - #[allow(dead_code)] - pub fn previous(&mut self) -> Option<&String> { - if self.current_index > 0 { - self.current_index -= 1; - self.history.get(self.current_index) - } else { - None - } - } - - #[allow(dead_code)] - pub fn next(&mut self) -> Option<&String> { - if self.current_index < self.history.len() { - self.current_index += 1; - if self.current_index < self.history.len() { - self.history.get(self.current_index) - } else { - None - } - } else { - None - } - } -} - -#[allow(unused_must_use)] -pub fn clear_line_and_redraw( - stdout: &mut dyn Write, - prompt: &str, - content: &str, - cursor_pos: usize, -) { - write!(stdout, "\r"); - write!(stdout, "\x1b[2K"); - write!(stdout, "{prompt}{content}"); - if cursor_pos < content.len() { - write!(stdout, "\x1b[{}D", content.len() - cursor_pos); - } - stdout.flush(); -} diff --git a/os/axvisor/src/shell/command/mod.rs b/os/axvisor/src/shell/command/mod.rs deleted file mode 100644 index 969d8fc67..000000000 --- a/os/axvisor/src/shell/command/mod.rs +++ /dev/null @@ -1,580 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod base; -mod history; -mod vm; - -pub use base::*; -pub use history::*; -pub use vm::*; - -use std::io::prelude::*; -use std::string::String; -use std::vec::Vec; -use std::{collections::BTreeMap, string::ToString}; -use std::{print, println}; - -lazy_static::lazy_static! { - pub static ref COMMAND_TREE: BTreeMap = build_command_tree(); -} - -#[derive(Debug, Clone)] -pub struct CommandNode { - handler: Option, - subcommands: BTreeMap, - description: &'static str, - usage: Option<&'static str>, - #[allow(dead_code)] - log_level: log::LevelFilter, - options: Vec, - flags: Vec, -} - -#[derive(Debug, Clone)] -pub struct OptionDef { - name: &'static str, - short: Option, - long: Option<&'static str>, - description: &'static str, - required: bool, -} - -#[derive(Debug, Clone)] -pub struct FlagDef { - name: &'static str, - short: Option, - long: Option<&'static str>, - description: &'static str, -} - -#[derive(Debug, Clone)] -pub struct ParsedCommand { - pub command_path: Vec, - pub options: BTreeMap, - pub flags: BTreeMap, - pub positional_args: Vec, -} - -#[derive(Debug)] -pub enum ParseError { - UnknownCommand(String), - UnknownOption(String), - MissingValue(String), - MissingRequiredOption(String), - NoHandler(String), -} - -impl CommandNode { - pub fn new(description: &'static str) -> Self { - Self { - handler: None, - subcommands: BTreeMap::new(), - description, - usage: None, - log_level: log::LevelFilter::Off, - options: Vec::new(), - flags: Vec::new(), - } - } - - pub fn with_handler(mut self, handler: fn(&ParsedCommand)) -> Self { - self.handler = Some(handler); - self - } - - pub fn with_usage(mut self, usage: &'static str) -> Self { - self.usage = Some(usage); - self - } - - #[allow(dead_code)] - pub fn with_log_level(mut self, level: log::LevelFilter) -> Self { - self.log_level = level; - self - } - - pub fn with_option(mut self, option: OptionDef) -> Self { - self.options.push(option); - self - } - - pub fn with_flag(mut self, flag: FlagDef) -> Self { - self.flags.push(flag); - self - } - - pub fn add_subcommand>(mut self, name: S, node: CommandNode) -> Self { - self.subcommands.insert(name.into(), node); - self - } -} - -impl OptionDef { - pub fn new(name: &'static str, description: &'static str) -> Self { - Self { - name, - short: None, - long: None, - description, - required: false, - } - } - - #[allow(dead_code)] - pub fn with_short(mut self, short: char) -> Self { - self.short = Some(short); - self - } - - pub fn with_long(mut self, long: &'static str) -> Self { - self.long = Some(long); - self - } - - #[allow(dead_code)] - pub fn required(mut self) -> Self { - self.required = true; - self - } -} - -impl FlagDef { - pub fn new(name: &'static str, description: &'static str) -> Self { - Self { - name, - short: None, - long: None, - description, - } - } - - pub fn with_short(mut self, short: char) -> Self { - self.short = Some(short); - self - } - - pub fn with_long(mut self, long: &'static str) -> Self { - self.long = Some(long); - self - } -} - -// Command Parser -pub struct CommandParser; - -impl CommandParser { - pub fn parse(input: &str) -> Result { - let tokens = Self::tokenize(input); - if tokens.is_empty() { - return Err(ParseError::UnknownCommand("empty command".to_string())); - } - - // Find the command path - let (command_path, command_node, remaining_tokens) = Self::find_command(&tokens)?; - - // Parse the arguments - let (options, flags, positional_args) = Self::parse_args(remaining_tokens, command_node)?; - - // Validate required options - Self::validate_required_options(command_node, &options)?; - - Ok(ParsedCommand { - command_path, - options, - flags, - positional_args, - }) - } - - fn tokenize(input: &str) -> Vec { - let mut tokens = Vec::new(); - let mut current_token = String::new(); - let mut in_quotes = false; - let mut escape_next = false; - - for ch in input.chars() { - if escape_next { - current_token.push(ch); - escape_next = false; - } else if ch == '\\' { - escape_next = true; - } else if ch == '"' { - in_quotes = !in_quotes; - } else if ch.is_whitespace() && !in_quotes { - if !current_token.is_empty() { - tokens.push(current_token.clone()); - current_token.clear(); - } - } else { - current_token.push(ch); - } - } - - if !current_token.is_empty() { - tokens.push(current_token); - } - - tokens - } - - fn find_command( - tokens: &[String], - ) -> Result<(Vec, &CommandNode, &[String]), ParseError> { - let mut current_node = COMMAND_TREE - .get(&tokens[0]) - .ok_or_else(|| ParseError::UnknownCommand(tokens[0].clone()))?; - - let mut command_path = vec![tokens[0].clone()]; - let mut token_index = 1; - - // Traverse to find the deepest command node - while token_index < tokens.len() { - if let Some(subcommand) = current_node.subcommands.get(&tokens[token_index]) { - current_node = subcommand; - command_path.push(tokens[token_index].clone()); - token_index += 1; - } else { - break; - } - } - - Ok((command_path, current_node, &tokens[token_index..])) - } - - #[allow(clippy::type_complexity)] - fn parse_args( - tokens: &[String], - command_node: &CommandNode, - ) -> Result< - ( - BTreeMap, - BTreeMap, - Vec, - ), - ParseError, - > { - let mut options = BTreeMap::new(); - let mut flags = BTreeMap::new(); - let mut positional_args = Vec::new(); - let mut i = 0; - - while i < tokens.len() { - let token = &tokens[i]; - - if let Some(name) = token.strip_prefix("--") { - // Long options/flags - if let Some(eq_pos) = name.find('=') { - // --option=value format - let (opt_name, value) = name.split_at(eq_pos); - let value = &value[1..]; // Skip '=' - if Self::is_option(opt_name, command_node) { - options.insert(opt_name.to_string(), value.to_string()); - } else { - return Err(ParseError::UnknownOption(format!("--{opt_name}"))); - } - } else if Self::is_flag(name, command_node) { - flags.insert(name.to_string(), true); - } else if Self::is_option(name, command_node) { - // --option value format - if i + 1 >= tokens.len() { - return Err(ParseError::MissingValue(format!("--{name}"))); - } - options.insert(name.to_string(), tokens[i + 1].clone()); - i += 1; // Skip value - } else { - return Err(ParseError::UnknownOption(format!("--{name}"))); - } - } else if token.starts_with('-') && token.len() > 1 { - // Short options/flags - let chars: Vec = token[1..].chars().collect(); - for (j, &ch) in chars.iter().enumerate() { - if Self::is_short_flag(ch, command_node) { - flags.insert( - Self::get_flag_name_by_short(ch, command_node) - .unwrap() - .to_string(), - true, - ); - } else if Self::is_short_option(ch, command_node) { - let opt_name = Self::get_option_name_by_short(ch, command_node).unwrap(); - if j == chars.len() - 1 && i + 1 < tokens.len() { - // Last character and there is a next token as value - options.insert(opt_name.to_string(), tokens[i + 1].clone()); - i += 1; // Skip value - } else { - return Err(ParseError::MissingValue(format!("-{ch}"))); - } - } else { - return Err(ParseError::UnknownOption(format!("-{ch}"))); - } - } - } else { - // Positional arguments - positional_args.push(token.clone()); - } - i += 1; - } - - Ok((options, flags, positional_args)) - } - - fn is_option(name: &str, node: &CommandNode) -> bool { - node.options - .iter() - .any(|opt| (opt.long == Some(name)) || opt.name == name) - } - - fn is_flag(name: &str, node: &CommandNode) -> bool { - node.flags - .iter() - .any(|flag| (flag.long == Some(name)) || flag.name == name) - } - - fn is_short_option(ch: char, node: &CommandNode) -> bool { - node.options.iter().any(|opt| opt.short == Some(ch)) - } - - fn is_short_flag(ch: char, node: &CommandNode) -> bool { - node.flags.iter().any(|flag| flag.short == Some(ch)) - } - - fn get_option_name_by_short(ch: char, node: &CommandNode) -> Option<&str> { - node.options - .iter() - .find(|opt| opt.short == Some(ch)) - .map(|opt| opt.name) - } - - fn get_flag_name_by_short(ch: char, node: &CommandNode) -> Option<&str> { - node.flags - .iter() - .find(|flag| flag.short == Some(ch)) - .map(|flag| flag.name) - } - - fn validate_required_options( - node: &CommandNode, - options: &BTreeMap, - ) -> Result<(), ParseError> { - for option in &node.options { - if option.required && !options.contains_key(option.name) { - return Err(ParseError::MissingRequiredOption(option.name.to_string())); - } - } - Ok(()) - } -} - -// Command execution function -pub fn execute_command(input: &str) -> Result<(), ParseError> { - let parsed = CommandParser::parse(input)?; - - // Find the corresponding command node - let mut current_node = COMMAND_TREE.get(&parsed.command_path[0]).unwrap(); - for cmd in &parsed.command_path[1..] { - current_node = current_node.subcommands.get(cmd).unwrap(); - } - - // Execute the command - if let Some(handler) = current_node.handler { - handler(&parsed); - Ok(()) - } else { - Err(ParseError::NoHandler(parsed.command_path.join(" "))) - } -} - -// Build command tree -fn build_command_tree() -> BTreeMap { - let mut tree = BTreeMap::new(); - - build_base_cmd(&mut tree); - build_vm_cmd(&mut tree); - - tree -} - -// Helper function: Display command help -pub fn show_help(command_path: &[String]) -> Result<(), ParseError> { - let mut current_node = COMMAND_TREE - .get(&command_path[0]) - .ok_or_else(|| ParseError::UnknownCommand(command_path[0].clone()))?; - - for cmd in &command_path[1..] { - current_node = current_node - .subcommands - .get(cmd) - .ok_or_else(|| ParseError::UnknownCommand(cmd.clone()))?; - } - - println!("Command: {}", command_path.join(" ")); - println!("Description: {}", current_node.description); - - if let Some(usage) = current_node.usage { - println!("Usage: {}", usage); - } - - if !current_node.options.is_empty() { - println!("\nOptions:"); - for option in ¤t_node.options { - let mut opt_str = String::new(); - if let Some(short) = option.short { - opt_str.push_str(&format!("-{short}")); - } - if let Some(long) = option.long { - if !opt_str.is_empty() { - opt_str.push_str(", "); - } - opt_str.push_str(&format!("--{long}")); - } - if opt_str.is_empty() { - opt_str = option.name.to_string(); - } - - let required_str = if option.required { " (required)" } else { "" }; - println!(" {:<20} {}{}", opt_str, option.description, required_str); - } - } - - if !current_node.flags.is_empty() { - println!("\nFlags:"); - for flag in ¤t_node.flags { - let mut flag_str = String::new(); - if let Some(short) = flag.short { - flag_str.push_str(&format!("-{short}")); - } - if let Some(long) = flag.long { - if !flag_str.is_empty() { - flag_str.push_str(", "); - } - flag_str.push_str(&format!("--{long}")); - } - if flag_str.is_empty() { - flag_str = flag.name.to_string(); - } - - println!(" {:<20} {}", flag_str, flag.description); - } - } - - if !current_node.subcommands.is_empty() { - println!("\nSubcommands:"); - for (name, node) in ¤t_node.subcommands { - println!(" {:<20} {}", name, node.description); - } - } - - Ok(()) -} - -pub fn print_prompt() { - #[cfg(feature = "fs")] - print!("axvisor:{}$ ", std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - print!("axvisor:$ "); - std::io::stdout().flush().unwrap(); -} - -pub fn run_cmd_bytes(cmd_bytes: &[u8]) { - match str::from_utf8(cmd_bytes) { - Ok(cmd_str) => { - let trimmed = cmd_str.trim(); - if trimmed.is_empty() { - return; - } - - match execute_command(trimmed) { - Ok(_) => { - // Command executed successfully - } - Err(ParseError::UnknownCommand(cmd)) => { - println!("Error: Unknown command '{}'", cmd); - println!("Type 'help' to see available commands"); - } - Err(ParseError::UnknownOption(opt)) => { - println!("Error: Unknown option '{}'", opt); - } - Err(ParseError::MissingValue(opt)) => { - println!("Error: Option '{}' is missing a value", opt); - } - Err(ParseError::MissingRequiredOption(opt)) => { - println!("Error: Missing required option '{}'", opt); - } - Err(ParseError::NoHandler(cmd)) => { - println!("Error: Command '{}' has no handler function", cmd); - } - } - } - Err(_) => { - println!("Error: Input contains invalid UTF-8 characters"); - } - } -} - -// Built-in command handler -pub fn handle_builtin_commands(input: &str) -> bool { - match input.trim() { - "help" => { - show_available_commands(); - true - } - "exit" | "quit" => { - println!("Goodbye!"); - std::process::exit(0); - } - "clear" => { - print!("\x1b[2J\x1b[H"); // ANSI clear screen sequence - std::io::stdout().flush().unwrap(); - true - } - _ if input.starts_with("help ") => { - let cmd_parts: Vec = input[5..] - .split_whitespace() - .map(|s| s.to_string()) - .collect(); - if let Err(e) = show_help(&cmd_parts) { - println!("Error: {:?}", e); - } - true - } - _ => false, - } -} - -pub fn show_available_commands() { - println!("ArceOS Shell - Available Commands:"); - println!(); - - // Display all top-level commands - for (name, node) in COMMAND_TREE.iter() { - println!(" {:<15} {}", name, node.description); - - // Display subcommands - if !node.subcommands.is_empty() { - for (sub_name, sub_node) in &node.subcommands { - println!(" {:<13} {}", sub_name, sub_node.description); - } - } - } - - println!(); - println!("Built-in Commands:"); - println!(" help Show help information"); - println!(" help Show help for a specific command"); - println!(" clear Clear the screen"); - println!(" exit/quit Exit the shell"); - println!(); - println!("Tip: Use 'help ' to see detailed usage of a command"); -} diff --git a/os/axvisor/src/shell/command/vm.rs b/os/axvisor/src/shell/command/vm.rs deleted file mode 100644 index c3d827405..000000000 --- a/os/axvisor/src/shell/command/vm.rs +++ /dev/null @@ -1,1409 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::{ - collections::btree_map::BTreeMap, - println, - string::{String, ToString}, - vec::Vec, -}; - -use axvm::VMStatus; -#[cfg(feature = "fs")] -use std::fs::read_to_string; - -use crate::{ - shell::command::{CommandNode, FlagDef, OptionDef, ParsedCommand}, - vmm::{add_running_vm_count, vcpus, vm_list, with_vm}, -}; - -/// Check if a VM can transition to Running state. -/// Returns Ok(()) if the transition is valid, Err with a message otherwise. -fn can_start_vm(status: VMStatus) -> Result<(), &'static str> { - match status { - VMStatus::Loaded | VMStatus::Stopped => Ok(()), - VMStatus::Running => Err("VM is already running"), - VMStatus::Suspended => Err("VM is suspended, use 'vm resume' instead"), - VMStatus::Stopping => Err("VM is stopping, wait for it to fully stop"), - VMStatus::Loading => Err("VM is still loading"), - } -} - -/// Check if a VM can transition to Stopping state. -/// Returns Ok(()) if the transition is valid, Err with a message otherwise. -fn can_stop_vm(status: VMStatus, force: bool) -> Result<(), &'static str> { - match status { - VMStatus::Running | VMStatus::Suspended => Ok(()), - VMStatus::Stopping => { - if force { - Ok(()) - } else { - Err("VM is already stopping") - } - } - VMStatus::Stopped => Err("VM is already stopped"), - VMStatus::Loading | VMStatus::Loaded => Ok(()), // Allow stopping VMs in these states - } -} - -/// Check if a VM can be suspended. -fn can_suspend_vm(status: VMStatus) -> Result<(), &'static str> { - match status { - VMStatus::Running => Ok(()), - VMStatus::Suspended => Err("VM is already suspended"), - VMStatus::Stopped => Err("VM is stopped, cannot suspend"), - VMStatus::Stopping => Err("VM is stopping, cannot suspend"), - VMStatus::Loading => Err("VM is loading, cannot suspend"), - VMStatus::Loaded => Err("VM is not running, cannot suspend"), - } -} - -/// Check if a VM can be resumed. -fn can_resume_vm(status: VMStatus) -> Result<(), &'static str> { - match status { - VMStatus::Suspended => Ok(()), - VMStatus::Running => Err("VM is already running"), - VMStatus::Stopped => Err("VM is stopped, use 'vm start' instead"), - VMStatus::Stopping => Err("VM is stopping, cannot resume"), - VMStatus::Loading => Err("VM is loading, cannot resume"), - VMStatus::Loaded => Err("VM is not started yet, use 'vm start' instead"), - } -} - -/// Format memory size in a human-readable way. -fn format_memory_size(bytes: usize) -> String { - if bytes < 1024 { - format!("{}B", bytes) - } else if bytes < 1024 * 1024 { - format!("{}KB", bytes / 1024) - } else if bytes < 1024 * 1024 * 1024 { - format!("{}MB", bytes / (1024 * 1024)) - } else { - format!("{}GB", bytes / (1024 * 1024 * 1024)) - } -} - -// ============================================================================ -// Command Handlers -// ============================================================================ - -fn vm_help(_cmd: &ParsedCommand) { - println!("VM - virtual machine management"); - println!(); - println!("Most commonly used vm commands:"); - println!(" create Create a new virtual machine"); - println!(" start Start a virtual machine"); - println!(" stop Stop a virtual machine"); - println!(" suspend Suspend (pause) a running virtual machine"); - println!(" resume Resume a suspended virtual machine"); - println!(" restart Restart a virtual machine"); - println!(" delete Delete a virtual machine"); - println!(); - println!("Information commands:"); - println!(" list Show table of all VMs"); - println!(" show Show VM details (requires VM_ID)"); - println!(" - Default: basic information"); - println!(" - --full: complete detailed information"); - println!(" - --config: show configuration"); - println!(" - --stats: show statistics"); - println!(); - println!("Use 'vm --help' for more information on a specific command."); -} - -#[cfg(feature = "fs")] -fn vm_create(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - println!("Positional args: {:?}", args); - - if args.is_empty() { - println!("Error: No VM configuration file specified"); - println!("Usage: vm create [CONFIG_FILE]"); - return; - } - - let initial_vm_count = vm_list::get_vm_list().len(); - - for config_path in args.iter() { - println!("Creating VM from config: {}", config_path); - - use crate::vmm::config::init_guest_vm; - match read_to_string(config_path) { - Ok(raw_cfg) => match init_guest_vm(&raw_cfg) { - Ok(vm_id) => { - println!( - "✓ Successfully created VM[{}] from config: {}", - vm_id, config_path - ); - } - Err(_) => { - println!( - "✗ Failed to create VM from {}: Configuration error or panic occurred", - config_path - ); - } - }, - Err(e) => { - println!("✗ Failed to read config file {}: {:?}", config_path, e); - } - } - } - - // Check the actual number of VMs created - let final_vm_count = vm_list::get_vm_list().len(); - let created_count = final_vm_count - initial_vm_count; - - if created_count > 0 { - println!("Successfully created {} VM(s)", created_count); - println!("Use 'vm start ' to start the created VMs."); - } else { - println!("No VMs were created."); - } -} - -#[cfg(feature = "fs")] -fn vm_start(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let detach = cmd.flags.get("detach").unwrap_or(&false); - - if args.is_empty() { - // start all VMs - info!("VMM starting, booting all VMs..."); - let mut started_count = 0; - - for vm in vm_list::get_vm_list() { - // Check current status before starting - let status: VMStatus = vm.vm_status(); - if status == VMStatus::Running { - println!("⚠ VM[{}] is already running, skipping", vm.id()); - continue; - } - - if status != VMStatus::Loaded && status != VMStatus::Stopped { - println!("⚠ VM[{}] is in {:?} state, cannot start", vm.id(), status); - continue; - } - - if let Err(e) = start_single_vm(vm.clone()) { - println!("✗ VM[{}] failed to start: {:?}", vm.id(), e); - } else { - println!("✓ VM[{}] started successfully", vm.id()); - started_count += 1; - } - } - println!("Started {} VM(s)", started_count); - } else { - // Start specified VMs - for vm_name in args { - // Try to parse as VM ID or lookup VM name - if let Ok(vm_id) = vm_name.parse::() { - start_vm_by_id(vm_id); - } else { - println!("Error: VM name lookup not implemented. Use VM ID instead."); - println!("Available VMs:"); - vm_list_simple(); - } - } - } - - if *detach { - println!("VMs started in background mode"); - } -} - -/// Start a single VM by setting up vCPUs and calling boot. -/// Returns Ok(()) if successful, Err otherwise. -fn start_single_vm(vm: crate::vmm::VMRef) -> Result<(), &'static str> { - let vm_id = vm.id(); - let status = vm.vm_status(); - - // Validate state transition using helper function - can_start_vm(status)?; - - // Set up primary virtual CPU before starting - vcpus::setup_vm_primary_vcpu(vm.clone()); - - // Boot the VM - match vm.boot() { - Ok(_) => { - // Transition to Running state and notify the primary VCpu - // Note: Since the VCpu task is created directly in the wait queue (blocked state), - // we can immediately notify it without waiting for it to be scheduled first. - vcpus::notify_primary_vcpu(vm_id); - add_running_vm_count(1); - Ok(()) - } - Err(err) => { - // Revert status on failure - error!("Failed to boot VM[{}]: {:?}", vm_id, err); - Err("Failed to boot VM") - } - } -} - -fn start_vm_by_id(vm_id: usize) { - match with_vm(vm_id, |vm| start_single_vm(vm.clone())) { - Some(Ok(_)) => { - println!("✓ VM[{}] started successfully", vm_id); - } - Some(Err(err)) => { - println!("✗ VM[{}] failed to start: {}", vm_id, err); - } - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -fn vm_stop(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let force = cmd.flags.get("force").unwrap_or(&false); - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm stop [OPTIONS] "); - return; - } - - for vm_name in args { - if let Ok(vm_id) = vm_name.parse::() { - stop_vm_by_id(vm_id, *force); - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } - } -} - -fn stop_vm_by_id(vm_id: usize, force: bool) { - match with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - // Validate state transition using helper function - if let Err(err) = can_stop_vm(status, force) { - println!("⚠ VM[{}] {}", vm_id, err); - return Err(err); - } - - // Print appropriate message based on status - match status { - VMStatus::Stopping if force => { - println!("Force stopping VM[{}]...", vm_id); - } - VMStatus::Running => { - if force { - println!("Force stopping VM[{}]...", vm_id); - } else { - println!("Gracefully stopping VM[{}]...", vm_id); - } - } - VMStatus::Loading | VMStatus::Loaded => { - println!( - "⚠ VM[{}] is in {:?} state, stopping anyway...", - vm_id, status - ); - } - _ => {} - } - - // Call shutdown - match vm.shutdown() { - Ok(_) => Ok(()), - Err(_err) => { - // Revert status on failure - Err("Failed to shutdown VM") - } - } - }) { - Some(Ok(_)) => { - println!("✓ VM[{}] stop signal sent successfully", vm_id); - println!( - " Note: vCPU threads will exit gracefully, VM status will transition to Stopped" - ); - } - Some(Err(err)) => { - println!("✗ Failed to stop VM[{}]: {:?}", vm_id, err); - } - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -/// Restart a VM by stopping it (if running) and then starting it again.(functionality incomplete) -fn vm_restart(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let force = cmd.flags.get("force").unwrap_or(&false); - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm restart [OPTIONS] "); - return; - } - - for vm_name in args { - if let Ok(vm_id) = vm_name.parse::() { - restart_vm_by_id(vm_id, *force); - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } - } -} - -fn restart_vm_by_id(vm_id: usize, force: bool) { - println!("Restarting VM[{}]...", vm_id); - - // Check current status - let current_status = with_vm(vm_id, |vm| vm.vm_status()); - if current_status.is_none() { - println!("✗ VM[{}] not found", vm_id); - return; - } - - let status = current_status.unwrap(); - match status { - VMStatus::Stopped | VMStatus::Loaded => { - // VM is already stopped, just start it - println!("VM[{}] is already stopped, starting...", vm_id); - start_vm_by_id(vm_id); - } - VMStatus::Suspended | VMStatus::Running => { - // Stop the VM (this will wake up suspended VCpus automatically) - println!("Stopping VM[{}]...", vm_id); - stop_vm_by_id(vm_id, force); - - // Wait for VM to fully stop - println!("Waiting for VM[{}] to stop completely...", vm_id); - let max_wait_iterations = 50; // 5 seconds timeout (50 * 100ms) - let mut iterations = 0; - - loop { - if let Some(vm_status) = with_vm(vm_id, |vm| vm.vm_status()) { - match vm_status { - VMStatus::Stopped => { - println!("✓ VM[{}] stopped successfully", vm_id); - break; - } - VMStatus::Stopping => { - // Still stopping, wait a bit - iterations += 1; - if iterations >= max_wait_iterations { - println!( - "⚠ VM[{}] stop timeout, it may still be shutting down", - vm_id - ); - println!(" Use 'vm status {}' to check status manually", vm_id); - return; - } - // Sleep for 100ms - std::os::arceos::modules::axhal::time::busy_wait( - core::time::Duration::from_millis(100), - ); - } - _ => { - println!("⚠ VM[{}] in unexpected state: {:?}", vm_id, vm_status); - return; - } - } - } else { - println!("✗ VM[{}] no longer exists", vm_id); - return; - } - } - - // Now restart the VM - println!("Starting VM[{}]...", vm_id); - start_vm_by_id(vm_id); - } - VMStatus::Stopping => { - if force { - println!( - "⚠ VM[{}] is currently stopping, waiting for shutdown to complete...", - vm_id - ); - // Could implement similar wait logic here if needed - } else { - println!("⚠ VM[{}] is currently stopping", vm_id); - println!( - " Wait for shutdown to complete, then use 'vm start {}'", - vm_id - ); - println!(" Or use --force to wait and then restart"); - } - } - VMStatus::Loading => { - println!("✗ VM[{}] is still loading, cannot restart", vm_id); - } - } -} - -/// Suspend a running VM (functionality incomplete) -fn vm_suspend(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm suspend ..."); - return; - } - - for vm_name in args { - if let Ok(vm_id) = vm_name.parse::() { - suspend_vm_by_id(vm_id); - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } - } -} - -fn suspend_vm_by_id(vm_id: usize) { - println!("Suspending VM[{}]...", vm_id); - - let result: Option> = with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - // Check if VM can be suspended - can_suspend_vm(status)?; - - // Set VM status to Suspended - vm.set_vm_status(VMStatus::Suspended); - info!("VM[{}] status set to Suspended", vm_id); - - Ok(()) - }); - - match result { - Some(Ok(_)) => { - println!("✓ VM[{}] suspend signal sent", vm_id); - - // Get VM to check VCpu count - let vcpu_count = with_vm(vm_id, |vm| vm.vcpu_num()).unwrap_or(0); - println!( - " Note: {} VCpu task(s) will enter wait queue at next VMExit", - vcpu_count - ); - - // Wait a brief moment for VCpus to enter suspended state - println!(" Waiting for VCpus to suspend..."); - let max_wait_iterations = 10; // 1 second timeout (10 * 100ms) - let mut iterations = 0; - let mut all_suspended = false; - - while iterations < max_wait_iterations { - // Check if all VCpus are in blocked state - if let Some(vm) = crate::vmm::vm_list::get_vm_by_id(vm_id) { - let vcpu_states: Vec<_> = - vm.vcpu_list().iter().map(|vcpu| vcpu.state()).collect(); - - let blocked_count = vcpu_states - .iter() - .filter(|s| matches!(s, axvcpu::VCpuState::Blocked)) - .count(); - - if blocked_count == vcpu_states.len() { - all_suspended = true; - break; - } - - // Show progress for the first few iterations - if iterations < 3 { - debug!(" VCpus blocked: {}/{}", blocked_count, vcpu_states.len()); - } - } - - iterations += 1; - std::os::arceos::modules::axhal::time::busy_wait( - core::time::Duration::from_millis(100), - ); - } - - if all_suspended { - println!("✓ All VCpu tasks are now suspended"); - } else { - println!("⚠ Some VCpu tasks may still be transitioning to suspended state"); - println!(" VCpus will suspend at next VMExit (timer interrupt, I/O, etc.)"); - println!(" This is normal for VMs with low interrupt rates"); - } - - println!(" Use 'vm resume {}' to resume the VM", vm_id); - } - Some(Err(err)) => { - println!("✗ Failed to suspend VM[{}]: {}", vm_id, err); - } - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -// Resume a suspended VM (functionality incomplete) -fn vm_resume(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm resume ..."); - return; - } - - for vm_name in args { - if let Ok(vm_id) = vm_name.parse::() { - resume_vm_by_id(vm_id); - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } - } -} - -fn resume_vm_by_id(vm_id: usize) { - println!("Resuming VM[{}]...", vm_id); - - let result: Option> = with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - // Check if VM can be resumed - can_resume_vm(status)?; - - // Set VM status back to Running - vm.set_vm_status(VMStatus::Running); - - // Notify all VCpus to wake up - vcpus::notify_all_vcpus(vm_id); - - info!("VM[{}] resumed", vm_id); - Ok(()) - }); - - match result { - Some(Ok(_)) => { - println!("✓ VM[{}] resumed successfully", vm_id); - } - Some(Err(err)) => { - println!("✗ Failed to resume VM[{}]: {}", vm_id, err); - } - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -fn vm_delete(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let force = cmd.flags.get("force").unwrap_or(&false); - let keep_data = cmd.flags.get("keep-data").unwrap_or(&false); - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm delete [OPTIONS] "); - return; - } - - let vm_name = &args[0]; - - if let Ok(vm_id) = vm_name.parse::() { - // Check if VM exists and get its status first - let vm_status = with_vm(vm_id, |vm| vm.vm_status()); - - if vm_status.is_none() { - println!("✗ VM[{}] not found", vm_id); - return; - } - - let status = vm_status.unwrap(); - - // Check if VM is running - match status { - VMStatus::Running => { - if !force { - println!("✗ VM[{}] is currently running", vm_id); - println!( - " Use 'vm stop {}' first, or use '--force' to force delete", - vm_id - ); - return; - } - println!("⚠ Force deleting running VM[{}]...", vm_id); - } - VMStatus::Stopping => { - if !force { - println!("⚠ VM[{}] is currently stopping", vm_id); - println!(" Wait for it to stop completely, or use '--force' to force delete"); - return; - } - println!("⚠ Force deleting stopping VM[{}]...", vm_id); - } - VMStatus::Stopped => { - println!("Deleting stopped VM[{}]...", vm_id); - } - _ => { - println!("⚠ VM[{}] is in {:?} state", vm_id, status); - if !force { - println!("Use --force to force delete"); - return; - } - } - } - - delete_vm_by_id(vm_id, *keep_data); - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } -} - -fn delete_vm_by_id(vm_id: usize, keep_data: bool) { - // First check VM status and try to stop it if running - let vm_status = with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - // If VM is running, suspended, or stopping, send shutdown signal - match status { - VMStatus::Running | VMStatus::Suspended | VMStatus::Stopping => { - println!( - " VM[{}] is {:?}, sending shutdown signal...", - vm_id, status - ); - vm.set_vm_status(VMStatus::Stopping); - let _ = vm.shutdown(); - } - VMStatus::Loaded => { - // Transition from Loaded to Stopped - vm.set_vm_status(VMStatus::Stopped); - } - _ => {} - } - - use alloc::sync::Arc; - let count = Arc::strong_count(&vm); - println!(" [Debug] VM Arc strong_count: {}", count); - - status - }); - - if vm_status.is_none() { - println!("✗ VM[{}] not found or already removed", vm_id); - return; - } - - let status = vm_status.unwrap(); - - // Remove VM from global list - // Note: This drops the reference from the global list, but the VM object - // will only be fully destroyed when all vCPU threads exit and drop their references - match crate::vmm::vm_list::remove_vm(vm_id) { - Some(vm) => { - println!("✓ VM[{}] removed from VM list", vm_id); - - // Wait for vCPU threads to exit if VM has VCpu tasks - match status { - VMStatus::Running - | VMStatus::Suspended - | VMStatus::Stopping - | VMStatus::Stopped => { - println!(" Waiting for vCPU threads to exit..."); - - // Debug: Check Arc count before cleanup - use alloc::sync::Arc; - println!( - " [Debug] VM Arc count before cleanup: {}", - Arc::strong_count(&vm) - ); - - // Clean up VCpu resources after threads have exited - println!(" Cleaning up VCpu resources..."); - vcpus::cleanup_vm_vcpus(vm_id); - - // Debug: Check Arc count after final wait - println!( - " [Debug] VM Arc count after final wait: {}", - Arc::strong_count(&vm) - ); - } - _ => { - // VM not running, no vCPU threads to wait for - // But still need to clean up VCpu queue entry if it exists - vcpus::cleanup_vm_vcpus(vm_id); - } - } - - if keep_data { - println!("✓ VM[{}] deleted (configuration and data preserved)", vm_id); - } else { - println!("✓ VM[{}] deleted completely", vm_id); - - // Debug: Check Arc count - should be 1 now (only this variable) - // TaskExt uses Weak reference, so it doesn't count - use alloc::sync::Arc; - let count = Arc::strong_count(&vm); - println!(" [Debug] VM Arc strong_count: {}", count); - - if count == 1 { - println!(" ✓ Perfect! VM will be freed immediately when function returns"); - } else { - println!( - " ⚠ Warning: Unexpected Arc count {}, possible reference leak!", - count - ); - } - - // TODO: Clean up VM-related data files - // - Remove disk images - // - Remove configuration files - // - Remove log files - } - - // When function returns, the 'vm' variable is dropped - // Since Arc count is 1, AxVM::drop() is called immediately - println!(" VM[{}] will be freed now", vm_id); - } - None => { - println!( - "✗ Failed to remove VM[{}] from list (may have been removed already)", - vm_id - ); - } - } - - // When function returns, the 'vm' Arc is dropped - // If all vCPU threads have exited (ref_count was 1), AxVM::drop() is called here - println!("✓ VM[{}] deletion completed", vm_id); -} - -#[cfg(feature = "fs")] -fn vm_list_simple() { - let vms = vm_list::get_vm_list(); - println!("ID NAME STATE VCPU MEMORY"); - println!("---- ----------- ------- ---- ------"); - for vm in vms { - let status = vm.vm_status(); - - // Calculate total memory size - let total_memory: usize = vm.memory_regions().iter().map(|region| region.size()).sum(); - - println!( - "{:<4} {:<11} {:<7} {:<4} {}", - vm.id(), - vm.with_config(|cfg| cfg.name()), - status.as_str(), - vm.vcpu_num(), - format_memory_size(total_memory) - ); - } -} - -fn vm_list(cmd: &ParsedCommand) { - let binding = "table".to_string(); - let format = cmd.options.get("format").unwrap_or(&binding); - - let display_vms = vm_list::get_vm_list(); - - if display_vms.is_empty() { - println!("No virtual machines found."); - return; - } - - if format == "json" { - // JSON output - println!("{{"); - println!(" \"vms\": ["); - for (i, vm) in display_vms.iter().enumerate() { - let status = vm.vm_status(); - let total_memory: usize = vm.memory_regions().iter().map(|region| region.size()).sum(); - - println!(" {{"); - println!(" \"id\": {},", vm.id()); - println!(" \"name\": \"{}\",", vm.with_config(|cfg| cfg.name())); - println!(" \"state\": \"{}\",", status.as_str()); - println!(" \"vcpu\": {},", vm.vcpu_num()); - println!(" \"memory\": \"{}\"", format_memory_size(total_memory)); - - if i < display_vms.len() - 1 { - println!(" }},"); - } else { - println!(" }}"); - } - } - println!(" ]"); - println!("}}"); - } else { - // Table output (default) - println!( - "{:<6} {:<15} {:<12} {:<15} {:<10} {:<20}", - "VM ID", "NAME", "STATUS", "VCPU", "MEMORY", "VCPU STATE" - ); - println!( - "{:-<6} {:-<15} {:-<12} {:-<15} {:-<10} {:-<20}", - "", "", "", "", "", "" - ); - - for vm in display_vms { - let status = vm.vm_status(); - let total_memory: usize = vm.memory_regions().iter().map(|region| region.size()).sum(); - - // Get VCpu ID list - let vcpu_ids: Vec = vm - .vcpu_list() - .iter() - .map(|vcpu| vcpu.id().to_string()) - .collect(); - let vcpu_id_list = vcpu_ids.join(","); - - // Get VCpu state summary - let mut state_counts = std::collections::BTreeMap::new(); - for vcpu in vm.vcpu_list() { - let state = match vcpu.state() { - axvcpu::VCpuState::Free => "Free", - axvcpu::VCpuState::Running => "Run", - axvcpu::VCpuState::Blocked => "Blk", - axvcpu::VCpuState::Invalid => "Inv", - axvcpu::VCpuState::Created => "Cre", - axvcpu::VCpuState::Ready => "Rdy", - }; - *state_counts.entry(state).or_insert(0) += 1; - } - - // Format: Run:2,Blk:1 - let summary: Vec = state_counts - .iter() - .map(|(state, count)| format!("{}:{}", state, count)) - .collect(); - let vcpu_state_summary = summary.join(","); - - println!( - "{:<6} {:<15} {:<12} {:<15} {:<10} {:<20}", - vm.id(), - vm.with_config(|cfg| cfg.name()), - status.as_str(), - vcpu_id_list, - format_memory_size(total_memory), - vcpu_state_summary - ); - } - } -} - -fn vm_show(cmd: &ParsedCommand) { - let args = &cmd.positional_args; - let show_config = cmd.flags.get("config").unwrap_or(&false); - let show_stats = cmd.flags.get("stats").unwrap_or(&false); - let show_full = cmd.flags.get("full").unwrap_or(&false); - - if args.is_empty() { - println!("Error: No VM specified"); - println!("Usage: vm show [OPTIONS] "); - println!(); - println!("Options:"); - println!(" -f, --full Show full detailed information"); - println!(" -c, --config Show configuration details"); - println!(" -s, --stats Show statistics"); - println!(); - println!("Use 'vm list' to see all VMs"); - return; - } - - // Show specific VM details - let vm_name = &args[0]; - if let Ok(vm_id) = vm_name.parse::() { - if *show_full { - show_vm_full_details(vm_id); - } else { - show_vm_basic_details(vm_id, *show_config, *show_stats); - } - } else { - println!("Error: Invalid VM ID: {}", vm_name); - } -} - -/// Show basic VM information (default view) -fn show_vm_basic_details(vm_id: usize, show_config: bool, show_stats: bool) { - match with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - println!("VM Details: {}", vm_id); - println!(); - - // Basic Information - println!(" VM ID: {}", vm.id()); - println!(" Name: {}", vm.with_config(|cfg| cfg.name())); - println!(" Status: {}", status.as_str_with_icon()); - println!(" VCPUs: {}", vm.vcpu_num()); - - // Calculate total memory - let total_memory: usize = vm.memory_regions().iter().map(|region| region.size()).sum(); - println!(" Memory: {}", format_memory_size(total_memory)); - - // Add state-specific information - match status { - VMStatus::Suspended => { - println!(); - println!(" ℹ VM is paused. Use 'vm resume {}' to continue.", vm_id); - } - VMStatus::Stopped => { - println!(); - println!(" ℹ VM is stopped. Use 'vm delete {}' to clean up.", vm_id); - } - VMStatus::Loaded => { - println!(); - println!(" ℹ VM is ready. Use 'vm start {}' to boot.", vm_id); - } - _ => {} - } - - // VCPU Summary - println!(); - println!("VCPU Summary:"); - let mut state_counts = std::collections::BTreeMap::new(); - for vcpu in vm.vcpu_list() { - let state = match vcpu.state() { - axvcpu::VCpuState::Free => "Free", - axvcpu::VCpuState::Running => "Running", - axvcpu::VCpuState::Blocked => "Blocked", - axvcpu::VCpuState::Invalid => "Invalid", - axvcpu::VCpuState::Created => "Created", - axvcpu::VCpuState::Ready => "Ready", - }; - *state_counts.entry(state).or_insert(0) += 1; - } - - for (state, count) in state_counts { - println!(" {}: {}", state, count); - } - - // Memory Summary - println!(); - println!("Memory Summary:"); - println!(" Total Regions: {}", vm.memory_regions().len()); - println!(" Total Size: {}", format_memory_size(total_memory)); - - // Configuration Summary - if show_config { - println!(); - println!("Configuration:"); - vm.with_config(|cfg| { - println!(" BSP Entry: {:#x}", cfg.bsp_entry().as_usize()); - println!(" AP Entry: {:#x}", cfg.ap_entry().as_usize()); - println!(" Interrupt Mode: {:?}", cfg.interrupt_mode()); - if let Some(dtb_addr) = cfg.image_config().dtb_load_gpa { - println!(" DTB Address: {:#x}", dtb_addr.as_usize()); - } - }); - } - - // Device Summary - if show_stats { - println!(); - println!("Device Summary:"); - println!( - " MMIO Devices: {}", - vm.get_devices().iter_mmio_dev().count() - ); - println!( - " SysReg Devices: {}", - vm.get_devices().iter_sys_reg_dev().count() - ); - } - - println!(); - println!("Use 'vm show {} --full' for detailed information", vm_id); - }) { - Some(_) => {} - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -/// Show full detailed information about a specific VM (--full flag) -fn show_vm_full_details(vm_id: usize) { - match with_vm(vm_id, |vm| { - let status = vm.vm_status(); - - println!("=== VM Details: {} ===", vm_id); - println!(); - - // Basic Information - println!("Basic Information:"); - println!(" VM ID: {}", vm.id()); - println!(" Name: {}", vm.with_config(|cfg| cfg.name())); - println!(" Status: {}", status.as_str_with_icon()); - println!(" VCPUs: {}", vm.vcpu_num()); - - // Calculate total memory - let total_memory: usize = vm.memory_regions().iter().map(|region| region.size()).sum(); - println!(" Memory: {}", format_memory_size(total_memory)); - println!(" EPT Root: {:#x}", vm.ept_root().as_usize()); - - // Add state-specific information - match status { - VMStatus::Suspended => { - println!( - " ℹ VM is paused, VCpu tasks are waiting. Use 'vm resume {}' to continue.", - vm_id - ); - } - VMStatus::Stopping => { - println!(" ℹ VM is shutting down, VCpu tasks are exiting."); - } - VMStatus::Stopped => { - println!( - " ℹ VM is stopped, all VCpu tasks have exited. Use 'vm delete {}' to clean up.", - vm_id - ); - } - VMStatus::Loaded => { - println!( - " ℹ VM is ready to start. Use 'vm start {}' to boot.", - vm_id - ); - } - _ => {} - } - - // VCPU Details - println!(); - println!("VCPU Details:"); - - // Count VCpu states for summary - let mut state_counts = std::collections::BTreeMap::new(); - for vcpu in vm.vcpu_list() { - let state = match vcpu.state() { - axvcpu::VCpuState::Free => "Free", - axvcpu::VCpuState::Running => "Running", - axvcpu::VCpuState::Blocked => "Blocked", - axvcpu::VCpuState::Invalid => "Invalid", - axvcpu::VCpuState::Created => "Created", - axvcpu::VCpuState::Ready => "Ready", - }; - *state_counts.entry(state).or_insert(0) += 1; - } - - // Show summary first - let summary: Vec = state_counts - .iter() - .map(|(state, count)| format!("{}: {}", state, count)) - .collect(); - println!(" Summary: {}", summary.join(", ")); - println!(); - - for vcpu in vm.vcpu_list() { - let vcpu_state = match vcpu.state() { - axvcpu::VCpuState::Free => "Free", - axvcpu::VCpuState::Running => "Running", - axvcpu::VCpuState::Blocked => "Blocked", - axvcpu::VCpuState::Invalid => "Invalid", - axvcpu::VCpuState::Created => "Created", - axvcpu::VCpuState::Ready => "Ready", - }; - - if let Some(phys_cpu_set) = vcpu.phys_cpu_set() { - println!( - " VCPU {}: {} (Affinity: {:#x})", - vcpu.id(), - vcpu_state, - phys_cpu_set - ); - } else { - println!(" VCPU {}: {} (No affinity)", vcpu.id(), vcpu_state); - } - } - - // Add note for Suspended VMs - if status == VMStatus::Suspended { - println!(); - println!( - " Note: VCpu tasks are blocked in wait queue and will resume when VM is unpaused." - ); - } - - // Memory Regions - println!(); - println!( - "Memory Regions: ({} region(s), {} total)", - vm.memory_regions().len(), - format_memory_size(total_memory) - ); - for (i, region) in vm.memory_regions().iter().enumerate() { - let region_type = if region.needs_dealloc { - "Allocated" - } else { - "Reserved" - }; - let identical = if region.is_identical() { - " [identical]" - } else { - "" - }; - println!( - " Region {}: GPA={:#x} HVA={:#x} Size={} Type={}{}", - i, - region.gpa, - region.hva, - format_memory_size(region.size()), - region_type, - identical - ); - } - - // Configuration - println!(); - println!("Configuration:"); - vm.with_config(|cfg| { - println!(" BSP Entry: {:#x}", cfg.bsp_entry().as_usize()); - println!(" AP Entry: {:#x}", cfg.ap_entry().as_usize()); - println!(" Interrupt Mode: {:?}", cfg.interrupt_mode()); - - if let Some(dtb_addr) = cfg.image_config().dtb_load_gpa { - println!(" DTB Address: {:#x}", dtb_addr.as_usize()); - } - - // Show kernel info - println!( - " Kernel GPA: {:#x}", - cfg.image_config().kernel_load_gpa.as_usize() - ); - - // Show passthrough devices - if !cfg.pass_through_devices().is_empty() { - println!(); - println!( - " Passthrough Devices: ({} device(s))", - cfg.pass_through_devices().len() - ); - for device in cfg.pass_through_devices() { - println!( - " - {}: GPA[{:#x}~{:#x}] -> HPA[{:#x}~{:#x}] ({})", - device.name, - device.base_gpa, - device.base_gpa + device.length, - device.base_hpa, - device.base_hpa + device.length, - format_memory_size(device.length) - ); - } - } - - // Show passthrough addresses - if !cfg.pass_through_addresses().is_empty() { - println!(); - println!( - " Passthrough Memory Regions: ({} region(s))", - cfg.pass_through_addresses().len() - ); - for pt_addr in cfg.pass_through_addresses() { - println!( - " - GPA[{:#x}~{:#x}] ({})", - pt_addr.base_gpa, - pt_addr.base_gpa + pt_addr.length, - format_memory_size(pt_addr.length) - ); - } - } - - // Show passthrough SPIs (ARM specific) - #[cfg(target_arch = "aarch64")] - { - let spis = cfg.pass_through_spis(); - if !spis.is_empty() { - println!(); - println!(" Passthrough SPIs: {:?}", spis); - } - } - - // Show emulated devices - if !cfg.emu_devices().is_empty() { - println!(); - println!( - " Emulated Devices: ({} device(s))", - cfg.emu_devices().len() - ); - for (idx, device) in cfg.emu_devices().iter().enumerate() { - println!(" {}. {:?}", idx + 1, device); - } - } - }); - - // Devices - println!(); - let mmio_dev_count = vm.get_devices().iter_mmio_dev().count(); - let sysreg_dev_count = vm.get_devices().iter_sys_reg_dev().count(); - println!("Devices:"); - println!(" MMIO Devices: {}", mmio_dev_count); - println!(" SysReg Devices: {}", sysreg_dev_count); - - // Additional Statistics - println!(); - println!("Additional Statistics:"); - println!(" Total Memory Regions: {}", vm.memory_regions().len()); - - // Show VCpu affinity details - println!(); - println!(" VCpu Affinity Details:"); - for (vcpu_id, affinity, pcpu_id) in vm.get_vcpu_affinities_pcpu_ids() { - if let Some(aff) = affinity { - println!( - " VCpu {}: Physical CPU mask {:#x}, PCpu ID {}", - vcpu_id, aff, pcpu_id - ); - } else { - println!( - " VCpu {}: No specific affinity, PCpu ID {}", - vcpu_id, pcpu_id - ); - } - } - }) { - Some(_) => {} - None => { - println!("✗ VM[{}] not found", vm_id); - } - } -} - -/// Build the VM command tree and register it. -pub fn build_vm_cmd(tree: &mut BTreeMap) { - #[cfg(feature = "fs")] - let create_cmd = CommandNode::new("Create a new virtual machine") - .with_handler(vm_create) - .with_usage("vm create [OPTIONS] ...") - .with_option( - OptionDef::new("name", "Virtual machine name") - .with_short('n') - .with_long("name"), - ) - .with_option( - OptionDef::new("cpu", "Number of CPU cores") - .with_short('c') - .with_long("cpu"), - ) - .with_option( - OptionDef::new("memory", "Amount of memory") - .with_short('m') - .with_long("memory"), - ) - .with_flag( - FlagDef::new("force", "Force creation without confirmation") - .with_short('f') - .with_long("force"), - ); - - #[cfg(feature = "fs")] - let start_cmd = CommandNode::new("Start a virtual machine") - .with_handler(vm_start) - .with_usage("vm start [OPTIONS] [VM_ID...]") - .with_flag( - FlagDef::new("detach", "Start in background") - .with_short('d') - .with_long("detach"), - ) - .with_flag( - FlagDef::new("console", "Attach to console") - .with_short('c') - .with_long("console"), - ); - - let stop_cmd = CommandNode::new("Stop a virtual machine") - .with_handler(vm_stop) - .with_usage("vm stop [OPTIONS] ...") - .with_flag( - FlagDef::new("force", "Force stop") - .with_short('f') - .with_long("force"), - ) - .with_flag( - FlagDef::new("graceful", "Graceful shutdown") - .with_short('g') - .with_long("graceful"), - ); - - let restart_cmd = CommandNode::new("Restart a virtual machine") - .with_handler(vm_restart) - .with_usage("vm restart [OPTIONS] ...") - .with_flag( - FlagDef::new("force", "Force restart") - .with_short('f') - .with_long("force"), - ); - - let suspend_cmd = CommandNode::new("Suspend (pause) a running virtual machine") - .with_handler(vm_suspend) - .with_usage("vm suspend ..."); - - let resume_cmd = CommandNode::new("Resume a suspended virtual machine") - .with_handler(vm_resume) - .with_usage("vm resume ..."); - - let delete_cmd = CommandNode::new("Delete a virtual machine") - .with_handler(vm_delete) - .with_usage("vm delete [OPTIONS] ") - .with_flag( - FlagDef::new("force", "Skip confirmation") - .with_short('f') - .with_long("force"), - ) - .with_flag(FlagDef::new("keep-data", "Keep VM data").with_long("keep-data")); - - let list_cmd = CommandNode::new("Show virtual machine lists") - .with_handler(vm_list) - .with_usage("vm list [OPTIONS]") - .with_flag( - FlagDef::new("all", "Show all VMs including stopped ones") - .with_short('a') - .with_long("all"), - ) - .with_option(OptionDef::new("format", "Output format (table, json)").with_long("format")); - - let show_cmd = CommandNode::new("Show detailed VM information") - .with_handler(vm_show) - .with_usage("vm show [OPTIONS] ") - .with_flag( - FlagDef::new("full", "Show full detailed information") - .with_short('f') - .with_long("full"), - ) - .with_flag( - FlagDef::new("config", "Show configuration details") - .with_short('c') - .with_long("config"), - ) - .with_flag( - FlagDef::new("stats", "Show device statistics") - .with_short('s') - .with_long("stats"), - ); - - // main VM command - let mut vm_node = CommandNode::new("Virtual machine management") - .with_handler(vm_help) - .with_usage("vm [options] [args...]") - .add_subcommand( - "help", - CommandNode::new("Show VM help").with_handler(vm_help), - ); - - #[cfg(feature = "fs")] - { - vm_node = vm_node - .add_subcommand("create", create_cmd) - .add_subcommand("start", start_cmd); - } - - vm_node = vm_node - .add_subcommand("stop", stop_cmd) - .add_subcommand("suspend", suspend_cmd) - .add_subcommand("resume", resume_cmd) - .add_subcommand("restart", restart_cmd) - .add_subcommand("delete", delete_cmd) - .add_subcommand("list", list_cmd) - .add_subcommand("show", show_cmd); - - tree.insert("vm".to_string(), vm_node); -} diff --git a/os/axvisor/src/shell/mod.rs b/os/axvisor/src/shell/mod.rs deleted file mode 100644 index 49aac346a..000000000 --- a/os/axvisor/src/shell/mod.rs +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod command; - -use std::io::prelude::*; -use std::println; -use std::string::ToString; - -use crate::shell::command::{ - CommandHistory, clear_line_and_redraw, handle_builtin_commands, print_prompt, run_cmd_bytes, -}; - -const LF: u8 = b'\n'; -const CR: u8 = b'\r'; -const DL: u8 = b'\x7f'; -const BS: u8 = b'\x08'; -const ESC: u8 = 0x1b; // ESC key - -const MAX_LINE_LEN: usize = 256; - -// Initialize the console shell. -pub fn console_init() { - let mut stdin = std::io::stdin(); - let mut stdout = std::io::stdout(); - let mut history = CommandHistory::new(100); - - let mut buf = [0; MAX_LINE_LEN]; - let mut cursor = 0; // cursor position in buffer - let mut line_len = 0; // actual length of current line - - enum InputState { - Normal, - Escape, - EscapeSeq, - } - - let mut input_state = InputState::Normal; - - println!("Welcome to AxVisor Shell!"); - println!("Type 'help' to see available commands"); - println!("Use UP/DOWN arrows to navigate command history"); - #[cfg(not(feature = "fs"))] - println!("Note: Running with limited features (filesystem support disabled)."); - println!(); - - print_prompt(); - - loop { - let mut temp_buf = [0u8; 1]; - - let ch = match stdin.read(&mut temp_buf) { - Ok(1) => temp_buf[0], - _ => { - continue; - } - }; - - match input_state { - InputState::Normal => { - match ch { - CR | LF => { - println!(); - if line_len > 0 { - let cmd_str = std::str::from_utf8(&buf[..line_len]).unwrap_or(""); - - // Add to history - history.add_command(cmd_str.to_string()); - - // Execute command - if !handle_builtin_commands(cmd_str) { - run_cmd_bytes(&buf[..line_len]); - } - - // reset buffer - buf[..line_len].fill(0); - cursor = 0; - line_len = 0; - } - print_prompt(); - } - BS | DL => { - // backspace: delete character before cursor / DEL key: delete character at cursor - if cursor > 0 { - // move characters after cursor forward - for i in cursor..line_len { - buf[i - 1] = buf[i]; - } - cursor -= 1; - line_len -= 1; - if line_len < buf.len() { - buf[line_len] = 0; - } - - let current_content = - std::str::from_utf8(&buf[..line_len]).unwrap_or(""); - #[cfg(feature = "fs")] - let prompt = format!("axvisor:{}$ ", &std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - let prompt = "axvisor:$ ".to_string(); - clear_line_and_redraw(&mut stdout, &prompt, current_content, cursor); - } - } - ESC => { - input_state = InputState::Escape; - } - 0..=31 => { - // ignore other control characters - } - c => { - // insert character - if line_len < MAX_LINE_LEN - 1 { - // move characters after cursor backward to make space for new character - for i in (cursor..line_len).rev() { - buf[i + 1] = buf[i]; - } - buf[cursor] = c; - cursor += 1; - line_len += 1; - - let current_content = - std::str::from_utf8(&buf[..line_len]).unwrap_or(""); - #[cfg(feature = "fs")] - let prompt = format!("axvisor:{}$ ", &std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - let prompt = "axvisor:$ ".to_string(); - clear_line_and_redraw(&mut stdout, &prompt, current_content, cursor); - } - } - } - } - InputState::Escape => match ch { - b'[' => { - input_state = InputState::EscapeSeq; - } - _ => { - input_state = InputState::Normal; - } - }, - InputState::EscapeSeq => { - match ch { - b'A' => { - // UP arrow - previous command - if let Some(prev_cmd) = history.previous() { - // clear current buffer - buf[..line_len].fill(0); - - let cmd_bytes = prev_cmd.as_bytes(); - let copy_len = cmd_bytes.len().min(MAX_LINE_LEN - 1); - buf[..copy_len].copy_from_slice(&cmd_bytes[..copy_len]); - cursor = copy_len; - line_len = copy_len; - #[cfg(feature = "fs")] - let prompt = format!("axvisor:{}$ ", &std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - let prompt = "axvisor:$ ".to_string(); - clear_line_and_redraw(&mut stdout, &prompt, prev_cmd, cursor); - } - input_state = InputState::Normal; - } - b'B' => { - // DOWN arrow - next command - match history.next() { - Some(next_cmd) => { - // clear current buffer - buf[..line_len].fill(0); - - let cmd_bytes = next_cmd.as_bytes(); - let copy_len = cmd_bytes.len().min(MAX_LINE_LEN - 1); - buf[..copy_len].copy_from_slice(&cmd_bytes[..copy_len]); - cursor = copy_len; - line_len = copy_len; - - #[cfg(feature = "fs")] - let prompt = - format!("axvisor:{}$ ", &std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - let prompt = "axvisor:$ ".to_string(); - clear_line_and_redraw(&mut stdout, &prompt, next_cmd, cursor); - } - None => { - // clear current line - buf[..line_len].fill(0); - cursor = 0; - line_len = 0; - #[cfg(feature = "fs")] - let prompt = - format!("axvisor:{}$ ", &std::env::current_dir().unwrap()); - #[cfg(not(feature = "fs"))] - let prompt = "axvisor:$ ".to_string(); - clear_line_and_redraw(&mut stdout, &prompt, "", cursor); - } - } - input_state = InputState::Normal; - } - b'C' => { - // RIGHT arrow - move cursor right - if cursor < line_len { - cursor += 1; - stdout.write_all(b"\x1b[C").ok(); - stdout.flush().ok(); - } - input_state = InputState::Normal; - } - b'D' => { - // LEFT arrow - move cursor left - if cursor > 0 { - cursor -= 1; - stdout.write_all(b"\x1b[D").ok(); - stdout.flush().ok(); - } - input_state = InputState::Normal; - } - b'3' => { - // check if this is Delete key sequence (ESC[3~) - // need to read next character to confirm - input_state = InputState::Normal; - // can add additional state to handle complete Delete sequence - } - _ => { - // ignore other escape sequences - input_state = InputState::Normal; - } - } - } - } - } -} diff --git a/os/axvisor/src/task.rs b/os/axvisor/src/task.rs deleted file mode 100644 index 27bbda958..000000000 --- a/os/axvisor/src/task.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::sync::{Arc, Weak}; -use std::os::arceos::modules::axtask::{TaskExt, TaskInner}; - -use crate::vmm::{VCpuRef, VM, VMRef}; - -/// Task extended data for the hypervisor. -pub struct VCpuTask { - /// The VM (Weak reference to avoid keeping VM alive). - pub vm: Weak, - /// The virtual CPU. - pub vcpu: VCpuRef, -} - -impl VCpuTask { - /// Create a new [`VCpuTask`]. - pub fn new(vm: &VMRef, vcpu: VCpuRef) -> Self { - Self { - vm: Arc::downgrade(vm), - vcpu, - } - } - - /// Get a strong reference to the VM if it's still alive. - /// Returns None if the VM has been dropped. - pub fn vm(&self) -> VMRef { - self.vm.upgrade().expect("VM has been dropped") - } -} - -#[extern_trait::extern_trait] -impl TaskExt for VCpuTask {} - -pub trait AsVCpuTask { - fn as_vcpu_task(&self) -> &VCpuTask; -} - -impl AsVCpuTask for TaskInner { - fn as_vcpu_task(&self) -> &VCpuTask { - self.task_ext() - .expect("Not a VCpuTask") - .downcast_ref::() - } -} diff --git a/os/axvisor/src/vmm/config.rs b/os/axvisor/src/vmm/config.rs deleted file mode 100644 index 3b780f976..000000000 --- a/os/axvisor/src/vmm/config.rs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axaddrspace::GuestPhysAddr; -use axerrno::AxResult; -use axvm::{ - VMMemoryRegion, - config::{AxVMConfig, AxVMCrateConfig, VmMemMappingType}, -}; -use core::alloc::Layout; - -use crate::vmm::{VM, images::ImageLoader, vm_list::push_vm}; - -#[cfg(target_arch = "aarch64")] -use crate::vmm::fdt::*; - -use alloc::sync::Arc; - -#[allow(clippy::module_inception, dead_code)] -pub mod config { - use alloc::string::String; - use alloc::vec::Vec; - - /// Default static VM configs. Used when no VM config is provided. - pub fn default_static_vm_configs() -> Vec<&'static str> { - vec![] - } - - /// Read VM configs from filesystem - #[cfg(feature = "fs")] - pub fn filesystem_vm_configs() -> Vec { - use axstd::fs; - use axstd::io::{BufReader, Read}; - - let config_dir = "/guest/vm_default"; - - let mut configs = Vec::new(); - - debug!("Read VM config files from filesystem."); - - let entries = match fs::read_dir(config_dir) { - Ok(entries) => { - info!("Find dir: {}", config_dir); - entries - } - Err(_e) => { - info!("NOT find dir: {} in filesystem", config_dir); - return configs; - } - }; - - for entry in entries.flatten() { - let path = entry.path(); - // Check if the file has a .toml extension - let path_str = path.as_str(); - debug!("Considering file: {}", path_str); - if path_str.ends_with(".toml") { - let toml_file = fs::File::open(path_str).expect("Failed to open file"); - let file_size = toml_file - .metadata() - .expect("Failed to get file metadata") - .len() as usize; - - info!("File {} size: {}", path_str, file_size); - - if file_size == 0 { - warn!("File {} is empty", path_str); - continue; - } - - let mut file = BufReader::new(toml_file); - let mut buffer = vec![0u8; file_size]; - match file.read_exact(&mut buffer) { - Ok(()) => { - debug!( - "Successfully read config file {} as bytes, size: {}", - path_str, - buffer.len() - ); - // Convert to string - let content = alloc::string::String::from_utf8(buffer) - .expect("Failed to convert bytes to UTF-8 string"); - - if content.contains("[base]") - && content.contains("[kernel]") - && content.contains("[devices]") - { - configs.push(content); - info!( - "TOML config: {} is valid, start the virtual machine directly now. ", - path_str - ); - } else { - warn!( - "File {} does not appear to contain valid VM config structure", - path_str - ); - } - } - Err(e) => { - error!("Failed to read file {}: {:?}", path_str, e); - } - } - } - } - - configs - } - - /// Fallback function for when "fs" feature is not enabled - #[cfg(not(feature = "fs"))] - pub fn filesystem_vm_configs() -> Vec { - Vec::new() - } - - include!(concat!(env!("OUT_DIR"), "/vm_configs.rs")); -} - -pub fn get_vm_dtb_arc(_vm_cfg: &AxVMConfig) -> Option> { - #[cfg(target_arch = "aarch64")] - { - let cache_lock = dtb_cache().lock(); - if let Some(dtb) = cache_lock.get(&_vm_cfg.id()) { - return Some(Arc::from(dtb.as_slice())); - } - } - None -} - -pub fn init_guest_vms() { - // Initialize the DTB cache in the fdt module - #[cfg(target_arch = "aarch64")] - { - init_dtb_cache(); - } - - // First try to get configs from filesystem if fs feature is enabled - let mut gvm_raw_configs = config::filesystem_vm_configs(); - - // If no filesystem configs found, fallback to static configs - if gvm_raw_configs.is_empty() { - let static_configs = config::static_vm_configs(); - if static_configs.is_empty() { - info!("Static VM configs are empty."); - info!("Now axvisor will entry the shell..."); - } else { - info!("Using static VM configs."); - } - // Convert static configs to String type - gvm_raw_configs.extend(static_configs.into_iter().map(|s| s.into())); - } - - for raw_cfg_str in gvm_raw_configs { - debug!("Initializing guest VM with config: {:#?}", raw_cfg_str); - if let Err(e) = init_guest_vm(&raw_cfg_str) { - error!("Failed to initialize guest VM: {e:?}"); - } - } -} - -pub fn init_guest_vm(raw_cfg: &str) -> AxResult { - let vm_create_config = - AxVMCrateConfig::from_toml(raw_cfg).expect("Failed to resolve VM config"); - - if let Some(linux) = super::images::get_image_header(&vm_create_config) { - debug!( - "VM[{}] Linux header: {:#x?}", - vm_create_config.base.id, linux - ); - } - - #[cfg(target_arch = "aarch64")] - let mut vm_config = AxVMConfig::from(vm_create_config.clone()); - - #[cfg(not(target_arch = "aarch64"))] - let vm_config = AxVMConfig::from(vm_create_config.clone()); - - // Handle FDT-related operations for aarch64 - #[cfg(target_arch = "aarch64")] - handle_fdt_operations(&mut vm_config, &vm_create_config); - - // info!("after parse_vm_interrupt, crate VM[{}] with config: {:#?}", vm_config.id(), vm_config); - info!("Creating VM[{}] {:?}", vm_config.id(), vm_config.name()); - - // Create VM. - let vm = VM::new(vm_config).expect("Failed to create VM"); - let vm_id = vm.id(); - push_vm(vm.clone()); - - vm_alloc_memorys(&vm_create_config, &vm); - - let main_mem = vm - .memory_regions() - .first() - .cloned() - .expect("VM must have at least one memory region"); - - config_guest_address(&vm, &main_mem); - - // Load corresponding images for VM. - info!("VM[{}] created success, loading images...", vm.id()); - - let mut loader = ImageLoader::new(main_mem, vm_create_config, vm.clone()); - loader.load().expect("Failed to load VM images"); - - if let Err(e) = vm.init() { - panic!("VM[{}] setup failed: {:?}", vm.id(), e); - } - - vm.set_vm_status(axvm::VMStatus::Loaded); - - Ok(vm_id) -} - -fn config_guest_address(vm: &VM, main_memory: &VMMemoryRegion) { - const MB: usize = 1024 * 1024; - vm.with_config(|config| { - if main_memory.is_identical() { - debug!( - "Adjusting kernel load address from {:#x} to {:#x}", - config.image_config.kernel_load_gpa, main_memory.gpa - ); - let mut kernel_addr = main_memory.gpa; - if config.image_config.bios_load_gpa.is_some() { - kernel_addr += MB * 2; // leave 2MB for BIOS - } - - config.image_config.kernel_load_gpa = kernel_addr; - config.cpu_config.bsp_entry = kernel_addr; - config.cpu_config.ap_entry = kernel_addr; - } - }); -} - -fn vm_alloc_memorys(vm_create_config: &AxVMCrateConfig, vm: &VM) { - const MB: usize = 1024 * 1024; - const ALIGN: usize = 2 * MB; - - for memory in &vm_create_config.kernel.memory_regions { - match memory.map_type { - VmMemMappingType::MapAlloc => { - vm.alloc_memory_region( - Layout::from_size_align(memory.size, ALIGN).unwrap(), - Some(GuestPhysAddr::from(memory.gpa)), - ) - .expect("Failed to allocate memory region for VM"); - } - VmMemMappingType::MapIdentical => { - vm.alloc_memory_region(Layout::from_size_align(memory.size, ALIGN).unwrap(), None) - .expect("Failed to allocate memory region for VM"); - } - VmMemMappingType::MapReserved => { - info!("VM[{}] map same region: {:#x?}", vm.id(), memory); - let layout = Layout::from_size_align(memory.size, ALIGN).unwrap(); - vm.map_reserved_memory_region(layout, Some(GuestPhysAddr::from(memory.gpa))) - .expect("Failed to map memory region for VM"); - } - } - } -} diff --git a/os/axvisor/src/vmm/fdt/create.rs b/os/axvisor/src/vmm/fdt/create.rs deleted file mode 100644 index baeaddd39..000000000 --- a/os/axvisor/src/vmm/fdt/create.rs +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::{ - string::{String, ToString}, - vec::Vec, -}; -use core::ptr::NonNull; - -use super::vm_fdt::{FdtWriter, FdtWriterNode}; -use axaddrspace::GuestPhysAddr; -use axvm::{VMMemoryRegion, config::AxVMCrateConfig}; -use fdt_parser::{Fdt, Node}; -use memory_addr::MemoryAddr; - -use crate::vmm::{VMRef, images::load_vm_image_from_memory}; - -// use crate::vmm::fdt::print::{print_fdt, print_guest_fdt}; -/// Generate guest FDT and return DTB data -/// -/// # Parameters -/// * `fdt` - Source FDT data -/// * `passthrough_device_names` - Passthrough device name list -/// * `crate_config` - VM creation configuration -/// -/// # Return Value -/// Returns the generated DTB data -pub fn crate_guest_fdt( - fdt: &Fdt, - passthrough_device_names: &[String], - crate_config: &AxVMCrateConfig, -) -> Vec { - let mut fdt_writer = FdtWriter::new().unwrap(); - // Track the level of the previously processed node for level change handling - let mut previous_node_level = 0; - // Maintain a stack of FDT nodes to correctly start and end nodes - let mut node_stack: Vec = Vec::new(); - let phys_cpu_ids = crate_config - .base - .phys_cpu_ids - .clone() - .expect("ERROR: phys_cpu_ids is None"); - - let all_nodes: Vec = fdt.all_nodes().collect(); - - for (index, node) in all_nodes.iter().enumerate() { - let node_path = super::build_node_path(&all_nodes, index); - let node_action = determine_node_action(node, &node_path, passthrough_device_names); - - match node_action { - NodeAction::RootNode => { - node_stack.push(fdt_writer.begin_node("").unwrap()); - } - NodeAction::CpuNode => { - let need = need_cpu_node(&phys_cpu_ids, node, &node_path); - if need { - handle_node_level_change( - &mut fdt_writer, - &mut node_stack, - node.level, - previous_node_level, - ); - node_stack.push(fdt_writer.begin_node(node.name()).unwrap()); - } else { - continue; - } - } - NodeAction::Skip => { - continue; - } - _ => { - trace!( - "Found exact passthrough device node: {}, path: {}", - node.name(), - node_path - ); - handle_node_level_change( - &mut fdt_writer, - &mut node_stack, - node.level, - previous_node_level, - ); - node_stack.push(fdt_writer.begin_node(node.name()).unwrap()); - } - } - - previous_node_level = node.level; - - // Copy all properties of the node - for prop in node.propertys() { - fdt_writer.property(prop.name, prop.raw_value()).unwrap(); - } - } - - // End all unclosed nodes - while let Some(node) = node_stack.pop() { - previous_node_level -= 1; - fdt_writer.end_node(node).unwrap(); - } - assert_eq!(previous_node_level, 0); - - fdt_writer.finish().unwrap() -} - -/// Node processing action enumeration -enum NodeAction { - /// Skip node, not included in guest FDT - Skip, - /// Root node - RootNode, - /// CPU node - CpuNode, - /// Include node as passthrough device node - IncludeAsPassthroughDevice, - /// Include node as child node of passthrough device - IncludeAsChildNode, - /// Include node as ancestor node of passthrough device - IncludeAsAncestorNode, -} - -/// Determine node processing action -fn determine_node_action( - node: &Node, - node_path: &str, - passthrough_device_names: &[String], -) -> NodeAction { - if node.name() == "/" { - // Special handling for root node - NodeAction::RootNode - } else if node.name().starts_with("memory") { - // Skip memory nodes, will add them later - NodeAction::Skip - } else if node_path.starts_with("/cpus") { - NodeAction::CpuNode - } else if passthrough_device_names.contains(&node_path.to_string()) { - // Fully matched passthrough device node - NodeAction::IncludeAsPassthroughDevice - } - // Check if the node is a descendant of a passthrough device (by path inclusion and level validation) - else if is_descendant_of_passthrough_device(node_path, node.level, passthrough_device_names) { - NodeAction::IncludeAsChildNode - } - // Check if the node is an ancestor of a passthrough device (by path inclusion and level validation) - else if is_ancestor_of_passthrough_device(node_path, passthrough_device_names) { - NodeAction::IncludeAsAncestorNode - } else { - NodeAction::Skip - } -} - -/// Determine if node is a descendant of passthrough device -/// When node path contains a path from passthrough_device_names and is longer than it, it is its descendant node -/// Also use node_level as validation condition -fn is_descendant_of_passthrough_device( - node_path: &str, - node_level: usize, - passthrough_device_names: &[String], -) -> bool { - for passthrough_path in passthrough_device_names { - // Check if the current node is a descendant of a passthrough device - if node_path.starts_with(passthrough_path) && node_path.len() > passthrough_path.len() { - // Ensure it is a true descendant path (separated by /) - if passthrough_path == "/" || node_path.chars().nth(passthrough_path.len()) == Some('/') - { - // Use level relationship for validation: the level of a descendant node should be higher than its parent - // Note: The level of the root node is 1, its direct child node level is 2, and so on - let expected_parent_level = passthrough_path.matches('/').count(); - let current_node_level = node_level; - - // If passthrough_path is the root node "/", then its child node level should be 2 - // Otherwise, the child node level should be higher than the parent node level - if (passthrough_path == "/" && current_node_level >= 2) - || (passthrough_path != "/" && current_node_level > expected_parent_level) - { - return true; - } - } - } - } - false -} - -/// Handle node level changes to ensure correct FDT structure -fn handle_node_level_change( - fdt_writer: &mut FdtWriter, - node_stack: &mut Vec, - current_level: usize, - previous_level: usize, -) { - if current_level <= previous_level { - for _ in current_level..=previous_level { - if let Some(end_node) = node_stack.pop() { - fdt_writer.end_node(end_node).unwrap(); - } - } - } -} - -/// Determine if node is an ancestor of passthrough device -fn is_ancestor_of_passthrough_device(node_path: &str, passthrough_device_names: &[String]) -> bool { - for passthrough_path in passthrough_device_names { - // Check if the current node is an ancestor of a passthrough device - if passthrough_path.starts_with(node_path) && passthrough_path.len() > node_path.len() { - // Ensure it is a true ancestor path (separated by /) - let next_char = passthrough_path.chars().nth(node_path.len()).unwrap_or(' '); - if next_char == '/' || node_path == "/" { - return true; - } - } - } - false -} - -/// Determine if CPU node is needed -fn need_cpu_node(phys_cpu_ids: &[usize], node: &Node, node_path: &str) -> bool { - let mut should_include_node = false; - - if !node_path.starts_with("/cpus/cpu@") { - should_include_node = true; - } else if let Some(mut cpu_reg) = node.reg() - && let Some(reg_entry) = cpu_reg.next() - { - let cpu_address = reg_entry.address as usize; - debug!( - "Checking CPU node {} with address 0x{:x}", - node.name(), - cpu_address - ); - // Check if this CPU address is in the configured phys_cpu_ids - if phys_cpu_ids.contains(&cpu_address) { - should_include_node = true; - debug!( - "CPU node {} with address 0x{:x} is in phys_cpu_ids, including in guest FDT", - node.name(), - cpu_address - ); - } else { - debug!( - "CPU node {} with address 0x{:x} is NOT in phys_cpu_ids, skipping", - node.name(), - cpu_address - ); - } - } - should_include_node -} - -/// Add memory node -fn add_memory_node(new_memory: &[VMMemoryRegion], new_fdt: &mut FdtWriter) { - let mut new_value: Vec = Vec::new(); - for mem in new_memory { - let gpa = mem.gpa.as_usize() as u64; - let size = mem.size() as u64; - new_value.push((gpa >> 32) as u32); - new_value.push((gpa & 0xFFFFFFFF) as u32); - new_value.push((size >> 32) as u32); - new_value.push((size & 0xFFFFFFFF) as u32); - } - info!("Adding memory node with value: 0x{new_value:x?}"); - new_fdt - .property_array_u32("reg", new_value.as_ref()) - .unwrap(); - new_fdt.property_string("device_type", "memory").unwrap(); -} - -pub fn update_fdt(fdt_src: NonNull, dtb_size: usize, vm: VMRef) { - let mut new_fdt = FdtWriter::new().unwrap(); - let mut previous_node_level = 0; - let mut node_stack: Vec = Vec::new(); - - let fdt_bytes = unsafe { core::slice::from_raw_parts(fdt_src.as_ptr(), dtb_size) }; - let fdt = Fdt::from_bytes(fdt_bytes) - .map_err(|e| format!("Failed to parse FDT: {e:#?}")) - .expect("Failed to parse FDT"); - - for node in fdt.all_nodes() { - if node.name() == "/" { - node_stack.push(new_fdt.begin_node("").unwrap()); - } else if node.name().starts_with("memory") { - // Skip memory nodes, will add them later - continue; - } else { - handle_node_level_change( - &mut new_fdt, - &mut node_stack, - node.level, - previous_node_level, - ); - // Start new node - node_stack.push(new_fdt.begin_node(node.name()).unwrap()); - } - - previous_node_level = node.level; - - if node.name() == "chosen" { - for prop in node.propertys() { - if prop.name.starts_with("linux,initrd-") { - info!( - "Skipping property: {}, belonging to node: {}", - prop.name, - node.name() - ); - } else if prop.name == "bootargs" { - let bootargs_str = prop.str(); - let modified_bootargs = bootargs_str.replace(" ro ", " rw "); - - if modified_bootargs != bootargs_str { - info!( - "Modifying bootargs: {} -> {}", - bootargs_str, modified_bootargs - ); - } - - new_fdt - .property_string(prop.name, &modified_bootargs) - .unwrap(); - } else { - debug!( - "Find property: {}, belonging to node: {}", - prop.name, - node.name() - ); - new_fdt.property(prop.name, prop.raw_value()).unwrap(); - } - } - } else { - for prop in node.propertys() { - new_fdt.property(prop.name, prop.raw_value()).unwrap(); - } - } - } - - // End all unclosed nodes, and add memory nodes at appropriate positions - while let Some(node) = node_stack.pop() { - previous_node_level -= 1; - new_fdt.end_node(node).unwrap(); - - // add memory node - if previous_node_level == 1 { - let memory_regions = vm.memory_regions(); - debug!("Adding memory node with regions: {memory_regions:?}"); - let memory_node = new_fdt.begin_node("memory").unwrap(); - add_memory_node(&memory_regions, &mut new_fdt); - new_fdt.end_node(memory_node).unwrap(); - } - } - - assert_eq!(previous_node_level, 0); - - info!("Updating FDT memory successfully"); - - let new_fdt_bytes = new_fdt.finish().unwrap(); - - // crate::vmm::fdt::print::print_guest_fdt(new_fdt_bytes.as_slice()); - let vm_clone = vm.clone(); - let dest_addr = calculate_dtb_load_addr(vm, new_fdt_bytes.len()); - info!( - "New FDT will be loaded at {:x}, size: 0x{:x}", - dest_addr, - new_fdt_bytes.len() - ); - // Load the updated FDT into VM - load_vm_image_from_memory(&new_fdt_bytes, dest_addr, vm_clone) - .expect("Failed to load VM images"); -} - -fn calculate_dtb_load_addr(vm: VMRef, fdt_size: usize) -> GuestPhysAddr { - const MB: usize = 1024 * 1024; - - // Get main memory from VM memory regions outside the closure - let main_memory = vm - .memory_regions() - .first() - .cloned() - .expect("VM must have at least one memory region"); - - vm.with_config(|config| { - let dtb_addr = if let Some(addr) = config.image_config.dtb_load_gpa - && !main_memory.is_identical() - { - // If dtb_load_gpa is already set, use the original value - addr - } else { - // If dtb_load_gpa is None, calculate based on memory size and FDT size - let main_memory_size = main_memory.size().min(512 * MB); - let addr = (main_memory.gpa + main_memory_size - fdt_size).align_down(2 * MB); - if fdt_size > main_memory_size { - error!("DTB size is larger than available memory"); - } - addr - }; - config.image_config.dtb_load_gpa = Some(dtb_addr); - dtb_addr - }) -} - -pub fn update_cpu_node(fdt: &Fdt, host_fdt: &Fdt, crate_config: &AxVMCrateConfig) -> Vec { - let mut new_fdt = FdtWriter::new().unwrap(); - let mut previous_node_level = 0; - let mut node_stack: Vec = Vec::new(); - let phys_cpu_ids = crate_config - .base - .phys_cpu_ids - .clone() - .expect("ERROR: phys_cpu_ids is None"); - - // Collect all nodes from both FDTs - let fdt_all_nodes: Vec = fdt.all_nodes().collect(); - let host_fdt_all_nodes: Vec = host_fdt.all_nodes().collect(); - - for (index, node) in fdt_all_nodes.iter().enumerate() { - let node_path = super::build_node_path(&fdt_all_nodes, index); - - if node.name() == "/" { - node_stack.push(new_fdt.begin_node("").unwrap()); - } else if node_path.starts_with("/cpus") { - // Skip CPU nodes from fdt, we'll process them from host_fdt later - continue; - } else { - // For all other nodes, include them from fdt as-is without filtering - handle_node_level_change( - &mut new_fdt, - &mut node_stack, - node.level, - previous_node_level, - ); - node_stack.push(new_fdt.begin_node(node.name()).unwrap()); - } - - previous_node_level = node.level; - - // Copy all properties of the node (for non-CPU nodes) - for prop in node.propertys() { - new_fdt.property(prop.name, prop.raw_value()).unwrap(); - } - } - - // Process all CPU nodes from host_fdt - for (index, node) in host_fdt_all_nodes.iter().enumerate() { - let node_path = super::build_node_path(&host_fdt_all_nodes, index); - - if node_path.starts_with("/cpus") { - // For CPU nodes, apply filtering based on host_fdt nodes - let need = need_cpu_node(&phys_cpu_ids, node, &node_path); - if need { - handle_node_level_change( - &mut new_fdt, - &mut node_stack, - node.level, - previous_node_level, - ); - node_stack.push(new_fdt.begin_node(node.name()).unwrap()); - - // Copy properties from host CPU node - for prop in node.propertys() { - new_fdt.property(prop.name, prop.raw_value()).unwrap(); - } - - previous_node_level = node.level; - } - } - } - - // End all unclosed nodes - while let Some(node) = node_stack.pop() { - previous_node_level -= 1; - new_fdt.end_node(node).unwrap(); - } - assert_eq!(previous_node_level, 0); - - new_fdt.finish().unwrap() -} diff --git a/os/axvisor/src/vmm/fdt/device.rs b/os/axvisor/src/vmm/fdt/device.rs deleted file mode 100644 index 9553ae2b8..000000000 --- a/os/axvisor/src/vmm/fdt/device.rs +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Device passthrough and dependency analysis for FDT processing. - -use alloc::{ - collections::{BTreeMap, BTreeSet}, - string::{String, ToString}, - vec::Vec, -}; -use axvm::config::AxVMConfig; -use fdt_parser::{Fdt, Node}; - -/// Return the collection of all passthrough devices in the configuration file and newly added devices found -pub fn find_all_passthrough_devices(vm_cfg: &mut AxVMConfig, fdt: &Fdt) -> Vec { - let initial_device_count = vm_cfg.pass_through_devices().len(); - - // Pre-build node cache, store all nodes by path to improve lookup performance - let node_cache: BTreeMap> = build_optimized_node_cache(fdt); - - // Get the list of configured device names - let initial_device_names: Vec = vm_cfg - .pass_through_devices() - .iter() - .map(|dev| dev.name.clone()) - .collect(); - - // Phase 1: Discover descendant nodes of all passthrough devices in the configuration file - // Build a set of configured devices, using BTreeSet to improve lookup efficiency - let mut configured_device_names: BTreeSet = - initial_device_names.iter().cloned().collect(); - - // Used to store newly discovered related device names - let mut additional_device_names = Vec::new(); - - // Phase 1: Process initial devices and their descendant nodes - // Note: Directly use device paths instead of device names - for device_name in &initial_device_names { - // Get all descendant node paths for this device - let descendant_paths = get_descendant_nodes_by_path(&node_cache, device_name); - trace!( - "Found {} descendant paths for {}", - descendant_paths.len(), - device_name - ); - - for descendant_path in descendant_paths { - if !configured_device_names.contains(&descendant_path) { - trace!("Found descendant device: {descendant_path}"); - configured_device_names.insert(descendant_path.clone()); - - additional_device_names.push(descendant_path.clone()); - } else { - trace!("Device already exists: {descendant_path}"); - } - } - } - - info!( - "Phase 1 completed: Found {} new descendant device names", - additional_device_names.len() - ); - - // Phase 2: Discover dependency nodes for all existing devices (including descendant devices) - let mut dependency_device_names = Vec::new(); - // Use a work queue of device names, including initial devices and descendant device names - let mut devices_to_process: Vec = configured_device_names.iter().cloned().collect(); - let mut processed_devices: BTreeSet = BTreeSet::new(); - - // Build phandle mapping table - let phandle_map = build_phandle_map(fdt); - - // Use work queue to recursively find all dependent devices - while let Some(device_node_path) = devices_to_process.pop() { - // Avoid processing the same device repeatedly - if processed_devices.contains(&device_node_path) { - continue; - } - processed_devices.insert(device_node_path.clone()); - - trace!("Analyzing dependencies for device: {device_node_path}"); - - // Find direct dependencies of the current device - let dependencies = find_device_dependencies(&device_node_path, &phandle_map, &node_cache); - trace!( - "Found {} dependencies: {:?}", - dependencies.len(), - dependencies - ); - for dep_node_name in dependencies { - // Check if dependency is already in configuration - if !configured_device_names.contains(&dep_node_name) { - trace!("Found new dependency device: {dep_node_name}"); - dependency_device_names.push(dep_node_name.clone()); - - // Add dependency device name to work queue to further find its dependencies - devices_to_process.push(dep_node_name.clone()); - configured_device_names.insert(dep_node_name.clone()); - } - } - } - - info!( - "Phase 2 completed: Found {} new dependency device names", - dependency_device_names.len() - ); - - // Phase 3: Find all excluded devices and remove them from the list - // Convert Vec> to Vec - let excluded_device_path: Vec = vm_cfg - .excluded_devices() - .iter() - .flatten() - .cloned() - .collect(); - let mut all_excludes_devices = excluded_device_path.clone(); - let mut process_excludeds: BTreeSet = excluded_device_path.iter().cloned().collect(); - - for device_path in &excluded_device_path { - // Get all descendant node paths for this device - let descendant_paths = get_descendant_nodes_by_path(&node_cache, device_path); - info!( - "Found {} descendant paths for {}", - descendant_paths.len(), - device_path - ); - - for descendant_path in descendant_paths { - if !process_excludeds.contains(&descendant_path) { - trace!("Found descendant device: {descendant_path}"); - process_excludeds.insert(descendant_path.clone()); - - all_excludes_devices.push(descendant_path.clone()); - } else { - trace!("Device already exists: {descendant_path}"); - } - } - } - info!("Found excluded devices: {all_excludes_devices:?}"); - - // Merge all device name lists - let mut all_device_names = initial_device_names.clone(); - all_device_names.extend(additional_device_names); - all_device_names.extend(dependency_device_names); - - // Remove excluded devices from the final list - if !all_excludes_devices.is_empty() { - info!( - "Removing {} excluded devices from the list", - all_excludes_devices.len() - ); - let excluded_set: BTreeSet = all_excludes_devices.into_iter().collect(); - - // Filter out excluded devices - all_device_names.retain(|device_name| { - let should_keep = !excluded_set.contains(device_name); - if !should_keep { - info!("Excluding device: {device_name}"); - } - should_keep - }); - } - - // Phase 4: remove root node from the list - all_device_names.retain(|device_name| device_name != "/"); - - let final_device_count = all_device_names.len(); - info!( - "Passthrough devices analysis completed. Total devices: {} (added: {})", - final_device_count, - final_device_count - initial_device_count - ); - - // Print final device list - for (i, device_name) in all_device_names.iter().enumerate() { - trace!("Final passthrough device[{i}]: {device_name}"); - } - - all_device_names -} - -/// Build the full path of a node based on node level relationships -/// Build the path by traversing all nodes and constructing paths based on level relationships to avoid path conflicts for nodes with the same name -pub fn build_node_path(all_nodes: &[Node], target_index: usize) -> String { - let mut path_stack: Vec = Vec::new(); - - for node in all_nodes.iter().take(target_index + 1) { - let level = node.level; - - if level == 1 { - path_stack.clear(); - if node.name() != "/" { - path_stack.push(node.name().to_string()); - } - } else { - while path_stack.len() >= level - 1 { - path_stack.pop(); - } - path_stack.push(node.name().to_string()); - } - } - - // Build the full path of the current node - if path_stack.is_empty() || (path_stack.len() == 1 && path_stack[0] == "/") { - "/".to_string() - } else { - "/".to_string() + &path_stack.join("/") - } -} - -/// Build a simplified node cache table, traverse all nodes once and group by full path -/// Use level relationships to directly build paths, avoiding path conflicts for nodes with the same name -pub fn build_optimized_node_cache<'a>(fdt: &'a Fdt) -> BTreeMap>> { - let mut node_cache: BTreeMap>> = BTreeMap::new(); - - let all_nodes: Vec = fdt.all_nodes().collect(); - - for (index, node) in all_nodes.iter().enumerate() { - let node_path = build_node_path(&all_nodes, index); - if let Some(existing_nodes) = node_cache.get(&node_path) - && !existing_nodes.is_empty() - { - error!( - "Duplicate node path found: {} for node '{}' at level {}, existing node: '{}'", - node_path, - node.name(), - node.level, - existing_nodes[0].name() - ); - } - - trace!( - "Adding node to cache: {} (level: {}, index: {})", - node_path, node.level, index - ); - node_cache.entry(node_path).or_default().push(node.clone()); - } - - debug!( - "Built simplified node cache with {} unique device paths", - node_cache.len() - ); - node_cache -} - -/// Build a mapping table from phandle to node information, optimized version using fdt-parser convenience methods -/// Use full path instead of node name -/// Use level relationships to directly build paths, avoiding path conflicts for nodes with the same name -fn build_phandle_map(fdt: &Fdt) -> BTreeMap)> { - let mut phandle_map = BTreeMap::new(); - - let all_nodes: Vec = fdt.all_nodes().collect(); - - for (index, node) in all_nodes.iter().enumerate() { - let node_path = build_node_path(&all_nodes, index); - - // Collect node properties - let mut phandle = None; - let mut cells_map = BTreeMap::new(); - for prop in node.propertys() { - match prop.name { - "phandle" | "linux,phandle" => { - phandle = Some(prop.u32()); - } - "#address-cells" - | "#size-cells" - | "#clock-cells" - | "#reset-cells" - | "#gpio-cells" - | "#interrupt-cells" - | "#power-domain-cells" - | "#thermal-sensor-cells" - | "#phy-cells" - | "#dma-cells" - | "#sound-dai-cells" - | "#mbox-cells" - | "#pwm-cells" - | "#iommu-cells" => { - cells_map.insert(prop.name.to_string(), prop.u32()); - } - _ => {} - } - } - - // If phandle is found, store it together with the node's full path - if let Some(ph) = phandle { - phandle_map.insert(ph, (node_path, cells_map)); - } - } - phandle_map -} - -/// Parse properties containing phandle references intelligently based on #*-cells properties -/// Supports multiple formats: -/// - Single phandle: \ -/// - phandle+specifier: \ -/// - Multiple phandle references: \ -fn parse_phandle_property_with_cells( - prop_data: &[u8], - prop_name: &str, - phandle_map: &BTreeMap)>, -) -> Vec<(u32, Vec)> { - let mut results = Vec::new(); - - debug!( - "Parsing property '{}' with cells info, data length: {} bytes", - prop_name, - prop_data.len() - ); - - if prop_data.is_empty() || !prop_data.len().is_multiple_of(4) { - warn!( - "Property '{}' data length ({} bytes) is invalid", - prop_name, - prop_data.len() - ); - return results; - } - - let u32_values: Vec = prop_data - .chunks(4) - .map(|chunk| u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]])) - .collect(); - - let mut i = 0; - while i < u32_values.len() { - let potential_phandle = u32_values[i]; - - // Check if it's a valid phandle - if let Some((device_name, cells_info)) = phandle_map.get(&potential_phandle) { - // Determine the number of cells required based on property name - let cells_count = get_cells_count_for_property(prop_name, cells_info); - trace!( - "Property '{prop_name}' requires {cells_count} cells for device '{device_name}'" - ); - - // Check if there's enough data - if i + cells_count < u32_values.len() { - let specifiers: Vec = u32_values[i + 1..=i + cells_count].to_vec(); - debug!( - "Parsed phandle reference: phandle={potential_phandle:#x}, specifiers={specifiers:?}" - ); - results.push((potential_phandle, specifiers)); - i += cells_count + 1; // Skip phandle and all specifiers - } else { - warn!( - "Property:{} not enough data for phandle {:#x}, expected {} cells but only {} values remaining", - prop_name, - potential_phandle, - cells_count, - u32_values.len() - i - 1 - ); - break; - } - } else { - // If not a valid phandle, skip this value - i += 1; - } - } - - results -} - -/// Determine the required number of cells based on property name and target node's cells information -fn get_cells_count_for_property(prop_name: &str, cells_info: &BTreeMap) -> usize { - let cells_property = match prop_name { - "clocks" | "assigned-clocks" => "#clock-cells", - "resets" => "#reset-cells", - "power-domains" => "#power-domain-cells", - "phys" => "#phy-cells", - "interrupts" | "interrupts-extended" => "#interrupt-cells", - "gpios" => "#gpio-cells", - _ if prop_name.ends_with("-gpios") || prop_name.ends_with("-gpio") => "#gpio-cells", - "dmas" => "#dma-cells", - "thermal-sensors" => "#thermal-sensor-cells", - "sound-dai" => "#sound-dai-cells", - "mboxes" => "#mbox-cells", - "pwms" => "#pwm-cells", - _ => { - debug!("Unknown property '{prop_name}', defaulting to 0 cell"); - return 0; - } - }; - - cells_info.get(cells_property).copied().unwrap_or(0) as usize -} - -/// Generic phandle property parsing function -/// Parse phandle references according to cells information with correct block size -/// Support single phandle and multiple phandle+specifier formats -/// Return full path instead of node name -fn parse_phandle_property( - prop_data: &[u8], - prop_name: &str, - phandle_map: &BTreeMap)>, -) -> Vec { - let mut dependencies = Vec::new(); - - let phandle_refs = parse_phandle_property_with_cells(prop_data, prop_name, phandle_map); - - for (phandle, specifiers) in phandle_refs { - if let Some((device_path, _cells_info)) = phandle_map.get(&phandle) { - let spec_info = if !specifiers.is_empty() { - format!(" (specifiers: {specifiers:?})") - } else { - String::new() - }; - debug!( - "Found {prop_name} dependency: phandle={phandle:#x}, device={device_path}{spec_info}" - ); - dependencies.push(device_path.clone()); - } - } - - dependencies -} - -/// Device property classifier - used to identify properties that require special handling -struct DevicePropertyClassifier; - -impl DevicePropertyClassifier { - /// Phandle properties that require special handling - includes all properties that need dependency resolution - const PHANDLE_PROPERTIES: &'static [&'static str] = &[ - "clocks", - "power-domains", - "phys", - "resets", - "dmas", - "thermal-sensors", - "mboxes", - "assigned-clocks", - "interrupt-parent", - "phy-handle", - "msi-parent", - "memory-region", - "syscon", - "regmap", - "iommus", - "interconnects", - "nvmem-cells", - "sound-dai", - "pinctrl-0", - "pinctrl-1", - "pinctrl-2", - "pinctrl-3", - "pinctrl-4", - ]; - - /// Determine if it's a phandle property that requires handling - fn is_phandle_property(prop_name: &str) -> bool { - Self::PHANDLE_PROPERTIES.contains(&prop_name) - || prop_name.ends_with("-supply") - || prop_name == "gpios" - || prop_name.ends_with("-gpios") - || prop_name.ends_with("-gpio") - || (prop_name.contains("cells") && !prop_name.starts_with("#") && prop_name.len() >= 4) - } -} - -/// Find device dependencies -fn find_device_dependencies( - device_node_path: &str, - phandle_map: &BTreeMap)>, - node_cache: &BTreeMap>, // Add node_cache parameter -) -> Vec { - let mut dependencies = Vec::new(); - - // Directly find nodes from node_cache, avoiding traversing all nodes - if let Some(nodes) = node_cache.get(device_node_path) { - // Traverse all properties of nodes to find dependencies - for node in nodes { - for prop in node.propertys() { - // Determine if it's a phandle property that needs to be processed - if DevicePropertyClassifier::is_phandle_property(prop.name) { - let mut prop_deps = - parse_phandle_property(prop.raw_value(), prop.name, phandle_map); - dependencies.append(&mut prop_deps); - } - } - } - } - - dependencies -} - -/// Get all descendant nodes based on parent node path (including child nodes, grandchild nodes, etc.) -/// Find all descendant nodes by looking up nodes with parent node path as prefix in node_cache -fn get_descendant_nodes_by_path<'a>( - node_cache: &'a BTreeMap>>, - parent_path: &str, -) -> Vec { - let mut descendant_paths = Vec::new(); - - // Special handling if parent path is root path - let search_prefix = if parent_path == "/" { - "/".to_string() - } else { - parent_path.to_string() + "/" - }; - - // Traverse node_cache, find all nodes with parent path as prefix - for path in node_cache.keys() { - // Check if path has parent path as prefix (and is not the parent path itself) - if path.starts_with(&search_prefix) && path.len() > search_prefix.len() { - // This is a descendant node path, add to results - descendant_paths.push(path.clone()); - } - } - - descendant_paths -} diff --git a/os/axvisor/src/vmm/fdt/mod.rs b/os/axvisor/src/vmm/fdt/mod.rs deleted file mode 100644 index d648ae03f..000000000 --- a/os/axvisor/src/vmm/fdt/mod.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! FDT (Flattened Device Tree) processing module for AxVisor. -//! -//! This module provides functionality for parsing and processing device tree blobs, -//! including CPU configuration, passthrough device detection, and FDT generation. - -mod create; -mod device; -mod parser; -mod print; -mod vm_fdt; - -use alloc::collections::BTreeMap; -use alloc::vec::Vec; -use axvm::config::{AxVMConfig, AxVMCrateConfig}; -use fdt_parser::Fdt; -use lazyinit::LazyInit; -use spin::Mutex; - -pub use parser::*; -// pub use print::print_fdt; -pub use create::*; -pub use device::build_node_path; - -use crate::vmm::config::{config, get_vm_dtb_arc}; - -// DTB cache for generated device trees -static GENERATED_DTB_CACHE: LazyInit>>> = LazyInit::new(); - -/// Initialize the DTB cache -pub fn init_dtb_cache() { - GENERATED_DTB_CACHE.init_once(Mutex::new(BTreeMap::new())); -} - -/// Get reference to the DTB cache -pub fn dtb_cache() -> &'static Mutex>> { - GENERATED_DTB_CACHE.get().unwrap() -} - -/// Generate guest FDT cache the result -/// # Return Value -/// Returns the generated DTB data and stores it in the global cache -pub fn crate_guest_fdt_with_cache(dtb_data: Vec, crate_config: &AxVMCrateConfig) { - // Store data in global cache - let mut cache_lock = dtb_cache().lock(); - cache_lock.insert(crate_config.base.id, dtb_data); -} - -/// Handle all FDT-related operations for aarch64 architecture -pub fn handle_fdt_operations(vm_config: &mut AxVMConfig, vm_create_config: &AxVMCrateConfig) { - let host_fdt_bytes = get_host_fdt(); - let host_fdt = Fdt::from_bytes(host_fdt_bytes) - .map_err(|e| format!("Failed to parse FDT: {e:#?}")) - .expect("Failed to parse FDT"); - set_phys_cpu_sets(vm_config, &host_fdt, vm_create_config); - - if let Some(provided_dtb) = get_developer_provided_dtb(vm_config, vm_create_config) { - info!("VM[{}] found DTB , parsing...", vm_config.id()); - update_provided_fdt(&provided_dtb, host_fdt_bytes, vm_create_config); - } else { - info!( - "VM[{}] DTB not found, generating based on the configuration file.", - vm_config.id() - ); - setup_guest_fdt_from_vmm(host_fdt_bytes, vm_config, vm_create_config); - } - - // Overlay VM config with the given DTB. - if let Some(dtb_arc) = get_vm_dtb_arc(vm_config) { - let dtb = dtb_arc.as_ref(); - parse_passthrough_devices_address(vm_config, dtb); - parse_vm_interrupt(vm_config, dtb); - } else { - error!( - "VM[{}] DTB not found in memory, skipping...", - vm_config.id() - ); - } -} - -pub fn get_developer_provided_dtb( - vm_cfg: &AxVMConfig, - crate_config: &AxVMCrateConfig, -) -> Option> { - match crate_config.kernel.image_location.as_deref() { - Some("memory") => { - let vm_imags = config::get_memory_images() - .iter() - .find(|&v| v.id == vm_cfg.id())?; - - if let Some(dtb) = vm_imags.dtb { - info!("DTB file in memory, size: 0x{:x}", dtb.len()); - return Some(dtb.to_vec()); - } - } - #[cfg(feature = "fs")] - Some("fs") => { - use axerrno::ax_err_type; - use std::io::{BufReader, Read}; - if let Some(dtb_path) = &crate_config.kernel.dtb_path { - let (dtb_file, dtb_size) = - crate::vmm::images::fs::open_image_file(dtb_path).unwrap(); - info!("DTB file in fs, size: 0x{:x}", dtb_size); - - let mut file = BufReader::new(dtb_file); - let mut dtb_buffer = vec![0; dtb_size]; - - file.read_exact(&mut dtb_buffer) - .map_err(|err| { - ax_err_type!( - Io, - format!("Failed in reading from file {}, err {:?}", dtb_path, err) - ) - }) - .unwrap(); - return Some(dtb_buffer); - } - } - _ => unimplemented!( - "Check your \"image_location\" in config.toml, \"memory\" and \"fs\" are supported,\n." - ), - } - None -} diff --git a/os/axvisor/src/vmm/fdt/parser.rs b/os/axvisor/src/vmm/fdt/parser.rs deleted file mode 100644 index e964ffc64..000000000 --- a/os/axvisor/src/vmm/fdt/parser.rs +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! FDT parsing and processing functionality. - -use alloc::{string::ToString, vec::Vec}; -use axvm::config::{AxVMConfig, AxVMCrateConfig, PassThroughDeviceConfig}; -use fdt_parser::{Fdt, FdtHeader, PciRange, PciSpace}; - -use crate::vmm::fdt::crate_guest_fdt_with_cache; -use crate::vmm::fdt::create::update_cpu_node; - -pub fn get_host_fdt() -> &'static [u8] { - const FDT_VALID_MAGIC: u32 = 0xd00d_feed; - let bootarg: usize = std::os::arceos::modules::axhal::dtb::get_bootarg(); - let header = unsafe { - core::slice::from_raw_parts(bootarg as *const u8, core::mem::size_of::()) - }; - let fdt_header = FdtHeader::from_bytes(header) - .map_err(|e| format!("Failed to parse FDT header: {e:#?}")) - .unwrap(); - - if fdt_header.magic.get() != FDT_VALID_MAGIC { - error!( - "FDT magic is invalid, expected {:#x}, got {:#x}", - FDT_VALID_MAGIC, - fdt_header.magic.get() - ); - } - - unsafe { core::slice::from_raw_parts(bootarg as *const u8, fdt_header.total_size()) } -} - -pub fn setup_guest_fdt_from_vmm( - fdt_bytes: &[u8], - vm_cfg: &mut AxVMConfig, - crate_config: &AxVMCrateConfig, -) { - let fdt = Fdt::from_bytes(fdt_bytes) - .map_err(|e| format!("Failed to parse FDT: {e:#?}")) - .expect("Failed to parse FDT"); - - // Call the modified function and get the returned device name list - let passthrough_device_names = super::device::find_all_passthrough_devices(vm_cfg, &fdt); - - let dtb_data = super::create::crate_guest_fdt(&fdt, &passthrough_device_names, crate_config); - crate_guest_fdt_with_cache(dtb_data, crate_config); -} - -pub fn set_phys_cpu_sets(vm_cfg: &mut AxVMConfig, fdt: &Fdt, crate_config: &AxVMCrateConfig) { - // Find and parse CPU information from host DTB - let host_cpus: Vec<_> = fdt.find_nodes("/cpus/cpu").collect(); - info!("Found {} host CPU nodes", &host_cpus.len()); - - let phys_cpu_ids = crate_config - .base - .phys_cpu_ids - .as_ref() - .expect("ERROR: phys_cpu_ids not found in config.toml"); - - // Collect all CPU node information into Vec to avoid using iterators multiple times - let cpu_nodes_info: Vec<_> = host_cpus - .iter() - .filter_map(|cpu_node| { - if let Some(mut cpu_reg) = cpu_node.reg() { - if let Some(r) = cpu_reg.next() { - info!( - "CPU node: {}, phys_cpu_id: 0x{:x}", - cpu_node.name(), - r.address - ); - Some((cpu_node.name().to_string(), r.address as usize)) - } else { - None - } - } else { - None - } - }) - .collect(); - // Create mapping from phys_cpu_id to physical CPU index - // Collect all unique CPU addresses, maintaining the order of appearance in the device tree - let mut unique_cpu_addresses = Vec::new(); - for (_, cpu_address) in &cpu_nodes_info { - if !unique_cpu_addresses.contains(cpu_address) { - unique_cpu_addresses.push(*cpu_address); - } else { - panic!("Duplicate CPU address found"); - } - } - - // Assign index to each CPU address in the device tree and print detailed information - for (index, &cpu_address) in unique_cpu_addresses.iter().enumerate() { - // Find all CPU nodes using this address - for (cpu_name, node_address) in &cpu_nodes_info { - if *node_address == cpu_address { - debug!( - " CPU node: {cpu_name}, address: 0x{cpu_address:x}, assigned index: {index}" - ); - break; // Print each address only once - } - } - } - - // Calculate phys_cpu_sets based on phys_cpu_ids in vcpu_mappings - let mut new_phys_cpu_sets = Vec::new(); - for phys_cpu_id in phys_cpu_ids { - // Find the index corresponding to phys_cpu_id in unique_cpu_addresses - if let Some(cpu_index) = unique_cpu_addresses - .iter() - .position(|&addr| addr == *phys_cpu_id) - { - let cpu_mask = 1usize << cpu_index; // Convert index to mask bit - new_phys_cpu_sets.push(cpu_mask); - debug!( - "vCPU {} with phys_cpu_id 0x{:x} mapped to CPU index {} (mask: 0x{:x})", - vm_cfg.id(), - phys_cpu_id, - cpu_index, - cpu_mask - ); - } else { - error!( - "vCPU {} with phys_cpu_id 0x{:x} not found in device tree!", - vm_cfg.id(), - phys_cpu_id - ); - } - } - - // Update phys_cpu_sets in VM configuration (if VM configuration supports setting) - info!("Calculated phys_cpu_sets: {new_phys_cpu_sets:?}"); - - vm_cfg - .phys_cpu_ls_mut() - .set_guest_cpu_sets(new_phys_cpu_sets); - - debug!( - "vcpu_mappings: {:?}", - vm_cfg.phys_cpu_ls_mut().get_vcpu_affinities_pcpu_ids() - ); -} - -/// Add address mapping configuration for a device -fn add_device_address_config( - vm_cfg: &mut AxVMConfig, - node_name: &str, - base_address: usize, - size: usize, - index: usize, - prefix: Option<&str>, -) { - // Only process devices with address information - if size == 0 { - return; - } - - // Create a device configuration for each address segment - let device_name = if index == 0 { - match prefix { - Some(p) => format!("{node_name}-{p}"), - None => node_name.to_string(), - } - } else { - match prefix { - Some(p) => format!("{node_name}-{p}-region{index}"), - None => format!("{node_name}-region{index}"), - } - }; - - // Add new device configuration - let pt_dev = axvm::config::PassThroughDeviceConfig { - name: device_name, - base_gpa: base_address, - base_hpa: base_address, - length: size, - irq_id: 0, - }; - vm_cfg.add_pass_through_device(pt_dev); -} - -/// Add ranges property configuration for PCIe devices -fn add_pci_ranges_config(vm_cfg: &mut AxVMConfig, node_name: &str, range: &PciRange, index: usize) { - let base_address = range.cpu_address as usize; - let size = range.size as usize; - - // Only process devices with address information - if size == 0 { - return; - } - - // Create a device configuration for each address segment - let prefix = match range.space { - PciSpace::Configuration => "config", - PciSpace::IO => "io", - PciSpace::Memory32 => "mem32", - PciSpace::Memory64 => "mem64", - }; - - let device_name = if index == 0 { - format!("{node_name}-{prefix}") - } else { - format!("{node_name}-{prefix}-region{index}") - }; - - // Add new device configuration - let pt_dev = axvm::config::PassThroughDeviceConfig { - name: device_name, - base_gpa: base_address, - base_hpa: base_address, - length: size, - irq_id: 0, - }; - vm_cfg.add_pass_through_device(pt_dev); - - trace!( - "Added PCIe passthrough device {}: base=0x{:x}, size=0x{:x}, space={:?}", - node_name, base_address, size, range.space - ); -} - -pub fn parse_passthrough_devices_address(vm_cfg: &mut AxVMConfig, dtb: &[u8]) { - let devices = vm_cfg.pass_through_devices().to_vec(); - if !devices.is_empty() && devices[0].length != 0 { - for (index, device) in devices.iter().enumerate() { - add_device_address_config( - vm_cfg, - &device.name, - device.base_gpa, - device.length, - index, - None, - ); - } - } else { - let fdt = Fdt::from_bytes(dtb) - .expect("Failed to parse DTB image, perhaps the DTB is invalid or corrupted"); - - // Clear existing passthrough device configurations - vm_cfg.clear_pass_through_devices(); - - // Traverse all device tree nodes - for node in fdt.all_nodes() { - // Skip root node - if node.name() == "/" || node.name().starts_with("memory") { - continue; - } - - let node_name = node.name().to_string(); - - // Check if it's a PCIe device node - if node_name.starts_with("pcie@") || node_name.contains("pci") { - // Process PCIe device's ranges property - if let Some(pci) = node.clone().into_pci() - && let Ok(ranges) = pci.ranges() - { - for (index, range) in ranges.enumerate() { - add_pci_ranges_config(vm_cfg, &node_name, &range, index); - } - } - - // Process PCIe device's reg property (ECAM space) - if let Some(reg_iter) = node.reg() { - for (index, reg) in reg_iter.enumerate() { - let base_address = reg.address as usize; - let size = reg.size.unwrap_or(0); - - add_device_address_config( - vm_cfg, - &node_name, - base_address, - size, - index, - Some("ecam"), - ); - } - } - } else { - // Get device's reg property (process regular devices) - if let Some(reg_iter) = node.reg() { - // Process all address segments of the device - for (index, reg) in reg_iter.enumerate() { - // Get device's address and size information - let base_address = reg.address as usize; - let size = reg.size.unwrap_or(0); - - add_device_address_config( - vm_cfg, - &node_name, - base_address, - size, - index, - None, - ); - } - } - } - } - trace!( - "All passthrough devices: {:#x?}", - vm_cfg.pass_through_devices() - ); - debug!( - "Finished parsing passthrough devices, total: {}", - vm_cfg.pass_through_devices().len() - ); - } -} - -pub fn parse_vm_interrupt(vm_cfg: &mut AxVMConfig, dtb: &[u8]) { - const GIC_PHANDLE: usize = 1; - let fdt = Fdt::from_bytes(dtb) - .expect("Failed to parse DTB image, perhaps the DTB is invalid or corrupted"); - - for node in fdt.all_nodes() { - let name = node.name(); - - if name.starts_with("memory") { - continue; - } - // Skip the interrupt controller, as we will use vGIC - // TODO: filter with compatible property and parse its phandle from DT; maybe needs a second pass? - else if name.starts_with("interrupt-controller") - || name.starts_with("intc") - || name.starts_with("its") - { - info!("skipping node {name} to use vGIC"); - continue; - } - - // Collect all GIC_SPI interrupts and add them to vGIC - if let Some(interrupts) = node.interrupts() { - // TODO: skip non-GIC interrupt - if let Some(parent) = node.interrupt_parent() { - trace!("node: {}, intr parent: {}", name, parent.node.name()); - if let Some(phandle) = parent.node.phandle() { - if phandle.as_usize() != GIC_PHANDLE { - debug!( - "node: {}, intr parent: {}, phandle: 0x{:x} is not GIC!", - name, - parent.node.name(), - phandle.as_usize() - ); - } - } else { - warn!( - "node: {}, intr parent: {} no phandle!", - name, - parent.node.name(), - ); - } - } else { - warn!("node: {name} no interrupt parent!"); - } - - for interrupt in interrupts { - // - for (k, v) in interrupt.enumerate() { - match k { - 0 => { - if v == 0 { - trace!("node: {name}, GIC_SPI"); - } else { - debug!("node: {name}, intr type: {v}, not GIC_SPI, not supported!"); - break; - } - } - 1 => { - trace!("node: {name}, interrupt id: 0x{v:x}"); - vm_cfg.add_pass_through_spi(v); - } - 2 => { - trace!("node: {name}, interrupt mode: 0x{v:x}"); - } - _ => { - warn!("unknown interrupt property {k}:0x{v:x}") - } - } - } - } - } - } - - vm_cfg.add_pass_through_device(PassThroughDeviceConfig { - name: "Fake Node".to_string(), - base_gpa: 0x0, - base_hpa: 0x0, - length: 0x20_0000, - irq_id: 0, - }); -} - -pub fn update_provided_fdt(provided_dtb: &[u8], host_dtb: &[u8], crate_config: &AxVMCrateConfig) { - let provided_fdt = Fdt::from_bytes(provided_dtb) - .expect("Failed to parse DTB image, perhaps the DTB is invalid or corrupted"); - let host_fdt = Fdt::from_bytes(host_dtb) - .expect("Failed to parse DTB image, perhaps the DTB is invalid or corrupted"); - let provided_dtb_data = update_cpu_node(&provided_fdt, &host_fdt, crate_config); - crate_guest_fdt_with_cache(provided_dtb_data, crate_config); -} diff --git a/os/axvisor/src/vmm/fdt/print.rs b/os/axvisor/src/vmm/fdt/print.rs deleted file mode 100644 index 96b4785c5..000000000 --- a/os/axvisor/src/vmm/fdt/print.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! FDT parsing and processing functionality. - -use fdt_parser::{Fdt, FdtHeader}; - -#[allow(dead_code)] -pub fn print_fdt(fdt_addr: usize) { - const FDT_VALID_MAGIC: u32 = 0xd00d_feed; - let header = unsafe { - core::slice::from_raw_parts(fdt_addr as *const u8, core::mem::size_of::()) - }; - let fdt_header = FdtHeader::from_bytes(header) - .map_err(|e| format!("Failed to parse FDT header: {e:#?}")) - .unwrap(); - - if fdt_header.magic.get() != FDT_VALID_MAGIC { - error!( - "FDT magic is invalid, expected {:#x}, got {:#x}", - FDT_VALID_MAGIC, - fdt_header.magic.get() - ); - return; - } - - let fdt_bytes = - unsafe { core::slice::from_raw_parts(fdt_addr as *const u8, fdt_header.total_size()) }; - - let fdt = Fdt::from_bytes(fdt_bytes) - .map_err(|e| format!("Failed to parse FDT: {e:#?}")) - .expect("Failed to parse FDT"); - - // Statistics of node count and level distribution - let mut node_count = 0; - let mut level_counts = alloc::collections::BTreeMap::new(); - let mut max_level = 0; - - info!("=== FDT Node Information Statistics ==="); - - // Traverse all nodes once for statistics (following optimization strategy) - for node in fdt.all_nodes() { - node_count += 1; - - // Count nodes by level - *level_counts.entry(node.level).or_insert(0) += 1; - - // Record maximum level - if node.level > max_level { - max_level = node.level; - } - - // Count property numbers - let node_properties_count = node.propertys().count(); - - trace!( - "Node[{}]: {} (Level: {}, Properties: {})", - node_count, - node.name(), - node.level, - node_properties_count - ); - - for prop in node.propertys() { - trace!( - "Properties: {}, Raw_value: {:x?}", - prop.name, - prop.raw_value() - ); - } - } - - info!("=== FDT Statistics Results ==="); - info!("Total node count: {node_count}"); - info!("FDT total size: {} bytes", fdt_header.total_size()); - info!("Maximum level depth: {max_level}"); - - info!("Node distribution by level:"); - for (level, count) in level_counts { - let percentage = (count as f32 / node_count as f32) * 100.0; - info!(" Level {level}: {count} nodes ({percentage:.1}%)"); - } -} - -#[allow(dead_code)] -pub fn print_guest_fdt(fdt_bytes: &[u8]) { - let fdt = Fdt::from_bytes(fdt_bytes) - .map_err(|e| format!("Failed to parse FDT: {e:#?}")) - .expect("Failed to parse FDT"); - // Statistics of node count and level distribution - let mut node_count = 0; - let mut level_counts = alloc::collections::BTreeMap::new(); - let mut max_level = 0; - - info!("=== FDT Node Information Statistics ==="); - - // Traverse all nodes once for statistics (following optimization strategy) - for node in fdt.all_nodes() { - node_count += 1; - - // Count nodes by level - *level_counts.entry(node.level).or_insert(0) += 1; - - // Record maximum level - if node.level > max_level { - max_level = node.level; - } - - // Count property numbers - let node_properties_count = node.propertys().count(); - - info!( - "Node[{}]: {} (Level: {}, Properties: {})", - node_count, - node.name(), - node.level, - node_properties_count - ); - - for prop in node.propertys() { - info!( - "Properties: {}, Raw_value: {:x?}", - prop.name, - prop.raw_value() - ); - } - } - - info!("=== FDT Statistics Results ==="); - info!("Total node count: {node_count}"); - info!("Maximum level depth: {max_level}"); - - info!("Node distribution by level:"); - for (level, count) in level_counts { - let percentage = (count as f32 / node_count as f32) * 100.0; - info!(" Level {level}: {count} nodes ({percentage:.1}%)"); - } -} diff --git a/os/axvisor/src/vmm/fdt/vm_fdt/mod.rs b/os/axvisor/src/vmm/fdt/vm_fdt/mod.rs deleted file mode 100644 index b2ac85663..000000000 --- a/os/axvisor/src/vmm/fdt/vm_fdt/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -mod writer; - -pub use writer::{FdtWriter, FdtWriterNode}; - -/// Magic number used in the FDT header. -pub const FDT_MAGIC: u32 = 0xd00dfeed; - -pub const FDT_BEGIN_NODE: u32 = 0x00000001; -pub const FDT_END_NODE: u32 = 0x00000002; -pub const FDT_PROP: u32 = 0x00000003; -pub const FDT_END: u32 = 0x00000009; - -pub const NODE_NAME_MAX_LEN: usize = 31; -pub const PROPERTY_NAME_MAX_LEN: usize = 63; diff --git a/os/axvisor/src/vmm/fdt/vm_fdt/writer.rs b/os/axvisor/src/vmm/fdt/vm_fdt/writer.rs deleted file mode 100644 index 37f4f5e3b..000000000 --- a/os/axvisor/src/vmm/fdt/vm_fdt/writer.rs +++ /dev/null @@ -1,553 +0,0 @@ -use alloc::collections::BTreeMap; -use alloc::ffi::CString; -use alloc::string::String; -use alloc::vec::Vec; -use core::cmp::{Ord, Ordering}; -use core::convert::TryInto; -use core::fmt; -use core::mem::size_of_val; -use hashbrown::HashSet; - -use super::{ - FDT_BEGIN_NODE, FDT_END, FDT_END_NODE, FDT_MAGIC, FDT_PROP, NODE_NAME_MAX_LEN, - PROPERTY_NAME_MAX_LEN, -}; - -#[derive(Debug, Eq, PartialEq)] -/// Errors associated with creating the Flattened Device Tree. -pub enum Error { - /// Properties may not be added before beginning a node. - PropertyBeforeBeginNode, - /// Properties may not be added after a node has been ended. - PropertyAfterEndNode, - /// Property value size must fit in 32 bits. - PropertyValueTooLarge, - /// Total size must fit in 32 bits. - TotalSizeTooLarge, - /// Strings cannot contain NUL. - InvalidString, - /// Attempted to end a node that was not the most recent. - OutOfOrderEndNode, - /// Attempted to call finish without ending all nodes. - UnclosedNode, - /// Memory reservation is invalid. - InvalidMemoryReservation, - /// Memory reservations are overlapping. - OverlappingMemoryReservations, - /// Invalid node name. - InvalidNodeName, - /// Invalid property name. - InvalidPropertyName, - /// Node depth exceeds FDT_MAX_NODE_DEPTH - NodeDepthTooLarge, - /// Duplicate phandle property - DuplicatePhandle, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::PropertyBeforeBeginNode => { - write!(f, "Properties may not be added before beginning a node") - } - Error::PropertyAfterEndNode => { - write!(f, "Properties may not be added after a node has been ended") - } - Error::PropertyValueTooLarge => write!(f, "Property value size must fit in 32 bits"), - Error::TotalSizeTooLarge => write!(f, "Total size must fit in 32 bits"), - Error::InvalidString => write!(f, "Strings cannot contain NUL"), - Error::OutOfOrderEndNode => { - write!(f, "Attempted to end a node that was not the most recent") - } - Error::UnclosedNode => write!(f, "Attempted to call finish without ending all nodes"), - Error::InvalidMemoryReservation => write!(f, "Memory reservation is invalid"), - Error::OverlappingMemoryReservations => { - write!(f, "Memory reservations are overlapping") - } - Error::InvalidNodeName => write!(f, "Invalid node name"), - Error::InvalidPropertyName => write!(f, "Invalid property name"), - Error::NodeDepthTooLarge => write!(f, "Node depth exceeds FDT_MAX_NODE_DEPTH"), - Error::DuplicatePhandle => write!(f, "Duplicate phandle value"), - } - } -} - -/// Result of a FDT writer operation. -pub type Result = core::result::Result; - -const FDT_HEADER_SIZE: usize = 40; -const FDT_VERSION: u32 = 17; -const FDT_LAST_COMP_VERSION: u32 = 16; -/// The same max depth as in the Linux kernel. -const FDT_MAX_NODE_DEPTH: usize = 64; - -/// Interface for writing a Flattened Devicetree (FDT) and emitting a Devicetree Blob (DTB). -#[derive(Debug)] -pub struct FdtWriter { - data: Vec, - off_mem_rsvmap: u32, - off_dt_struct: u32, - strings: Vec, - string_offsets: BTreeMap, - node_depth: usize, - node_ended: bool, - boot_cpuid_phys: u32, - // The set is used to track the uniqueness of phandle values as required by the spec - // https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#phandle - #[allow(dead_code)] - phandles: HashSet, -} - -/// Reserved physical memory region. -/// -/// This represents an area of physical memory reserved by the firmware and unusable by the OS. -/// For example, this could be used to preserve bootloader code or data used at runtime. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct FdtReserveEntry { - address: u64, - size: u64, -} - -impl FdtReserveEntry { - /// Create a memory reservation for the FDT. - /// - /// # Arguments - /// - /// * address: Physical address of the beginning of the reserved region. - /// * size: Size of the reserved region in bytes. - #[allow(dead_code)] - pub fn new(address: u64, size: u64) -> Result { - if address.checked_add(size).is_none() || size == 0 { - return Err(Error::InvalidMemoryReservation); - } - - Ok(FdtReserveEntry { address, size }) - } -} - -impl Ord for FdtReserveEntry { - fn cmp(&self, other: &Self) -> Ordering { - self.address.cmp(&other.address) - } -} - -impl PartialOrd for FdtReserveEntry { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -// Returns true if there are any overlapping memory reservations. -fn check_overlapping(mem_reservations: &[FdtReserveEntry]) -> Result<()> { - let mut mem_rsvmap_copy = mem_reservations.to_vec(); - mem_rsvmap_copy.sort(); - let overlapping = mem_rsvmap_copy.windows(2).any(|w| { - // The following add cannot overflow because we can only have - // valid FdtReserveEntry (as per the constructor of the type). - w[0].address + w[0].size > w[1].address - }); - - if overlapping { - return Err(Error::OverlappingMemoryReservations); - } - - Ok(()) -} - -// Check if `name` is a valid node name in the form "node-name@unit-address". -// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#node-name-requirements -fn node_name_valid(name: &str) -> bool { - // Special case: allow empty node names. - // This is technically not allowed by the spec, but it seems to be accepted in practice. - if name.is_empty() { - return true; - } - - let mut parts = name.split('@'); - - let node_name = parts.next().unwrap(); // split() always returns at least one part - let unit_address = parts.next(); - - if unit_address.is_some() && parts.next().is_some() { - // Node names should only contain one '@'. - return false; - } - - if node_name.is_empty() || node_name.len() > NODE_NAME_MAX_LEN { - return false; - } - - // if !node_name.starts_with(node_name_valid_first_char) { - // return false; - // } - - if node_name.contains(|c: char| !node_name_valid_char(c)) { - return false; - } - - if let Some(unit_address) = unit_address - && unit_address.contains(|c: char| !node_name_valid_char(c)) - { - return false; - } - - true -} - -fn node_name_valid_char(c: char) -> bool { - c.is_ascii_alphanumeric() || matches!(c, ',' | '.' | '_' | '+' | '-') -} - -#[allow(dead_code)] -fn node_name_valid_first_char(c: char) -> bool { - c.is_ascii_alphabetic() -} - -// Check if `name` is a valid property name. -// https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#property-names -fn property_name_valid(name: &str) -> bool { - if name.is_empty() || name.len() > PROPERTY_NAME_MAX_LEN { - return false; - } - - if name.contains(|c: char| !property_name_valid_char(c)) { - return false; - } - - true -} - -fn property_name_valid_char(c: char) -> bool { - matches!(c, '0'..='9' | 'a'..='z' | 'A'..='Z' | ',' | '.' | '_' | '+' | '?' | '#' | '-') -} - -/// Handle to an open node created by `FdtWriter::begin_node`. -/// -/// This must be passed back to `FdtWriter::end_node` to close the nodes. -/// Nodes must be closed in reverse order as they were opened, matching the nesting structure -/// of the devicetree. -#[derive(Debug)] -pub struct FdtWriterNode { - depth: usize, -} - -impl FdtWriter { - /// Create a new Flattened Devicetree writer instance. - pub fn new() -> Result { - FdtWriter::new_with_mem_reserv(&[]) - } - - /// Create a new Flattened Devicetree writer instance. - /// - /// # Arguments - /// - /// `mem_reservations` - reserved physical memory regions to list in the FDT header. - pub fn new_with_mem_reserv(mem_reservations: &[FdtReserveEntry]) -> Result { - let data = vec![0u8; FDT_HEADER_SIZE]; // Reserve space for header. - - let mut fdt = FdtWriter { - data, - off_mem_rsvmap: 0, - off_dt_struct: 0, - strings: Vec::new(), - string_offsets: BTreeMap::new(), - node_depth: 0, - node_ended: false, - boot_cpuid_phys: 0, - phandles: HashSet::new(), - }; - - fdt.align(8); - // This conversion cannot fail since the size of the header is fixed. - fdt.off_mem_rsvmap = fdt.data.len() as u32; - - check_overlapping(mem_reservations)?; - fdt.write_mem_rsvmap(mem_reservations); - - fdt.align(4); - fdt.off_dt_struct = fdt - .data - .len() - .try_into() - .map_err(|_| Error::TotalSizeTooLarge)?; - - Ok(fdt) - } - - fn write_mem_rsvmap(&mut self, mem_reservations: &[FdtReserveEntry]) { - for rsv in mem_reservations { - self.append_u64(rsv.address); - self.append_u64(rsv.size); - } - - self.append_u64(0); - self.append_u64(0); - } - - /// Set the `boot_cpuid_phys` field of the devicetree header. - /// - /// # Example - /// - /// ```rust - /// use vm_fdt::{Error, FdtWriter}; - /// - /// fn create_fdt() -> Result, Error> { - /// let mut fdt = FdtWriter::new()?; - /// fdt.set_boot_cpuid_phys(0x12345678); - /// // ... add other nodes & properties - /// fdt.finish() - /// } - /// - /// # let dtb = create_fdt().unwrap(); - /// ``` - #[allow(dead_code)] - pub fn set_boot_cpuid_phys(&mut self, boot_cpuid_phys: u32) { - self.boot_cpuid_phys = boot_cpuid_phys; - } - - // Append `num_bytes` padding bytes (0x00). - fn pad(&mut self, num_bytes: usize) { - self.data.extend(core::iter::repeat_n(0, num_bytes)); - } - - // Append padding bytes (0x00) until the length of data is a multiple of `alignment`. - fn align(&mut self, alignment: usize) { - let offset = self.data.len() % alignment; - if offset != 0 { - self.pad(alignment - offset); - } - } - - // Rewrite the value of a big-endian u32 within data. - fn update_u32(&mut self, offset: usize, val: u32) { - // Safe to use `+ 4` since we are calling this function with small values, and it's a - // private function. - let data_slice = &mut self.data[offset..offset + 4]; - data_slice.copy_from_slice(&val.to_be_bytes()); - } - - fn append_u32(&mut self, val: u32) { - self.data.extend_from_slice(&val.to_be_bytes()); - } - - fn append_u64(&mut self, val: u64) { - self.data.extend_from_slice(&val.to_be_bytes()); - } - - /// Open a new FDT node. - /// - /// The node must be closed using `end_node`. - /// - /// # Arguments - /// - /// `name` - name of the node; must not contain any NUL bytes. - pub fn begin_node(&mut self, name: &str) -> Result { - if self.node_depth >= FDT_MAX_NODE_DEPTH { - return Err(Error::NodeDepthTooLarge); - } - - let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?; - // The unit adddress part of the node name, if present, is not fully validated - // since the exact requirements depend on the bus mapping. - // https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html#node-name-requirements - if !node_name_valid(name) { - return Err(Error::InvalidNodeName); - } - self.append_u32(FDT_BEGIN_NODE); - self.data.extend(name_cstr.to_bytes_with_nul()); - self.align(4); - // This can not overflow due to the `if` at the beginning of the function - // where the current depth is checked against FDT_MAX_NODE_DEPTH. - self.node_depth += 1; - self.node_ended = false; - Ok(FdtWriterNode { - depth: self.node_depth, - }) - } - - /// Close a node previously opened with `begin_node`. - pub fn end_node(&mut self, node: FdtWriterNode) -> Result<()> { - if node.depth != self.node_depth { - return Err(Error::OutOfOrderEndNode); - } - - self.append_u32(FDT_END_NODE); - // This can not underflow. The above `if` makes sure there is at least one open node - // (node_depth >= 1). - self.node_depth -= 1; - self.node_ended = true; - Ok(()) - } - - // Find an existing instance of a string `s`, or add it to the strings block. - // Returns the offset into the strings block. - fn intern_string(&mut self, s: CString) -> Result { - if let Some(off) = self.string_offsets.get(&s) { - Ok(*off) - } else { - let off = self - .strings - .len() - .try_into() - .map_err(|_| Error::TotalSizeTooLarge)?; - self.strings.extend_from_slice(s.to_bytes_with_nul()); - self.string_offsets.insert(s, off); - Ok(off) - } - } - - /// Write a property. - /// - /// # Arguments - /// - /// `name` - name of the property; must not contain any NUL bytes. - /// `val` - value of the property (raw byte array). - pub fn property(&mut self, name: &str, val: &[u8]) -> Result<()> { - if self.node_ended { - return Err(Error::PropertyAfterEndNode); - } - - if self.node_depth == 0 { - return Err(Error::PropertyBeforeBeginNode); - } - - let name_cstr = CString::new(name).map_err(|_| Error::InvalidString)?; - - if !property_name_valid(name) { - return Err(Error::InvalidPropertyName); - } - - let len = val - .len() - .try_into() - .map_err(|_| Error::PropertyValueTooLarge)?; - - let nameoff = self.intern_string(name_cstr)?; - self.append_u32(FDT_PROP); - self.append_u32(len); - self.append_u32(nameoff); - self.data.extend_from_slice(val); - self.align(4); - Ok(()) - } - - /// Write an empty property. - #[allow(dead_code)] - pub fn property_null(&mut self, name: &str) -> Result<()> { - self.property(name, &[]) - } - - /// Write a string property. - pub fn property_string(&mut self, name: &str, val: &str) -> Result<()> { - let cstr_value = CString::new(val).map_err(|_| Error::InvalidString)?; - self.property(name, cstr_value.to_bytes_with_nul()) - } - - /// Write a stringlist property. - #[allow(dead_code)] - pub fn property_string_list(&mut self, name: &str, values: Vec) -> Result<()> { - let mut bytes = Vec::new(); - for s in values { - let cstr = CString::new(s).map_err(|_| Error::InvalidString)?; - bytes.extend_from_slice(cstr.to_bytes_with_nul()); - } - self.property(name, &bytes) - } - - /// Write a 32-bit unsigned integer property. - #[allow(dead_code)] - pub fn property_u32(&mut self, name: &str, val: u32) -> Result<()> { - self.property(name, &val.to_be_bytes()) - } - - /// Write a 64-bit unsigned integer property. - #[allow(dead_code)] - pub fn property_u64(&mut self, name: &str, val: u64) -> Result<()> { - self.property(name, &val.to_be_bytes()) - } - - /// Write a property containing an array of 32-bit unsigned integers. - pub fn property_array_u32(&mut self, name: &str, cells: &[u32]) -> Result<()> { - let mut arr = Vec::with_capacity(size_of_val(cells)); - for &c in cells { - arr.extend(c.to_be_bytes()); - } - self.property(name, &arr) - } - - /// Write a property containing an array of 64-bit unsigned integers. - #[allow(dead_code)] - pub fn property_array_u64(&mut self, name: &str, cells: &[u64]) -> Result<()> { - let mut arr = Vec::with_capacity(size_of_val(cells)); - for &c in cells { - arr.extend(c.to_be_bytes()); - } - self.property(name, &arr) - } - - /// Write a [`phandle`](https://devicetree-specification.readthedocs.io/en/stable/devicetree-basics.html?#phandle) - /// property. The value is checked for uniqueness within the FDT. In the case of a duplicate - /// [`Error::DuplicatePhandle`] is returned. - #[allow(dead_code)] - pub fn property_phandle(&mut self, val: u32) -> Result<()> { - if !self.phandles.insert(val) { - return Err(Error::DuplicatePhandle); - } - self.property("phandle", &val.to_be_bytes()) - } - - /// Finish writing the Devicetree Blob (DTB). - /// - /// Returns the DTB as a vector of bytes, consuming the `FdtWriter`. - pub fn finish(mut self) -> Result> { - if self.node_depth > 0 { - return Err(Error::UnclosedNode); - } - - self.append_u32(FDT_END); - let size_dt_plus_header: u32 = self - .data - .len() - .try_into() - .map_err(|_| Error::TotalSizeTooLarge)?; - // The following operation cannot fail because the total size of data - // also includes the offset, and we checked that `size_dt_plus_header` - // does not wrap around when converted to an u32. - let size_dt_struct = size_dt_plus_header - self.off_dt_struct; - - let off_dt_strings = self - .data - .len() - .try_into() - .map_err(|_| Error::TotalSizeTooLarge)?; - let size_dt_strings = self - .strings - .len() - .try_into() - .map_err(|_| Error::TotalSizeTooLarge)?; - - let totalsize = self - .data - .len() - .checked_add(self.strings.len()) - .ok_or(Error::TotalSizeTooLarge)?; - let totalsize = totalsize.try_into().map_err(|_| Error::TotalSizeTooLarge)?; - - // Finalize the header. - self.update_u32(0, FDT_MAGIC); - self.update_u32(4, totalsize); - self.update_u32(2 * 4, self.off_dt_struct); - self.update_u32(3 * 4, off_dt_strings); - self.update_u32(4 * 4, self.off_mem_rsvmap); - self.update_u32(5 * 4, FDT_VERSION); - self.update_u32(6 * 4, FDT_LAST_COMP_VERSION); - self.update_u32(7 * 4, self.boot_cpuid_phys); - self.update_u32(8 * 4, size_dt_strings); - self.update_u32(9 * 4, size_dt_struct); - - // Add the strings block. - self.data.append(&mut self.strings); - - Ok(self.data) - } -} diff --git a/os/axvisor/src/vmm/hvc.rs b/os/axvisor/src/vmm/hvc.rs deleted file mode 100644 index 578f0b5b8..000000000 --- a/os/axvisor/src/vmm/hvc.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axaddrspace::{GuestPhysAddr, MappingFlags}; -use axerrno::{AxResult, ax_err, ax_err_type}; -use axhvc::{HyperCallCode, HyperCallResult}; - -use crate::vmm::ivc::{self, IVCChannel}; -use crate::vmm::{VCpuRef, VMRef}; - -pub struct HyperCall { - _vcpu: VCpuRef, - vm: VMRef, - code: HyperCallCode, - args: [u64; 6], -} - -impl HyperCall { - pub fn new(vcpu: VCpuRef, vm: VMRef, code: u64, args: [u64; 6]) -> AxResult { - let code = HyperCallCode::try_from(code as u32).map_err(|e| { - warn!("Invalid hypercall code: {code} e {e:?}"); - ax_err_type!(InvalidInput) - })?; - - Ok(Self { - _vcpu: vcpu, - vm, - code, - args, - }) - } - - pub fn execute(&self) -> HyperCallResult { - match self.code { - HyperCallCode::HIVCPublishChannel => { - let key = self.args[0] as usize; - let shm_base_gpa_ptr = GuestPhysAddr::from_usize(self.args[1] as usize); - let shm_size_ptr = GuestPhysAddr::from_usize(self.args[2] as usize); - - info!( - "VM[{}] HyperCall {:?} key {:#x}", - self.vm.id(), - self.code, - key - ); - // User will pass the size of the shared memory region, - // we will allocate the shared memory region based on this size. - let shm_region_size = self.vm.read_from_guest_of::(shm_size_ptr)?; - let (shm_base_gpa, shm_region_size) = self.vm.alloc_ivc_channel(shm_region_size)?; - - let ivc_channel = - IVCChannel::alloc(self.vm.id(), key, shm_region_size, shm_base_gpa)?; - - let actual_size = ivc_channel.size(); - - self.vm.map_region( - shm_base_gpa, - ivc_channel.base_hpa(), - actual_size, - MappingFlags::READ | MappingFlags::WRITE, - )?; - - self.vm - .write_to_guest_of(shm_base_gpa_ptr, &shm_base_gpa.as_usize())?; - self.vm.write_to_guest_of(shm_size_ptr, &actual_size)?; - - ivc::insert_channel(self.vm.id(), ivc_channel)?; - - Ok(0) - } - HyperCallCode::HIVCUnPublishChannel => { - let key = self.args[0] as usize; - - info!( - "VM[{}] HyperCall {:?} with key {:#x}", - self.vm.id(), - self.code, - key - ); - let (base_gpa, size) = ivc::unpublish_channel(self.vm.id(), key)?.unwrap(); - self.vm.unmap_region(base_gpa, size)?; - - Ok(0) - } - HyperCallCode::HIVCSubscribChannel => { - let publisher_vm_id = self.args[0] as usize; - let key = self.args[1] as usize; - let shm_base_gpa_ptr = GuestPhysAddr::from_usize(self.args[2] as usize); - let shm_size_ptr = GuestPhysAddr::from_usize(self.args[3] as usize); - - info!( - "VM[{}] HyperCall {:?} to VM[{}]", - self.vm.id(), - self.code, - publisher_vm_id - ); - - let shm_size = ivc::get_channel_size(publisher_vm_id, key)?; - let (shm_base_gpa, _) = self.vm.alloc_ivc_channel(shm_size)?; - - let (base_hpa, actual_size) = ivc::subscribe_to_channel_of_publisher( - publisher_vm_id, - key, - self.vm.id(), - shm_base_gpa, - )?; - - // TODO: seperate the mapping flags of metadata and data. - self.vm.map_region( - shm_base_gpa, - base_hpa, - actual_size, - MappingFlags::READ | MappingFlags::WRITE, - )?; - - self.vm - .write_to_guest_of(shm_base_gpa_ptr, &shm_base_gpa.as_usize())?; - self.vm.write_to_guest_of(shm_size_ptr, &actual_size)?; - - info!( - "VM[{}] HyperCall HIVC_REGISTER_SUBSCRIBER success, base GPA: {:#x}, size: {}", - self.vm.id(), - shm_base_gpa, - actual_size - ); - - Ok(0) - } - HyperCallCode::HIVCUnSubscribChannel => { - let publisher_vm_id = self.args[0] as usize; - let key = self.args[1] as usize; - - info!( - "VM[{}] HyperCall {:?} from VM[{}]", - self.vm.id(), - self.code, - publisher_vm_id - ); - let (base_gpa, size) = - ivc::unsubscribe_from_channel_of_publisher(publisher_vm_id, key, self.vm.id())?; - self.vm.unmap_region(base_gpa, size)?; - - Ok(0) - } - _ => { - warn!("Unsupported hypercall code: {:?}", self.code); - ax_err!(Unsupported)? - } - } - } -} diff --git a/os/axvisor/src/vmm/images/linux.rs b/os/axvisor/src/vmm/images/linux.rs deleted file mode 100644 index 4efe56a2d..000000000 --- a/os/axvisor/src/vmm/images/linux.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// Architecture variants detected from the image header. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ImageArch { - Riscv { - is_be: bool, - }, - Arm64 { - is_be: bool, - page_size: PageSize, - phys_placement_48bit: bool, - }, -} - -#[allow(unused)] -#[derive(Debug, Clone)] -pub struct Header { - pub text_offset: u64, - pub image_size: u64, - pub arch: ImageArch, -} - -#[allow(unused)] -impl Header { - pub fn parse(image: &[u8]) -> Option { - if let Some(hdr) = ARM64Header::parse(image) { - return Some(Self { - text_offset: hdr.text_offset, - image_size: hdr.image_size, - arch: ImageArch::Arm64 { - is_be: hdr.kernel_is_be(), - page_size: hdr.page_size(), - phys_placement_48bit: hdr.phys_placement_48bit(), - }, - }); - } - - if let Some(hdr) = RiscvHeader::parse(image) { - return Some(Self { - text_offset: hdr.text_offset, - image_size: hdr.image_size, - arch: ImageArch::Riscv { - is_be: hdr.kernel_is_be(), - }, - }); - } - - None - } - - pub fn hdr_size() -> usize { - size_of::() - } -} - -#[allow(unused)] -#[repr(C)] -struct ARM64Header { - code0: u32, - code1: u32, - text_offset: u64, - image_size: u64, - flags: u64, - res2: u64, - res3: u64, - res4: u64, - magic: u32, - res5: u32, -} - -impl ARM64Header { - const MAGIC: u32 = 0x644d5241; // 'ARMd' in little-endian - - fn parse(buffer: &[u8]) -> Option { - if buffer.len() < core::mem::size_of::() { - return None; - } - let hdr: Self = unsafe { core::ptr::read_unaligned(buffer.as_ptr() as *const _) }; - if hdr.magic != Self::MAGIC { - return None; - } - Some(hdr) - } - - /// Return whether the kernel image is big-endian according to flags bit 0. - fn kernel_is_be(&self) -> bool { - (self.flags & 0x1) != 0 - } - - /// Return page size encoded in flags bits 1-2. - fn page_size(&self) -> PageSize { - match (self.flags >> 1) & 0x3 { - 0 => PageSize::Unspecified, - 1 => PageSize::Size4K, - 2 => PageSize::Size16K, - 3 => PageSize::Size64K, - _ => PageSize::Unspecified, - } - } - - /// Return physical placement mode (bit 3): false=default, true=48-bit constrained. - fn phys_placement_48bit(&self) -> bool { - ((self.flags >> 3) & 0x1) != 0 - } -} - -/// Page size encoded in the image header flags (bits 1-2). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PageSize { - Unspecified, - Size4K, - Size16K, - Size64K, -} - -#[allow(unused)] -struct RiscvHeader { - code0: u32, - code1: u32, - text_offset: u64, - image_size: u64, - flags: u64, - version: u32, - res1: u32, - res2: u64, - magic: u64, - magic2: u32, - res4: u32, -} - -impl RiscvHeader { - const MAGIC: u64 = 0x5643534952; // 'RISCV\0\0\0' in little-endian - const MAGIC2: u32 = 0x56534905; // secondary magic - - fn parse(buffer: &[u8]) -> Option { - if buffer.len() < core::mem::size_of::() { - return None; - } - let hdr: Self = unsafe { core::ptr::read_unaligned(buffer.as_ptr() as *const _) }; - if hdr.magic != Self::MAGIC || hdr.magic2 != Self::MAGIC2 { - return None; - } - Some(hdr) - } - - fn kernel_is_be(&self) -> bool { - (self.flags & 0x1) != 0 - } -} diff --git a/os/axvisor/src/vmm/images/mod.rs b/os/axvisor/src/vmm/images/mod.rs deleted file mode 100644 index 82610474a..000000000 --- a/os/axvisor/src/vmm/images/mod.rs +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use axaddrspace::GuestPhysAddr; -use axerrno::AxResult; - -use axvm::VMMemoryRegion; -use axvm::config::AxVMCrateConfig; -use byte_unit::Byte; - -use crate::hal::CacheOp; -use crate::vmm::VMRef; -use crate::vmm::config::{config, get_vm_dtb_arc}; - -mod linux; - -pub fn get_image_header(config: &AxVMCrateConfig) -> Option { - match config.kernel.image_location.as_deref() { - Some("memory") => with_memory_image(config, linux::Header::parse), - #[cfg(feature = "fs")] - Some("fs") => { - let read_size = linux::Header::hdr_size(); - let data = fs::kernal_read(config, read_size).ok()?; - linux::Header::parse(&data) - } - _ => unimplemented!( - "Check your \"image_location\" in config.toml, \"memory\" and \"fs\" are supported,\n NOTE: \"fs\" feature should be enabled if you want to load images from filesystem. (APP_FEATURES=fs)" - ), - } -} - -fn with_memory_image(config: &AxVMCrateConfig, func: F) -> R -where - F: FnOnce(&[u8]) -> R, -{ - let vm_imags = config::get_memory_images() - .iter() - .find(|&v| v.id == config.base.id) - .expect("VM images is missed, Perhaps add `VM_CONFIGS=PATH/CONFIGS/FILE` command."); - - func(vm_imags.kernel) -} - -pub struct ImageLoader { - main_memory: VMMemoryRegion, - vm: VMRef, - config: AxVMCrateConfig, - kernel_load_gpa: GuestPhysAddr, - bios_load_gpa: Option, - dtb_load_gpa: Option, - ramdisk_load_gpa: Option, -} - -impl ImageLoader { - pub fn new(main_memory: VMMemoryRegion, config: AxVMCrateConfig, vm: VMRef) -> Self { - Self { - main_memory, - vm, - config, - kernel_load_gpa: GuestPhysAddr::default(), - bios_load_gpa: None, - dtb_load_gpa: None, - ramdisk_load_gpa: None, - } - } - - pub fn load(&mut self) -> AxResult { - info!( - "Loading VM[{}] images into memory region: gpa={:#x}, hva={:#x}, size={:#}", - self.vm.id(), - self.main_memory.gpa, - self.main_memory.hva, - Byte::from(self.main_memory.size()) - ); - - self.vm.with_config(|config| { - self.kernel_load_gpa = config.image_config.kernel_load_gpa; - self.dtb_load_gpa = config.image_config.dtb_load_gpa; - self.bios_load_gpa = config.image_config.bios_load_gpa; - self.ramdisk_load_gpa = config.image_config.ramdisk_load_gpa; - }); - - match self.config.kernel.image_location.as_deref() { - Some("memory") => self.load_vm_images_from_memory(), - #[cfg(feature = "fs")] - Some("fs") => fs::load_vm_images_from_filesystem(self), - _ => unimplemented!( - "Check your \"image_location\" in config.toml, \"memory\" and \"fs\" are supported,\n NOTE: \"fs\" feature should be enabled if you want to load images from filesystem. (APP_FEATURES=fs)" - ), - } - } - - /// Load VM images from memory - /// into the guest VM's memory space based on the VM configuration. - fn load_vm_images_from_memory(&self) -> AxResult { - info!("Loading VM[{}] images from memory", self.config.base.id); - - let vm_imags = config::get_memory_images() - .iter() - .find(|&v| v.id == self.config.base.id) - .expect("VM images is missed, Perhaps add `VM_CONFIGS=PATH/CONFIGS/FILE` command."); - - load_vm_image_from_memory(vm_imags.kernel, self.kernel_load_gpa, self.vm.clone()) - .expect("Failed to load VM images"); - // Load DTB image - let vm_config = axvm::config::AxVMConfig::from(self.config.clone()); - - if let Some(dtb_arc) = get_vm_dtb_arc(&vm_config) { - let _dtb_slice: &[u8] = &dtb_arc; - #[cfg(target_arch = "aarch64")] - crate::vmm::fdt::update_fdt( - core::ptr::NonNull::new(_dtb_slice.as_ptr() as *mut u8).unwrap(), - _dtb_slice.len(), - self.vm.clone(), - ); - } else { - if let Some(buffer) = vm_imags.dtb { - #[cfg(target_arch = "riscv64")] - load_vm_image_from_memory(buffer, self.dtb_load_gpa.unwrap(), self.vm.clone()) - .expect("Failed to load DTB images"); - } else { - info!("dtb_load_gpa not provided"); - } - } - - // Load BIOS image - if let Some(buffer) = vm_imags.bios { - load_vm_image_from_memory(buffer, self.bios_load_gpa.unwrap(), self.vm.clone()) - .expect("Failed to load BIOS images"); - } - - // Load Ramdisk image - if let Some(buffer) = vm_imags.ramdisk { - load_vm_image_from_memory(buffer, self.ramdisk_load_gpa.unwrap(), self.vm.clone()) - .expect("Failed to load Ramdisk images"); - }; - - Ok(()) - } -} - -pub fn load_vm_image_from_memory( - image_buffer: &[u8], - load_addr: GuestPhysAddr, - vm: VMRef, -) -> AxResult { - let mut buffer_pos = 0; - - let image_size = image_buffer.len(); - - debug!( - "loading VM image from memory {:?} {}", - load_addr, - image_buffer.len() - ); - - let image_load_regions = vm.get_image_load_region(load_addr, image_size)?; - - for region in image_load_regions { - let region_len = region.len(); - let bytes_to_write = region_len.min(image_size - buffer_pos); - - // copy data from memory - unsafe { - core::ptr::copy_nonoverlapping( - image_buffer[buffer_pos..].as_ptr(), - region.as_mut_ptr().cast(), - bytes_to_write, - ); - } - - crate::hal::arch::cache::dcache_range( - CacheOp::Clean, - (region.as_ptr() as usize).into(), - region_len, - ); - - // Update the position of the buffer. - buffer_pos += bytes_to_write; - - // If the buffer is fully written, exit the loop. - if buffer_pos >= image_size { - debug!("copy size: {bytes_to_write}"); - break; - } - } - - Ok(()) -} - -#[cfg(feature = "fs")] -pub mod fs { - use super::*; - use crate::hal::CacheOp; - use axerrno::{AxResult, ax_err, ax_err_type}; - use std::{fs::File, vec::Vec}; - - pub fn kernal_read(config: &AxVMCrateConfig, read_size: usize) -> AxResult> { - use std::fs::File; - use std::io::Read; - let file_name = &config.kernel.kernel_path; - - let mut file = File::open(file_name).map_err(|err| { - ax_err_type!( - NotFound, - format!( - "Failed to open {}, err {:?}, please check your disk.img", - file_name, err - ) - ) - })?; - - let mut buffer = vec![0u8; read_size]; - - file.read_exact(&mut buffer).map_err(|err| { - ax_err_type!( - NotFound, - format!( - "Failed to read {}, err {:?}, please check your disk.img", - file_name, err - ) - ) - })?; - - Ok(buffer) - } - - /// Loads the VM image files from the filesystem - /// into the guest VM's memory space based on the VM configuration. - pub(crate) fn load_vm_images_from_filesystem(loader: &ImageLoader) -> AxResult { - info!("Loading VM images from filesystem"); - // Load kernel image. - load_vm_image( - &loader.config.kernel.kernel_path, - loader.kernel_load_gpa, - loader.vm.clone(), - )?; - // Load BIOS image if needed. - if let Some(bios_path) = &loader.config.kernel.bios_path { - if let Some(bios_load_addr) = loader.bios_load_gpa { - load_vm_image(bios_path, bios_load_addr, loader.vm.clone())?; - } else { - return ax_err!(NotFound, "BIOS load addr is missed"); - } - }; - // Load Ramdisk image if needed. - if let Some(ramdisk_path) = &loader.config.kernel.ramdisk_path { - if let Some(ramdisk_load_addr) = loader.ramdisk_load_gpa { - load_vm_image(ramdisk_path, ramdisk_load_addr, loader.vm.clone())?; - } else { - return ax_err!(NotFound, "Ramdisk load addr is missed"); - } - }; - // Load DTB image if needed. - let vm_config = axvm::config::AxVMConfig::from(loader.config.clone()); - if let Some(dtb_arc) = get_vm_dtb_arc(&vm_config) { - let _dtb_slice: &[u8] = &dtb_arc; - #[cfg(target_arch = "aarch64")] - crate::vmm::fdt::update_fdt( - core::ptr::NonNull::new(_dtb_slice.as_ptr() as *mut u8).unwrap(), - _dtb_slice.len(), - loader.vm.clone(), - ); - } - - Ok(()) - } - - fn load_vm_image(image_path: &str, image_load_gpa: GuestPhysAddr, vm: VMRef) -> AxResult { - use std::io::{BufReader, Read}; - let (image_file, image_size) = open_image_file(image_path)?; - - let image_load_regions = vm.get_image_load_region(image_load_gpa, image_size)?; - let mut file = BufReader::new(image_file); - - for buffer in image_load_regions { - file.read_exact(buffer).map_err(|err| { - ax_err_type!( - Io, - format!("Failed in reading from file {}, err {:?}", image_path, err) - ) - })?; - - crate::hal::arch::cache::dcache_range( - CacheOp::Clean, - (buffer.as_ptr() as usize).into(), - buffer.len(), - ); - } - - Ok(()) - } - - pub fn open_image_file(file_name: &str) -> AxResult<(File, usize)> { - let file = File::open(file_name).map_err(|err| { - ax_err_type!( - NotFound, - format!( - "Failed to open {}, err {:?}, please check your disk.img", - file_name, err - ) - ) - })?; - let file_size = file - .metadata() - .map_err(|err| { - ax_err_type!( - Io, - format!( - "Failed to get metadate of file {}, err {:?}", - file_name, err - ) - ) - })? - .size() as usize; - Ok((file, file_size)) - } -} diff --git a/os/axvisor/src/vmm/ivc.rs b/os/axvisor/src/vmm/ivc.rs deleted file mode 100644 index 77aeefac3..000000000 --- a/os/axvisor/src/vmm/ivc.rs +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Inter-VM communication (IVC) module. -use alloc::collections::BTreeMap; -use alloc::vec::Vec; - -use std::os::arceos::modules::axhal::paging::PagingHandlerImpl; -use std::sync::Mutex; - -use axaddrspace::{GuestPhysAddr, HostPhysAddr}; -use axerrno::AxResult; -use page_table_multiarch::PagingHandler; - -/// A global btree map to store IVC channels, -/// indexed by (publisher_vm_id, channel_key). -static IVC_CHANNELS: Mutex>> = - Mutex::new(BTreeMap::new()); - -pub fn insert_channel( - publisher_vm_id: usize, - channel: IVCChannel, -) -> AxResult<()> { - let mut channels = IVC_CHANNELS.lock(); - if channels - .insert((publisher_vm_id, channel.key), channel) - .is_some() - { - Err(axerrno::ax_err_type!( - AlreadyExists, - "IVC channel already exists" - )) - } else { - Ok(()) - } -} - -/// Try to remove a channel according to the publisher VM ID and key. -/// If the channel still has subscribers, it will just mark it as unpublished -/// (by setting its base GPA to None). -/// If the channel is successfully unpublished, it will return the base GPA and size of the channel. -/// If the channel does not exist, it will return an error. -pub fn unpublish_channel( - publisher_vm_id: usize, - key: usize, -) -> AxResult> { - let mut channels = IVC_CHANNELS.lock(); - if let Some(mut channel) = channels.remove(&(publisher_vm_id, key)) { - let base_gpa = channel.base_gpa_in_publisher().ok_or_else(|| { - axerrno::ax_err_type!( - NotFound, - format!( - "IVC channel for publisher VM {} with key {} has no base GPA, it may have been marked as unpublished", - publisher_vm_id, key - ) - ) - })?; - let size = channel.size(); - if !channel.subscribers().is_empty() { - channel.base_gpa = None; // Mark the channel as removed. - // If there are still subscribers, just return None. - channels.insert((publisher_vm_id, key), channel); - } - Ok(Some((base_gpa, size))) - } else { - Err(axerrno::ax_err_type!( - NotFound, - format!( - "IVC channel for publisher VM {} with key {} not found", - publisher_vm_id, key - ) - )) - } -} - -pub fn get_channel_size(publisher_vm_id: usize, key: usize) -> AxResult { - let channels = IVC_CHANNELS.lock(); - if let Some(channel) = channels.get(&(publisher_vm_id, key)) { - Ok(channel.size()) - } else { - Err(axerrno::ax_err_type!( - NotFound, - format!( - "IVC channel for publisher VM {} with key {} not found", - publisher_vm_id, key - ) - )) - } -} - -/// Subcribe to a channel of a publisher VM with the given key, -/// return the shared region base address and size. -pub fn subscribe_to_channel_of_publisher( - publisher_vm_id: usize, - key: usize, - subscriber_vm_id: usize, - subscriber_gpa: GuestPhysAddr, -) -> AxResult<(HostPhysAddr, usize)> { - let mut channels = IVC_CHANNELS.lock(); - if let Some(channel) = channels.get_mut(&(publisher_vm_id, key)) { - // Add the subscriber VM ID to the channel. - channel.add_subscriber(subscriber_vm_id, subscriber_gpa); - Ok((channel.base_hpa(), channel.size())) - } else { - Err(axerrno::ax_err_type!( - NotFound, - format!( - "IVC channel for publisher VM [{}] key {:#x} not found", - publisher_vm_id, key - ) - )) - } -} - -/// Unsubscribe from a channel of a publisher VM with the given key, -/// if the channel has been unpublished (i.e., the base GPA is None) and has no subscribers, -/// it will remove the channel from the global map. -pub fn unsubscribe_from_channel_of_publisher( - publisher_vm_id: usize, - key: usize, - subscriber_vm_id: usize, -) -> AxResult<(GuestPhysAddr, usize)> { - let mut channels = IVC_CHANNELS.lock(); - let (base_gpa, size) = if let Some(channel) = channels.get_mut(&(publisher_vm_id, key)) { - // Remove the subscriber VM ID from the channel. - if let Some(subscriber_gpa) = channel.remove_subscriber(subscriber_vm_id) { - Ok((subscriber_gpa, channel.size())) - } else { - Err(axerrno::ax_err_type!( - NotFound, - format!( - "VM[{}] tries to unsubscribe non-existed channel publisher VM[{}] Key {:#x}", - subscriber_vm_id, publisher_vm_id, key - ) - )) - } - } else { - Err(axerrno::ax_err_type!( - NotFound, - format!("IVC channel for publisher VM {} not found", publisher_vm_id) - )) - }?; - - // If the channel has no subscribers and has been unpublished (base GPA is None), - // remove it from the global map. - if channels - .get(&(publisher_vm_id, key)) - .is_some_and(|c| c.subscribers().is_empty() && c.base_gpa.is_none()) - { - channels.remove(&(publisher_vm_id, key)); - } - - Ok((base_gpa, size)) -} - -pub struct IVCChannel { - publisher_vm_id: usize, - key: usize, - /// A list of subscriber VM IDs that are subscribed to this channel. - /// The key is the subscriber VM ID, and the value is the base address of the shared region in - /// guest physical address of the subscriber VM. - subscriber_vms: BTreeMap, - shared_region_base: HostPhysAddr, - shared_region_size: usize, - /// The base address of the shared memory region in guest physical address of the publisher VM. - /// `None` if the channel has been unpublished (but still has subscribers). - base_gpa: Option, - _phatom: core::marker::PhantomData, -} - -#[repr(C)] -pub struct IVCChannelHeader { - pub publisher_id: u64, - pub key: u64, -} - -impl IVCChannel { - #[allow(unused)] - pub fn header(&self) -> &IVCChannelHeader { - unsafe { - // Map the shared region base to the header structure. - &*H::phys_to_virt(self.shared_region_base).as_mut_ptr_of::() - } - } - - pub fn header_mut(&mut self) -> &mut IVCChannelHeader { - unsafe { - // Map the shared region base to the mutable header structure. - &mut *H::phys_to_virt(self.shared_region_base).as_mut_ptr_of::() - } - } - - #[allow(unused)] - pub fn data_region(&self) -> *const u8 { - unsafe { - // Return a pointer to the data region, which starts after the header. - H::phys_to_virt(self.shared_region_base) - .as_mut_ptr() - .add(core::mem::size_of::()) - } - } -} - -impl core::fmt::Debug for IVCChannel { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "IVCChannel(publisher[{}], subscribers {:?}, base: {:?}, size: {:#x}, gpa: {:?})", - self.publisher_vm_id, - self.subscriber_vms, - self.shared_region_base, - self.shared_region_size, - self.base_gpa - ) - } -} - -impl Drop for IVCChannel { - fn drop(&mut self) { - // Free the shared region frame when the channel is dropped. - debug!( - "Dropping IVCChannel for VM[{}], shared region base: {:?}", - self.publisher_vm_id, self.shared_region_base - ); - H::dealloc_frame(self.shared_region_base); - } -} - -impl IVCChannel { - pub fn alloc( - publisher_vm_id: usize, - key: usize, - shared_region_size: usize, - base_gpa: GuestPhysAddr, - ) -> AxResult { - // TODO: support larger shared region sizes with alloc_frames API. - let shared_region_size = shared_region_size.min(4096); - let shared_region_base = H::alloc_frame().ok_or_else(|| { - axerrno::ax_err_type!(NoMemory, "Failed to allocate shared region frame") - })?; - - let mut channel = IVCChannel { - publisher_vm_id, - key, - subscriber_vms: BTreeMap::new(), - shared_region_base, - shared_region_size, - base_gpa: Some(base_gpa), - _phatom: core::marker::PhantomData, - }; - - channel.header_mut().publisher_id = publisher_vm_id as u64; - channel.header_mut().key = key as u64; - - debug!("Allocated IVCChannel: {channel:?}"); - - Ok(channel) - } - - pub fn base_hpa(&self) -> HostPhysAddr { - self.shared_region_base - } - - pub fn base_gpa_in_publisher(&self) -> Option { - self.base_gpa - } - - pub fn size(&self) -> usize { - self.shared_region_size - } - - pub fn add_subscriber(&mut self, subscriber_vm_id: usize, subscriber_gpa: GuestPhysAddr) { - self.subscriber_vms - .entry(subscriber_vm_id) - .or_insert(subscriber_gpa); - } - - pub fn remove_subscriber(&mut self, subscriber_vm_id: usize) -> Option { - self.subscriber_vms.remove(&subscriber_vm_id) - } - - pub fn subscribers(&self) -> Vec<(usize, GuestPhysAddr)> { - self.subscriber_vms - .iter() - .map(|(vm_id, gpa)| (*vm_id, *gpa)) - .collect() - } -} diff --git a/os/axvisor/src/vmm/mod.rs b/os/axvisor/src/vmm/mod.rs deleted file mode 100644 index e9e64737e..000000000 --- a/os/axvisor/src/vmm/mod.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod hvc; -mod ivc; - -pub mod config; -pub mod images; -pub mod timer; -pub mod vcpus; -pub mod vm_list; - -#[cfg(target_arch = "aarch64")] -pub mod fdt; - -use core::sync::atomic::{AtomicUsize, Ordering}; -use std::os::arceos::{ - api::task::{self, AxWaitQueueHandle}, - modules::axtask, -}; - -use axerrno::{AxResult, ax_err_type}; - -use crate::task::AsVCpuTask; -pub use timer::init_percpu as init_timer_percpu; - -/// The instantiated VM type. -pub type VM = axvm::AxVM; -/// The instantiated VM ref type (by `Arc`). -pub type VMRef = axvm::AxVMRef; -/// The instantiated VCpu ref type (by `Arc`). -pub type VCpuRef = axvm::AxVCpuRef; - -static VMM: AxWaitQueueHandle = AxWaitQueueHandle::new(); - -/// The number of running VMs. This is used to determine when to exit the VMM. -static RUNNING_VM_COUNT: AtomicUsize = AtomicUsize::new(0); - -/// Initialize the VMM. -/// -/// This function creates the VM structures and sets up the primary VCpu for each VM. -pub fn init() { - info!("Initializing VMM..."); - // Initialize guest VM according to config file. - config::init_guest_vms(); - - // Setup vcpus, spawn axtask for primary VCpu. - info!("Setting up vcpus..."); - for vm in vm_list::get_vm_list() { - vcpus::setup_vm_primary_vcpu(vm); - } -} - -/// Start the VMM. -pub fn start() { - info!("VMM starting, booting VMs..."); - for vm in vm_list::get_vm_list() { - match vm.boot() { - Ok(_) => { - vcpus::notify_primary_vcpu(vm.id()); - RUNNING_VM_COUNT.fetch_add(1, Ordering::Release); - info!("VM[{}] boot success", vm.id()) - } - Err(err) => warn!("VM[{}] boot failed, error {:?}", vm.id(), err), - } - } - - // Do not exit until all VMs are stopped. - task::ax_wait_queue_wait_until( - &VMM, - || { - let vm_count = RUNNING_VM_COUNT.load(Ordering::Acquire); - info!("a VM exited, current running VM count: {vm_count}"); - vm_count == 0 - }, - None, - ); -} - -#[allow(unused_imports)] -pub use vcpus::with_vcpu_task; - -/// Run a closure with the specified VM. -pub fn with_vm(vm_id: usize, f: impl FnOnce(VMRef) -> T) -> Option { - let vm = vm_list::get_vm_by_id(vm_id)?; - Some(f(vm)) -} - -/// Run a closure with the specified VM and vCPU. -pub fn with_vm_and_vcpu( - vm_id: usize, - vcpu_id: usize, - f: impl FnOnce(VMRef, VCpuRef) -> T, -) -> Option { - let vm = vm_list::get_vm_by_id(vm_id)?; - let vcpu = vm.vcpu(vcpu_id)?; - - Some(f(vm, vcpu)) -} - -/// Run a closure with the specified VM and vCPU, with the guarantee that the closure will be -/// executed on the physical CPU where the vCPU is running, waiting, or queueing. -/// -/// TODO: It seems necessary to disable scheduling when running the closure. -pub fn with_vm_and_vcpu_on_pcpu( - vm_id: usize, - vcpu_id: usize, - f: impl FnOnce(VMRef, VCpuRef) + 'static, -) -> AxResult { - // Disables preemption and IRQs to prevent the current task from being preempted or re-scheduled. - let guard = kernel_guard::NoPreemptIrqSave::new(); - - let current_vm = axtask::current().as_vcpu_task().vm().id(); - let current_vcpu = axtask::current().as_vcpu_task().vcpu.id(); - - // The target vCPU is the current task, execute the closure directly. - if current_vm == vm_id && current_vcpu == vcpu_id { - with_vm_and_vcpu(vm_id, vcpu_id, f).unwrap(); // unwrap is safe here - return Ok(()); - } - - // The target vCPU is not the current task, send an IPI to the target physical CPU. - drop(guard); - - let _pcpu_id = vcpus::with_vcpu_task(vm_id, vcpu_id, |task| task.cpu_id()) - .ok_or_else(|| ax_err_type!(NotFound))?; - - unimplemented!(); - // use std::os::arceos::modules::axipi; - // Ok(axipi::send_ipi_event_to_one(pcpu_id as usize, move || { - // with_vm_and_vcpu_on_pcpu(vm_id, vcpu_id, f); - // })) -} - -pub fn add_running_vm_count(count: usize) { - RUNNING_VM_COUNT.fetch_add(count, Ordering::Release); -} - -pub fn sub_running_vm_count(count: usize) { - RUNNING_VM_COUNT.fetch_sub(count, Ordering::Release); -} diff --git a/os/axvisor/src/vmm/timer.rs b/os/axvisor/src/vmm/timer.rs deleted file mode 100644 index e2e7242b1..000000000 --- a/os/axvisor/src/vmm/timer.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use core::sync::atomic::AtomicUsize; -use core::sync::atomic::Ordering; - -use std::os::arceos::modules::axhal; - -use alloc::boxed::Box; -use kspin::SpinNoIrq; -use lazyinit::LazyInit; -use timer_list::{TimeValue, TimerEvent, TimerList}; - -static TOKEN: AtomicUsize = AtomicUsize::new(0); -// const PERIODIC_INTERVAL_NANOS: u64 = axhal::time::NANOS_PER_SEC / axconfig::TICKS_PER_SEC as u64; - -/// Represents a timer event in the virtual machine monitor (VMM). -/// -/// This struct holds a unique token for the timer and a callback function -/// that will be executed when the timer expires. -pub struct VmmTimerEvent { - // Unique identifier for the timer event - token: usize, - // Callback function to be executed when the timer expires - timer_callback: Box, -} - -impl VmmTimerEvent { - fn new(token: usize, f: F) -> Self - where - F: FnOnce(TimeValue) + Send + 'static, - { - Self { - token, - timer_callback: Box::new(f), - } - } -} - -impl TimerEvent for VmmTimerEvent { - fn callback(self, now: TimeValue) { - (self.timer_callback)(now) - } -} - -#[percpu::def_percpu] -static TIMER_LIST: LazyInit>> = LazyInit::new(); - -/// Registers a new timer that will execute at the specified deadline -/// -/// # Arguments -/// - `deadline`: The absolute time in nanoseconds when the timer should trigger -/// - `handler`: The callback function to execute when the timer expires -/// -/// # Returns -/// A unique token that can be used to cancel this timer later -pub fn register_timer(deadline: u64, handler: F) -> usize -where - F: FnOnce(TimeValue) + Send + 'static, -{ - trace!("Registering timer..."); - trace!( - "deadline is {:#?} = {:#?}", - deadline, - TimeValue::from_nanos(deadline) - ); - let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; - let mut timers = timer_list.lock(); - let token = TOKEN.fetch_add(1, Ordering::Release); - let event = VmmTimerEvent::new(token, handler); - timers.set(TimeValue::from_nanos(deadline), event); - token -} - -/// Cancels a timer with the specified token. -/// -/// # Parameters -/// - `token`: The unique token of the timer to cancel. -pub fn cancel_timer(token: usize) { - let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; - let mut timers = timer_list.lock(); - timers.cancel(|event| event.token == token); -} - -/// Check and process any pending timer events -pub fn check_events() { - // info!("Checking timer events..."); - // info!("now is {:#?}", axhal::time::wall_time()); - let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; - loop { - let now = axhal::time::wall_time(); - let event = timer_list.lock().expire_one(now); - if let Some((_deadline, event)) = event { - trace!("pick one {_deadline:#?} to handle!!!"); - event.callback(now); - } else { - break; - } - } -} - -// /// Schedule the next timer event based on the periodic interval -// pub fn scheduler_next_event() { -// trace!("Scheduling next event..."); -// let now_ns = axhal::time::monotonic_time_nanos(); -// let deadline = now_ns + PERIODIC_INTERVAL_NANOS; -// debug!("PHY deadline {} !!!", deadline); -// axhal::time::set_oneshot_timer(deadline); -// } - -/// Initialize the hypervisor timer system -pub fn init_percpu() { - info!("Initing HV Timer..."); - let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() }; - timer_list.init_once(SpinNoIrq::new(TimerList::new())); -} diff --git a/os/axvisor/src/vmm/vcpus.rs b/os/axvisor/src/vmm/vcpus.rs deleted file mode 100644 index 5a26355d6..000000000 --- a/os/axvisor/src/vmm/vcpus.rs +++ /dev/null @@ -1,603 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::{collections::BTreeMap, vec::Vec}; -use cpumask::CpuMask; - -use core::{ - cell::UnsafeCell, - sync::atomic::{AtomicUsize, Ordering}, - time::Duration, -}; -use std::os::arceos::{ - api::task::{AxCpuMask, ax_wait_queue_wake}, - modules::{ - axhal::{self, time::busy_wait}, - axtask::{self, AxTaskExt}, - }, -}; - -use axaddrspace::GuestPhysAddr; -use axtask::{AxTaskRef, TaskInner, WaitQueue}; -use axvcpu::{AxVCpuExitReason, VCpuState}; - -use crate::{hal::arch::inject_interrupt, task::VCpuTask}; -use crate::{ - task::AsVCpuTask, - vmm::{VCpuRef, VMRef, sub_running_vm_count}, -}; - -const KERNEL_STACK_SIZE: usize = 0x40000; // 256 KiB - -/// A global static BTreeMap that holds the wait queues for VCpus -/// associated with their respective VMs, identified by their VM IDs. -/// -/// TODO: find a better data structure to replace the `static mut`, something like a conditional -/// variable. -static VM_VCPU_TASK_WAIT_QUEUE: Queue = Queue::new(); - -/// A thread-safe queue that manages wait queues for VCpus across multiple VMs. -/// -/// This structure wraps a BTreeMap that maps VM IDs to their corresponding VMVCpus structures. -/// It provides thread-safe access to the mapping through interior mutability using UnsafeCell. -/// Each VM is identified by its unique ID, and the queue manages the VCpu tasks and wait -/// operations for all VMs in the system. -struct Queue(UnsafeCell>); - -unsafe impl Sync for Queue {} -unsafe impl Send for Queue {} - -impl Queue { - /// Creates a new empty Queue. - /// - /// # Returns - /// - /// A new Queue instance with an empty BTreeMap. - const fn new() -> Self { - Self(UnsafeCell::new(BTreeMap::new())) - } - - /// Retrieves a reference to the VMVCpus for the specified VM ID. - fn get(&self, vm_id: &usize) -> Option<&VMVCpus> { - unsafe { (*self.0.get()).get(vm_id) } - } - - /// Retrieves a mutable reference to the VMVCpus for the specified VM ID. - #[allow(clippy::mut_from_ref)] - fn get_mut(&self, vm_id: &usize) -> Option<&mut VMVCpus> { - unsafe { (*self.0.get()).get_mut(vm_id) } - } - - /// Inserts a new VMVCpus entry for the specified VM ID. - fn insert(&self, vm_id: usize, vcpus: VMVCpus) { - unsafe { - (*self.0.get()).insert(vm_id, vcpus); - } - } - - /// Removes the VMVCpus entry for the specified VM ID. - fn remove(&self, vm_id: &usize) -> Option { - unsafe { (*self.0.get()).remove(vm_id) } - } -} - -/// A structure representing the VCpus of a specific VM, including a wait queue -/// and a list of tasks associated with the VCpus. -pub struct VMVCpus { - // The ID of the VM to which these VCpus belong. - _vm_id: usize, - // A wait queue to manage task scheduling for the VCpus. - wait_queue: WaitQueue, - // A list of tasks associated with the VCpus of this VM. - vcpu_task_list: Vec, - /// The number of currently running or halting VCpus. Used to track when the VM is fully - /// shutdown. - /// - /// This number is incremented when a VCpu starts running and decremented when it exits because - /// of the VM being shutdown. - running_halting_vcpu_count: AtomicUsize, -} - -impl VMVCpus { - /// Creates a new `VMVCpus` instance for the given VM. - /// - /// # Arguments - /// - /// * `vm` - A reference to the VM for which the VCpus are being created. - /// - /// # Returns - /// - /// A new `VMVCpus` instance with an empty task list and a fresh wait queue. - fn new(vm: VMRef) -> Self { - Self { - _vm_id: vm.id(), - wait_queue: WaitQueue::new(), - vcpu_task_list: Vec::with_capacity(vm.vcpu_num()), - running_halting_vcpu_count: AtomicUsize::new(0), - } - } - - /// Adds a VCpu task to the list of VCpu tasks for this VM. - /// - /// # Arguments - /// - /// * `vcpu_task` - A reference to the task associated with a VCpu that is to be added. - fn add_vcpu_task(&mut self, vcpu_task: AxTaskRef) { - // It may be dangerous to go lock-free here, as two VCpus may invoke `CpuUp` at the same - // time. However, in most scenarios, only the bsp will `CpuUp` other VCpus, making this - // operation single-threaded. Therefore, we just tolerate this as for now. - self.vcpu_task_list.push(vcpu_task); - } - - /// Blocks the current thread on the wait queue associated with the VCpus of this VM. - fn wait(&self) { - self.wait_queue.wait() - } - - /// Blocks the current thread on the wait queue associated with the VCpus of this VM - /// until the provided condition is met. - fn wait_until(&self, condition: F) - where - F: Fn() -> bool, - { - self.wait_queue.wait_until(condition) - } - - #[allow(dead_code)] - fn notify_one(&mut self) { - // FIXME: `WaitQueue::len` is removed - // info!("Current wait queue length: {}", self.wait_queue.len()); - self.wait_queue.notify_one(false); - } - - /// Notify all waiting vCPU threads to wake up. - /// This is useful when shutting down a VM to ensure all vCPUs can check the shutdown flag. - fn notify_all(&mut self) { - self.wait_queue.notify_all(false); - } - - /// Increments the count of running or halting VCpus by one. - fn mark_vcpu_running(&self) { - self.running_halting_vcpu_count - .fetch_add(1, Ordering::Relaxed); - // Relaxed is enough here, as we only need to ensure that the count is incremented and - // decremented correctly, and there is no other data synchronization needed. - } - - /// Decrements the count of running or halting VCpus by one. Returns true if this was the last - /// VCpu to exit. - fn mark_vcpu_exiting(&self) -> bool { - self.running_halting_vcpu_count - .fetch_sub(1, Ordering::Relaxed) - == 1 - // Relaxed is enough here, as we only need to ensure that the count is incremented and - // decremented correctly, and there is no other data synchronization needed. - } -} - -/// Blocks the current thread until it is explicitly woken up, using the wait queue -/// associated with the VCpus of the specified VM. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM whose VCpu wait queue is used to block the current thread. -/// -fn wait(vm_id: usize) { - VM_VCPU_TASK_WAIT_QUEUE.get(&vm_id).unwrap().wait() -} - -/// Blocks the current thread until the provided condition is met, using the wait queue -/// associated with the VCpus of the specified VM. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM whose VCpu wait queue is used to block the current thread. -/// * `condition` - A closure that returns a boolean value indicating whether the condition is met. -/// -fn wait_for(vm_id: usize, condition: F) -where - F: Fn() -> bool, -{ - VM_VCPU_TASK_WAIT_QUEUE - .get(&vm_id) - .unwrap() - .wait_until(condition) -} - -/// Notifies the primary VCpu task associated with the specified VM to wake up and resume execution. -/// This function is used to notify the primary VCpu of a VM to start running after the VM has been booted. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM whose VCpus are to be notified. -/// -pub(crate) fn notify_primary_vcpu(vm_id: usize) { - // Generally, the primary VCpu is the first and **only** VCpu in the list. - VM_VCPU_TASK_WAIT_QUEUE - .get_mut(&vm_id) - .unwrap() - .notify_one() -} - -/// Notifies all VCpu tasks associated with the specified VM to wake up. -/// This is useful when shutting down a VM to ensure all waiting vCPUs can check the shutdown flag. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM whose VCpus should be notified. -/// -pub(crate) fn notify_all_vcpus(vm_id: usize) { - if let Some(vm_vcpus) = VM_VCPU_TASK_WAIT_QUEUE.get_mut(&vm_id) { - vm_vcpus.notify_all(); - } -} - -/// Cleans up VCpu resources for a VM that is being deleted. -/// This removes the VM's entry from the global VCpu wait queue. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM whose VCpu resources should be cleaned up. -/// -/// # Note -/// -/// This should be called after all VCpu threads have exited to avoid resource leaks. -/// It will join all VCpu tasks to ensure they are fully cleaned up. -pub(crate) fn cleanup_vm_vcpus(vm_id: usize) { - if let Some(vm_vcpus) = VM_VCPU_TASK_WAIT_QUEUE.remove(&vm_id) { - let task_count = vm_vcpus.vcpu_task_list.len(); - - info!("VM[{}] Joining {} VCpu tasks...", vm_id, task_count); - - // Join all VCpu tasks to ensure they have fully exited and cleaned up - for (idx, task) in vm_vcpus.vcpu_task_list.iter().enumerate() { - debug!( - "VM[{}] Joining VCpu task[{}]: {}", - vm_id, - idx, - task.id_name() - ); - let exit_code = task.join(); - debug!( - "VM[{}] VCpu task[{}] exited with code: {}", - vm_id, idx, exit_code - ); - } - - info!( - "VM[{}] VCpu resources cleaned up, {} VCpu tasks joined successfully", - vm_id, task_count - ); - } else { - warn!("VM[{}] VCpu resources not found in queue", vm_id); - } -} - -/// Marks the VCpu of the specified VM as running. -fn mark_vcpu_running(vm_id: usize) { - VM_VCPU_TASK_WAIT_QUEUE - .get(&vm_id) - .unwrap() - .mark_vcpu_running(); -} - -/// Marks the VCpu of the specified VM as exiting for VM shutdown. Returns true if this was the last -/// VCpu to exit. -fn mark_vcpu_exiting(vm_id: usize) -> bool { - VM_VCPU_TASK_WAIT_QUEUE - .get(&vm_id) - .unwrap() - .mark_vcpu_exiting() -} - -/// Boot target VCpu on the specified VM. -/// This function is used to boot a secondary VCpu on a VM, setting the entry point and argument for the VCpu. -/// -/// # Arguments -/// -/// * `vm_id` - The ID of the VM on which the VCpu is to be booted. -/// * `vcpu_id` - The ID of the VCpu to be booted. -/// * `entry_point` - The entry point of the VCpu. -/// * `arg` - The argument to be passed to the VCpu. -/// -fn vcpu_on(vm: VMRef, vcpu_id: usize, entry_point: GuestPhysAddr, arg: usize) { - let vcpu = vm.vcpu_list()[vcpu_id].clone(); - assert_eq!( - vcpu.state(), - VCpuState::Free, - "vcpu_on: {} invalid vcpu state {:?}", - vcpu.id(), - vcpu.state() - ); - - vcpu.set_entry(entry_point) - .expect("vcpu_on: set_entry failed"); - #[cfg(not(target_arch = "riscv64"))] - vcpu.set_gpr(0, arg); - - #[cfg(target_arch = "riscv64")] - { - info!( - "vcpu_on: vcpu[{}] entry={:x} opaque={:x}", - vcpu_id, entry_point, arg - ); - vcpu.set_gpr(riscv_vcpu::GprIndex::A0 as usize, vcpu_id); - vcpu.set_gpr(riscv_vcpu::GprIndex::A1 as usize, arg); - } - - let vcpu_task = alloc_vcpu_task(&vm, vcpu); - - VM_VCPU_TASK_WAIT_QUEUE - .get_mut(&vm.id()) - .unwrap() - .add_vcpu_task(vcpu_task); -} - -/// Sets up the primary VCpu for the given VM, -/// generally the first VCpu in the VCpu list, -/// and initializing their respective wait queues and task lists. -/// VM's secondary VCpus are not started at this point. -/// -/// # Arguments -/// -/// * `vm` - A reference to the VM for which the VCpus are being set up. -pub fn setup_vm_primary_vcpu(vm: VMRef) { - info!("Initializing VM[{}]'s {} vcpus", vm.id(), vm.vcpu_num()); - let vm_id = vm.id(); - let mut vm_vcpus = VMVCpus::new(vm.clone()); - - let primary_vcpu_id = 0; - - let primary_vcpu = vm.vcpu_list()[primary_vcpu_id].clone(); - let primary_vcpu_task = alloc_vcpu_task(&vm, primary_vcpu); - vm_vcpus.add_vcpu_task(primary_vcpu_task); - - VM_VCPU_TASK_WAIT_QUEUE.insert(vm_id, vm_vcpus); -} - -/// Finds the [`AxTaskRef`] associated with the specified vCPU of the specified VM. -// pub fn find_vcpu_task(vm_id: usize, vcpu_id: usize) -> Option { -// with_vcpu_task(vm_id, vcpu_id, |task| task.clone()) -// } -/// Executes the provided closure with the [`AxTaskRef`] associated with the specified vCPU of the specified VM. -pub fn with_vcpu_task T>( - vm_id: usize, - vcpu_id: usize, - f: F, -) -> Option { - VM_VCPU_TASK_WAIT_QUEUE - .get(&vm_id) - .unwrap() - .vcpu_task_list - .get(vcpu_id) - .map(f) -} - -/// Allocates arceos task for vcpu, set the task's entry function to [`vcpu_run()`], -/// also initializes the CPU mask if the VCpu has a dedicated physical CPU set. -/// -/// # Arguments -/// -/// * `vm` - A reference to the VM for which the VCpu task is being allocated. -/// * `vcpu` - A reference to the VCpu for which the task is being allocated. -/// -/// # Returns -/// -/// A reference to the task that has been allocated for the VCpu. -/// -/// # Note -/// -/// * The task associated with the VCpu is created with a kernel stack size of 256 KiB. -/// * The task is created in blocked state and added to the wait queue directly, -/// instead of being added to the ready queue. It will be woken up by notify_primary_vcpu(). -fn alloc_vcpu_task(vm: &VMRef, vcpu: VCpuRef) -> AxTaskRef { - info!("Spawning task for VM[{}] VCpu[{}]", vm.id(), vcpu.id()); - let mut vcpu_task = TaskInner::new( - vcpu_run, - format!("VM[{}]-VCpu[{}]", vm.id(), vcpu.id()), - KERNEL_STACK_SIZE, - ); - - if let Some(phys_cpu_set) = vcpu.phys_cpu_set() { - vcpu_task.set_cpumask(AxCpuMask::from_raw_bits(phys_cpu_set)); - } - - // Use Weak reference in TaskExt to avoid keeping VM alive - let inner = VCpuTask::new(vm, vcpu); - *vcpu_task.task_ext_mut() = Some(AxTaskExt::from_impl(inner)); - - info!( - "VCpu task {} created {:?}", - vcpu_task.id_name(), - vcpu_task.cpumask() - ); - axtask::spawn_task(vcpu_task) -} - -/// The main routine for VCpu task. -/// This function is the entry point for the VCpu tasks, which are spawned for each VCpu of a VM. -/// -/// When the VCpu first starts running, it waits for the VM to be in the running state. -/// It then enters a loop where it runs the VCpu and handles the various exit reasons. -fn vcpu_run() { - let curr = axtask::current(); - - let vm = curr.as_vcpu_task().vm(); - let vcpu = curr.as_vcpu_task().vcpu.clone(); - let vm_id = vm.id(); - let vcpu_id = vcpu.id(); - - // boot delay - let boot_delay_sec = (vm_id - 1) * 5; - info!("VM[{vm_id}] boot delay: {boot_delay_sec}s"); - busy_wait(Duration::from_secs(boot_delay_sec as _)); - - info!("VM[{}] VCpu[{}] waiting for running", vm.id(), vcpu.id()); - wait_for(vm_id, || vm.running()); - - info!("VM[{}] VCpu[{}] running...", vm.id(), vcpu.id()); - mark_vcpu_running(vm_id); - - loop { - match vm.run_vcpu(vcpu_id) { - Ok(exit_reason) => match exit_reason { - AxVCpuExitReason::Hypercall { nr, args } => { - debug!("Hypercall [{nr}] args {args:x?}"); - use crate::vmm::hvc::HyperCall; - - match HyperCall::new(vcpu.clone(), vm.clone(), nr, args) { - Ok(hypercall) => { - let ret_val = match hypercall.execute() { - Ok(ret_val) => ret_val as isize, - Err(err) => { - warn!("Hypercall [{nr:#x}] failed: {err:?}"); - -1 - } - }; - vcpu.set_return_value(ret_val as usize); - } - Err(err) => { - warn!("Hypercall [{nr:#x}] failed: {err:?}"); - } - } - } - AxVCpuExitReason::FailEntry { - hardware_entry_failure_reason, - } => { - warn!( - "VM[{vm_id}] VCpu[{vcpu_id}] run failed with exit code {hardware_entry_failure_reason}" - ); - } - AxVCpuExitReason::ExternalInterrupt { vector } => { - debug!("VM[{vm_id}] run VCpu[{vcpu_id}] get irq {vector}"); - - // TODO: maybe move this irq dispatcher to lower layer to accelerate the interrupt handling - axhal::irq::irq_handler(vector as usize); - super::timer::check_events(); - } - AxVCpuExitReason::Halt => { - debug!("VM[{vm_id}] run VCpu[{vcpu_id}] Halt"); - wait(vm_id) - } - AxVCpuExitReason::Nothing => {} - AxVCpuExitReason::CpuDown { _state } => { - warn!("VM[{vm_id}] run VCpu[{vcpu_id}] CpuDown state {_state:#x}"); - wait(vm_id) - } - AxVCpuExitReason::CpuUp { - target_cpu, - entry_point, - arg, - } => { - info!( - "VM[{vm_id}]'s VCpu[{vcpu_id}] try to boot target_cpu [{target_cpu}] entry_point={entry_point:x} arg={arg:#x}" - ); - - // Get the mapping relationship between all vCPUs and physical CPUs from the configuration - let vcpu_mappings = vm.get_vcpu_affinities_pcpu_ids(); - - // Find the vCPU ID corresponding to the physical ID - let target_vcpu_id = vcpu_mappings - .iter() - .find_map(|(vcpu_id, _, phys_id)| { - if *phys_id == target_cpu as usize { - Some(*vcpu_id) - } else { - None - } - }) - .unwrap_or_else(|| { - panic!("Physical CPU ID {target_cpu} not found in VM configuration",) - }); - - vcpu_on(vm.clone(), target_vcpu_id, entry_point, arg as _); - #[cfg(not(target_arch = "riscv64"))] - vcpu.set_gpr(0, 0); - #[cfg(target_arch = "riscv64")] - vcpu.set_gpr(riscv_vcpu::GprIndex::A0 as usize, 0); - } - AxVCpuExitReason::SystemDown => { - warn!("VM[{vm_id}] run VCpu[{vcpu_id}] SystemDown"); - vm.shutdown().expect("VM shutdown failed"); - } - AxVCpuExitReason::SendIPI { - target_cpu, - target_cpu_aux, - send_to_all, - send_to_self, - vector, - } => { - debug!( - "VM[{vm_id}] run VCpu[{vcpu_id}] SendIPI, target_cpu={target_cpu:#x}, target_cpu_aux={target_cpu_aux:#x}, vector={vector}", - ); - if send_to_all { - unimplemented!("Send IPI to all CPUs is not implemented yet"); - } - - if target_cpu == vcpu_id as u64 || send_to_self { - inject_interrupt(vector as _); - } else { - vm.inject_interrupt_to_vcpu( - CpuMask::one_shot(target_cpu as _), - vector as _, - ) - .unwrap(); - } - } - e => { - warn!("VM[{vm_id}] run VCpu[{vcpu_id}] unhandled vmexit: {e:?}"); - } - }, - Err(err) => { - error!("VM[{vm_id}] run VCpu[{vcpu_id}] get error {err:?}"); - // wait(vm_id) - vm.shutdown().expect("VM shutdown failed"); - } - } - - // Check if the VM is suspended - if vm.suspending() { - debug!( - "VM[{}] VCpu[{}] is suspended, waiting for resume...", - vm_id, vcpu_id - ); - wait_for(vm_id, || !vm.suspending()); - info!("VM[{}] VCpu[{}] resumed from suspend", vm_id, vcpu_id); - continue; - } - - // Check if the VM is stopping. - if vm.stopping() { - warn!( - "VM[{}] VCpu[{}] stopping because of VM stopping", - vm_id, vcpu_id - ); - - if mark_vcpu_exiting(vm_id) { - info!("VM[{vm_id}] VCpu[{vcpu_id}] last VCpu exiting, decreasing running VM count"); - - // Transition from Stopping to Stopped - vm.set_vm_status(axvm::VMStatus::Stopped); - info!("VM[{}] state changed to Stopped", vm_id); - - sub_running_vm_count(1); - ax_wait_queue_wake(&super::VMM, 1); - } - - break; - } - } - - info!("VM[{}] VCpu[{}] exiting...", vm_id, vcpu_id); -} diff --git a/os/axvisor/src/vmm/vm_list.rs b/os/axvisor/src/vmm/vm_list.rs deleted file mode 100644 index 136a49949..000000000 --- a/os/axvisor/src/vmm/vm_list.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::collections::BTreeMap; -use alloc::vec::Vec; - -use spin::Mutex; - -use crate::vmm::VMRef; - -/// Represents a list of VMs, -/// stored in a BTreeMap where the key is the VM ID and the value is a reference to the VM. -struct VMList { - vm_list: BTreeMap, -} - -impl VMList { - /// Creates a new, empty `VMList`. - const fn new() -> VMList { - VMList { - vm_list: BTreeMap::new(), - } - } - - /// Adds a new VM to the list. - /// - /// If a VM with the given ID already exists, a warning is logged, and the VM is not added. - /// - /// # Arguments - /// - /// * `vm_id` - The unique identifier for the VM. - /// * `vm` - A reference to the VM that will be added. - fn push_vm(&mut self, vm_id: usize, vm: VMRef) { - if self.vm_list.contains_key(&vm_id) { - warn!("VM[{vm_id}] already exists, push VM failed, just return ..."); - return; - } - self.vm_list.insert(vm_id, vm); - } - - /// Removes a VM from the list by its ID. - /// - /// # Arguments - /// - /// * `vm_id` - The unique identifier of the VM to be removed. - /// - /// # Returns - /// - /// Returns `Some(VMRef)` if the VM was successfully removed, or `None` if the VM with the given ID did not exist. - #[allow(unused)] - fn remove_vm(&mut self, vm_id: usize) -> Option { - self.vm_list.remove(&vm_id) - } - - /// Retrieves a VM from the list by its ID. - /// - /// # Arguments - /// - /// * `vm_id` - The unique identifier of the VM to be retrieved. - /// - /// # Returns - /// - /// Returns `Some(VMRef)` if the VM exists, or `None` if the VM with the given ID does not exist. - fn get_vm_by_id(&self, vm_id: usize) -> Option { - self.vm_list.get(&vm_id).cloned() - } -} - -// A global list of VMs, protected by a mutex for thread-safe access. -static GLOBAL_VM_LIST: Mutex = Mutex::new(VMList::new()); - -/// Adds a VM to the global VM list. -/// -/// # Arguments -/// -/// * `vm` - A reference to the VM instance. -pub fn push_vm(vm: VMRef) { - GLOBAL_VM_LIST.lock().push_vm(vm.id(), vm) -} - -/// Removes a VM from the global VM list by its ID. -/// -/// # Arguments -/// -/// * `vm_id` - The unique identifier of the VM to be removed. -/// -/// # Returns -/// -/// * `Option` - The removed VM reference if it exists, or `None` if not. -#[allow(unused)] -pub fn remove_vm(vm_id: usize) -> Option { - GLOBAL_VM_LIST.lock().remove_vm(vm_id) -} - -/// Retrieves a VM from the global VM list by its ID. -/// -/// # Arguments -/// -/// * `vm_id` - The unique identifier of the VM to retrieve. -/// -/// # Returns -/// -/// * `Option` - The VM reference if it exists, or `None` if not. -#[allow(unused)] -pub fn get_vm_by_id(vm_id: usize) -> Option { - GLOBAL_VM_LIST.lock().get_vm_by_id(vm_id) -} - -pub fn get_vm_list() -> Vec { - let global_vm_list = GLOBAL_VM_LIST.lock().vm_list.clone(); - let mut vm_list = Vec::with_capacity(global_vm_list.len()); - for (_id, vm) in global_vm_list { - vm_list.push(vm.clone()); - } - vm_list -} diff --git a/os/axvisor/xtask/src/cargo.rs b/os/axvisor/xtask/src/cargo.rs deleted file mode 100644 index 6298377bb..000000000 --- a/os/axvisor/xtask/src/cargo.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ostool::build::CargoRunnerKind; -use std::{fs, path::PathBuf}; - -use crate::ctx::Context; - -impl Context { - pub async fn run_qemu(&mut self, config_path: Option) -> anyhow::Result<()> { - let build_config = self.load_config()?; - - let arch = if build_config.target.contains("aarch64") { - Arch::Aarch64 - } else if build_config.target.contains("x86_64") { - Arch::X86_64 - } else if build_config.target.contains("riscv64") { - Arch::Riscv64 - } else { - return Err(anyhow::anyhow!( - "Unsupported target architecture: {}", - build_config.target - )); - }; - - let config_path = if let Some(path) = config_path { - path - } else { - PathBuf::from(format!(".qemu-{arch:?}.toml").to_lowercase()) - }; - - // If the configuration file does not exist, copy from the default location - if !config_path.exists() { - fs::copy( - PathBuf::from("scripts") - .join("ostool") - .join(format!("qemu-{arch:?}.toml").to_lowercase()), - &config_path, - )?; - } - - let kind = CargoRunnerKind::Qemu { - qemu_config: Some(config_path), - debug: false, - dtb_dump: false, - }; - - self.ctx.cargo_run(&build_config, &kind).await?; - - Ok(()) - } - - pub async fn run_uboot(&mut self, config_path: Option) -> anyhow::Result<()> { - let build_config = self.load_config()?; - - let config_path = config_path.unwrap_or_else(|| PathBuf::from(".uboot.toml")); - - let kind = CargoRunnerKind::Uboot { - uboot_config: Some(config_path), - }; - - self.ctx.cargo_run(&build_config, &kind).await?; - - Ok(()) - } -} - -#[derive(Debug, Clone, Copy)] -enum Arch { - Aarch64, - X86_64, - Riscv64, -} diff --git a/os/axvisor/xtask/src/clippy.rs b/os/axvisor/xtask/src/clippy.rs deleted file mode 100644 index 5ba071ac1..000000000 --- a/os/axvisor/xtask/src/clippy.rs +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use anyhow::{Context, Result, anyhow}; -use cargo_metadata::{MetadataCommand, Package}; -use colored::*; -use std::collections::HashSet; -use std::process::Command; -use std::time::Instant; - -// Import ClippyArgs from main.rs -use super::ClippyArgs; - -/// Static target array configuration -const TARGETS: &[&str] = &["x86_64-unknown-none", "aarch64-unknown-none-softfloat"]; -const PACKAGE: &str = "axvisor"; - -/// Clippy check result statistics -#[derive(Debug, Default)] -struct ClippyStats { - total_checks: usize, - passed_checks: usize, - failed_checks: usize, - skipped_checks: usize, -} - -impl ClippyStats { - fn record_passed(&mut self) { - self.total_checks += 1; - self.passed_checks += 1; - } - - fn record_failed(&mut self) { - self.total_checks += 1; - self.failed_checks += 1; - } - - fn record_skipped(&mut self) { - self.total_checks += 1; - self.skipped_checks += 1; - } - - fn print_summary(&self, duration: std::time::Duration) { - println!("\n{}", "=== Clippy Check Summary ===".bold().cyan()); - println!("Total checks: {}", self.total_checks); - println!("Passed checks: {}", self.passed_checks.to_string().green()); - if self.failed_checks > 0 { - println!( - "Failed checks: {}", - self.failed_checks.to_string().red().bold() - ); - } else { - println!("Failed checks: {}", self.failed_checks); - } - println!( - "Skipped checks: {}", - self.skipped_checks.to_string().yellow() - ); - println!("Execution time: {:.2}s", duration.as_secs_f64()); - - if self.failed_checks > 0 { - println!( - "\n{}", - "❌ Clippy errors found, please check the output above" - .bold() - .red() - ); - } else { - println!("\n{}", "✅ All clippy checks passed!".bold().green()); - } - } -} - -/// Main clippy runner function -pub fn run_clippy(args: ClippyArgs) -> Result<()> { - let start_time = Instant::now(); - println!( - "{}", - "🔍 Starting comprehensive Clippy checks...".bold().cyan() - ); - - // Parse package filter, default to axvisor package only - let package_filter = args - .packages - .as_ref() - .map(|s| { - s.split(',') - .map(|s| s.trim().to_string()) - .collect::>() - }) - .or_else(|| Some(HashSet::from([PACKAGE.to_string()]))); - - // Parse target filter - let target_filter = args.targets.as_ref().map(|s| { - s.split(',') - .map(|s| s.trim().to_string()) - .collect::>() - }); - - // Get workspace metadata - let metadata = MetadataCommand::new() - .no_deps() - .exec() - .context("Failed to get workspace metadata")?; - - let workspace_members: Vec<&Package> = metadata - .workspace_members - .iter() - .filter_map(|id| metadata.packages.iter().find(|p| p.id == *id)) - .filter(|pkg| { - // Filter out xtask itself - pkg.name != "xtask" - }) - .filter(|pkg| { - // Apply package filter - if let Some(ref filter) = package_filter { - filter.contains(&pkg.name.to_string()) - } else { - true - } - }) - .collect(); - - if workspace_members.is_empty() { - return Err(anyhow!("No matching workspace packages found")); - } - - println!("Found {} workspace packages:", workspace_members.len()); - for pkg in &workspace_members { - println!(" - {} ({})", pkg.name.bold(), pkg.version); - } - - let mut stats = ClippyStats::default(); - let mut has_errors = false; - - // Iterate through all targets - for target in TARGETS { - // Apply target filter - if let Some(ref filter) = target_filter - && !filter.contains(&target.to_string()) - { - continue; - } - - println!( - "\n{}", - format!("🎯 Checking target: {target}").bold().yellow() - ); - - // Iterate through all workspace packages - for package in &workspace_members { - println!("\n📦 Checking package: {}", package.name.bold()); - - // Get all features of the package - let features = get_package_features(package); - - if features.is_empty() { - // If no features, perform basic check - println!(" 🔧 No additional features, performing basic check..."); - - if args.dry_run { - let fix_str = if args.fix { " --fix" } else { "" }; - let allow_dirty_str = if args.fix && args.allow_dirty { - " --allow-dirty" - } else { - "" - }; - println!( - " [DRY RUN] cargo clippy --target {} -p {}{}{}", - target, package.name, fix_str, allow_dirty_str - ); - stats.record_skipped(); - } else { - match run_single_clippy( - target, - package.name.as_ref(), - &[], - args.continue_on_error, - args.fix, - args.allow_dirty, - ) { - Ok(_) => { - stats.record_passed(); - } - Err(e) => { - stats.record_failed(); - eprintln!(" {}", format!("❌ Error: {e}").red()); - if !args.continue_on_error { - return Err(e); - } - has_errors = true; - } - } - } - } else { - // Iterate through each feature for individual checks - for feature in &features { - println!(" 🔧 Checking feature: {}", feature.cyan()); - - if args.dry_run { - let fix_str = if args.fix { " --fix" } else { "" }; - let allow_dirty_str = if args.fix && args.allow_dirty { - " --allow-dirty" - } else { - "" - }; - println!( - " [DRY RUN] cargo clippy --target {} -p {} --features {}{}{}", - target, package.name, feature, fix_str, allow_dirty_str - ); - stats.record_skipped(); - } else { - match run_single_clippy( - target, - package.name.as_ref(), - std::slice::from_ref(feature), - args.continue_on_error, - args.fix, - args.allow_dirty, - ) { - Ok(_) => { - stats.record_passed(); - } - Err(e) => { - stats.record_failed(); - eprintln!(" {}", format!("❌ Error: {e}").red()); - if !args.continue_on_error { - return Err(e); - } - has_errors = true; - } - } - } - } - - // Also check all features enabled together - println!(" 🔧 Checking all features together..."); - if args.dry_run { - let fix_str = if args.fix { " --fix" } else { "" }; - let allow_dirty_str = if args.fix && args.allow_dirty { - " --allow-dirty" - } else { - "" - }; - println!( - " [DRY RUN] cargo clippy --target {} -p {} --features {}{}{}", - target, - package.name, - features.join(","), - fix_str, - allow_dirty_str - ); - stats.record_skipped(); - } else { - match run_single_clippy( - target, - package.name.as_ref(), - &features, - args.continue_on_error, - args.fix, - args.allow_dirty, - ) { - Ok(_) => { - stats.record_passed(); - } - Err(e) => { - stats.record_failed(); - eprintln!(" {}", format!("❌ Error: {e}").red()); - if !args.continue_on_error { - return Err(e); - } - has_errors = true; - } - } - } - } - } - } - - let duration = start_time.elapsed(); - stats.print_summary(duration); - - if has_errors { - Err(anyhow!("Clippy checks found errors")) - } else { - Ok(()) - } -} - -/// Get all features of a package -fn get_package_features(package: &Package) -> Vec { - let mut features = Vec::new(); - - // Collect all non-empty feature names - for feature_name in package.features.keys() { - if !feature_name.is_empty() && !feature_name.starts_with("dep:") { - features.push(feature_name.clone()); - } - } - - // Sort for consistent output - features.sort(); - features -} - -/// Run a single clippy check -fn run_single_clippy( - target: &str, - package: &str, - features: &[String], - continue_on_error: bool, - fix: bool, - allow_dirty: bool, -) -> Result<()> { - let mut args = vec![ - "clippy".to_string(), - "--target".to_string(), - target.to_string(), - "-p".to_string(), - package.to_string(), - "--all-targets".to_string(), - ]; - - // Add --fix parameter - if fix { - args.push("--fix".to_string()); - } - - // Add --allow-dirty parameter - if fix && allow_dirty { - args.push("--allow-dirty".to_string()); - } - - // Add features parameter - if !features.is_empty() { - args.push("--features".to_string()); - args.push(features.join(",")); - } - - // Add clippy options - args.push("--".to_string()); - args.push("-D".to_string()); // Treat all warnings as errors - args.push("warnings".to_string()); - - let mut cmd = Command::new("cargo"); - cmd.args(&args); - - // Set environment variable for stricter checking - cmd.env("RUSTFLAGS", "-D warnings"); - - let fix_str = if fix { " --fix" } else { "" }; - let allow_dirty_str = if fix && allow_dirty { - " --allow-dirty" - } else { - "" - }; - println!( - " Executing: {}", - format!( - "cargo clippy --target {} -p {}{}{}{}", - target, - package, - if features.is_empty() { - String::new() - } else { - format!(" --features {}", features.join(",")) - }, - fix_str, - allow_dirty_str - ) - .dimmed() - ); - - let output = cmd.output().context(format!( - "Failed to execute cargo clippy: target={target}, package={package}, features={features:?}" - ))?; - - if output.status.success() { - // Even if successful, check if there's output (sometimes clippy has warnings but still returns success) - if !output.stderr.is_empty() { - let stderr = String::from_utf8_lossy(&output.stderr); - if stderr.contains("warning:") || stderr.contains("error:") { - return Err(anyhow!("Clippy output warnings or errors:\n{stderr}")); - } - } - println!(" {}", "✅ Passed".green()); - Ok(()) - } else { - let stderr = String::from_utf8_lossy(&output.stderr); - let stdout = String::from_utf8_lossy(&output.stdout); - - let error_msg = if !stderr.is_empty() { - stderr.to_string() - } else if !stdout.is_empty() { - stdout.to_string() - } else { - format!( - "clippy check failed, exit code: {}", - output.status.code().unwrap_or(-1) - ) - }; - - if continue_on_error { - eprintln!( - " {}", - format!("⚠️ Error (continuing):\n{error_msg}").yellow() - ); - Ok(()) - } else { - Err(anyhow!("Clippy check failed:\n{error_msg}")) - } - } -} diff --git a/os/axvisor/xtask/src/ctx.rs b/os/axvisor/xtask/src/ctx.rs deleted file mode 100644 index 1a7727725..000000000 --- a/os/axvisor/xtask/src/ctx.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ostool::ctx::{AppContext, PathConfig}; - -use crate::BuildArgs; - -pub struct Context { - pub ctx: AppContext, - pub build_config_path: Option, - pub vmconfigs: Vec, -} - -impl Context { - pub fn new() -> Self { - let workdir = std::env::current_dir().expect("Failed to get current working directory"); - - let ctx = AppContext { - paths: PathConfig { - workspace: workdir.clone(), - manifest: workdir, - ..Default::default() - }, - ..Default::default() - }; - - Context { - ctx, - build_config_path: None, - vmconfigs: vec![], - } - } - - pub fn apply_build_args(&mut self, args: &BuildArgs) { - self.ctx.paths.config.build_dir = args.build_dir.clone(); - self.ctx.paths.config.bin_dir = args.bin_dir.clone(); - } -} diff --git a/os/axvisor/xtask/src/devspace.rs b/os/axvisor/xtask/src/devspace.rs deleted file mode 100644 index 9427da1dc..000000000 --- a/os/axvisor/xtask/src/devspace.rs +++ /dev/null @@ -1,501 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::collections::{BTreeMap, HashMap, hash_map::Entry}; -use std::fs; -use std::path::{Path, PathBuf}; -use std::process::Command; - -use anyhow::{Context, Result, anyhow}; -use cargo_metadata::{Metadata, MetadataCommand, Package}; -use serde::{Deserialize, Serialize}; - -const STATE_DIR: &str = ".devspace"; -const STATE_FILE: &str = ".devspace/state.json"; -const PATCH_BEGIN_MARKER: &str = "# >>> devspace patches >>>"; -const PATCH_END_MARKER: &str = "# <<< devspace patches <<<"; -const CRATES_IO_SOURCE_KEY: &str = "crates-io"; - -const DEVSPACE_REPOS: &[&str] = &[ - "arm_vcpu", - "arm_vgic", - "axaddrspace", - "axdevice_base", - "x86_vcpu", - "x86_vlapic", -]; - -const DEVSPACE_REPO_OVERRIDES: &[(&str, &str)] = &[( - "axdevice_base", - "https://github.com/arceos-hypervisor/axdevice_base.git", -)]; - -pub fn start() -> Result<()> { - let metadata = MetadataCommand::new() - .exec() - .context("Failed to run cargo metadata")?; - let repos = resolve_dev_repos(&metadata)?; - - let mut state = load_state()?; - ensure_submodules(&mut state, &repos)?; - save_state(&state)?; - - let specs = compute_patch_specs(&metadata, &repos)?; - apply_patches(&specs)?; - - state.patches = specs - .into_iter() - .map(|spec| PatchRecord { - source: spec.source, - crate_name: spec.crate_name, - }) - .collect(); - save_state(&state)?; - - println!("devspace start completed"); - Ok(()) -} - -pub fn stop() -> Result<()> { - let mut state = load_state()?; - - if !state.patches.is_empty() { - remove_patches(&state.patches)?; - state.patches.clear(); - } - - if !state.modules.is_empty() { - remove_submodules(&state.modules)?; - state.modules.clear(); - } - - save_state(&state)?; - println!("devspace stop completed"); - Ok(()) -} - -fn ensure_submodules(state: &mut DevspaceState, repos: &[DevRepo]) -> Result<()> { - for repo in repos { - let dest_path = Path::new(&repo.dest); - if dest_path.exists() { - continue; - } - - println!("Adding submodule {} -> {}", repo.git_url, repo.dest); - run_git(&[ - "submodule", - "add", - "--force", - repo.git_url.as_str(), - repo.dest.as_str(), - ])?; - run_git(&[ - "submodule", - "update", - "--init", - "--recursive", - repo.dest.as_str(), - ])?; - match state.modules.entry(repo.name.clone()) { - Entry::Occupied(mut entry) => { - entry.get_mut().path = repo.dest.clone(); - } - Entry::Vacant(entry) => { - entry.insert(ManagedModule { - name: repo.name.clone(), - path: repo.dest.clone(), - }); - } - } - } - - Ok(()) -} - -fn remove_submodules(modules: &HashMap) -> Result<()> { - for module in modules.values() { - println!("Removing submodule {}", module.path); - let path = module.path.as_str(); - let _ = run_git(&["submodule", "deinit", "-f", "--", path]); - let git_modules_dir = Path::new(".git/modules").join(path); - if git_modules_dir.exists() { - fs::remove_dir_all(&git_modules_dir) - .with_context(|| format!("Failed to remove {git_modules_dir:?}"))?; - } - if Path::new(path).exists() { - let _ = run_git(&["rm", "-f", "--", path]); - if Path::new(path).exists() { - fs::remove_dir_all(path).with_context(|| format!("Failed to remove {path}"))?; - } - } - } - Ok(()) -} - -fn compute_patch_specs(metadata: &Metadata, repos: &[DevRepo]) -> Result> { - let repo_map = build_repo_lookup(repos); - let mut specs = BTreeMap::new(); - - for pkg in &metadata.packages { - if let Some(spec) = package_patch_spec(pkg, &repo_map) { - specs - .entry((spec.source.clone(), spec.crate_name.clone())) - .or_insert(spec); - } - } - - for repo in repos { - if !specs.contains_key(&(repo.source.clone(), repo.name.clone())) { - return Err(anyhow!( - "Failed to prepare patch for crate {} (source: {})", - repo.name, - repo.source - )); - } - } - - Ok(specs.into_values().collect()) -} - -fn package_patch_spec(pkg: &Package, repo_map: &HashMap) -> Option { - let source_raw = pkg.source.as_ref()?.to_string(); - let normalized = normalize_source(&source_raw)?; - let key = repo_lookup_key(&normalized, pkg.name.as_str()); - let repo = repo_map.get(&key)?; - let manifest = Path::new(pkg.manifest_path.as_str()); - let local_path = manifest_relative_dir(manifest) - .map(|subdir| Path::new(&repo.dest).join(subdir)) - .unwrap_or_else(|| PathBuf::from(&repo.dest)); - Some(PatchSpec { - source: repo.source.to_string(), - crate_name: pkg.name.to_string(), - path: to_unix_path(&local_path), - }) -} - -fn apply_patches(specs: &[PatchSpec]) -> Result<()> { - if specs.is_empty() { - println!("No git dependencies matched managed repos; skipping patch stage"); - return Ok(()); - } - - let config_path = Path::new(".cargo/config.toml"); - let mut contents = if config_path.exists() { - fs::read_to_string(config_path) - .with_context(|| format!("Failed to read {config_path:?}"))? - } else { - String::new() - }; - - let (cleaned, _) = strip_devspace_section(&contents); - contents = cleaned; - - if !contents.is_empty() && !contents.ends_with('\n') { - contents.push('\n'); - } - if !contents.is_empty() && !contents.ends_with("\n\n") { - contents.push('\n'); - } - - contents.push_str(&render_devspace_section(specs)); - if !contents.ends_with('\n') { - contents.push('\n'); - } - - fs::write(config_path, contents).with_context(|| format!("Failed to write {config_path:?}"))?; - Ok(()) -} - -fn remove_patches(_: &[PatchRecord]) -> Result<()> { - let config_path = Path::new(".cargo/config.toml"); - if !config_path.exists() { - return Ok(()); - } - - let original = fs::read_to_string(config_path) - .with_context(|| format!("Failed to read {config_path:?}"))?; - let (cleaned, removed) = strip_devspace_section(&original); - - if removed { - fs::write(config_path, cleaned) - .with_context(|| format!("Failed to write {config_path:?}"))?; - } - Ok(()) -} - -fn load_state() -> Result { - let path = Path::new(STATE_FILE); - if !path.exists() { - return Ok(DevspaceState::default()); - } - - let contents = fs::read_to_string(path).with_context(|| format!("Failed to read {path:?}"))?; - let state = - serde_json::from_str(&contents).with_context(|| format!("Failed to parse {path:?}"))?; - Ok(state) -} - -fn save_state(state: &DevspaceState) -> Result<()> { - fs::create_dir_all(STATE_DIR).context("Failed to create devspace state dir")?; - let data = serde_json::to_string_pretty(state)?; - fs::write(STATE_FILE, data).context("Failed to write devspace state")?; - Ok(()) -} - -fn resolve_dev_repos(metadata: &Metadata) -> Result> { - let override_map: HashMap<&str, &str> = DEVSPACE_REPO_OVERRIDES.iter().copied().collect(); - - DEVSPACE_REPOS - .iter() - .map(|crate_name| { - let override_url = override_map.get(*crate_name).copied(); - - let matches: Vec<&Package> = metadata - .packages - .iter() - .filter(|pkg| pkg.name == *crate_name) - .collect(); - - if matches.is_empty() { - return Err(anyhow!( - "crate {crate_name} not found in workspace metadata" - )); - } - - let pkg = matches - .iter() - .copied() - .find(|pkg| { - pkg.source - .as_ref() - .map(|src| src.to_string().starts_with("git+")) - .unwrap_or(false) - }) - .unwrap_or(*matches.first().unwrap()); - - let source_raw = pkg - .source - .as_ref() - .map(|s| s.to_string()) - .ok_or_else(|| anyhow!("crate {crate_name} has no source information"))?; - - let (patch_source, git_url) = if source_raw.starts_with("git+") { - let normalized = normalize_source(&source_raw).ok_or_else(|| { - anyhow!( - "crate {} has unsupported source {}", - crate_name, - source_raw.clone() - ) - })?; - let git_url = extract_git_url(&source_raw).ok_or_else(|| { - anyhow!( - "crate {} has unsupported source {}", - crate_name, - source_raw.clone() - ) - })?; - (normalized, git_url) - } else if source_raw == "registry+https://github.com/rust-lang/crates.io-index" { - let repo_url = if let Some(url) = pkg.repository.clone() { - url - } else if let Some(url) = override_url { - println!( - "crate {crate_name} is missing repository metadata; using override {url}" - ); - url.to_string() - } else { - return Err(anyhow!( - "crate {crate_name} is from crates.io but missing repository metadata" - )); - }; - (CRATES_IO_SOURCE_KEY.to_string(), repo_url) - } else { - return Err(anyhow!( - "crate {crate_name} uses unsupported source {source_raw}" - )); - }; - - Ok(DevRepo { - name: crate_name.to_string(), - git_url, - source: patch_source, - dest: format!("modules/{crate_name}"), - }) - }) - .collect() -} - -fn build_repo_lookup(repos: &[DevRepo]) -> HashMap { - repos - .iter() - .map(|repo| (repo_lookup_key(&repo.source, &repo.name), repo)) - .collect() -} - -fn manifest_relative_path(path: &Path) -> Option { - let components: Vec<_> = path.components().collect(); - let idx = components - .iter() - .position(|comp| comp.as_os_str() == "checkouts")?; - if idx + 3 >= components.len() { - return None; - } - let mut rel = PathBuf::new(); - for comp in &components[idx + 3..] { - rel.push(comp.as_os_str()); - } - Some(rel) -} - -fn manifest_relative_dir(path: &Path) -> Option { - let mut rel = manifest_relative_path(path)?; - if rel.pop() { - Some(rel) - } else { - Some(PathBuf::new()) - } -} - -fn normalize_source(raw: &str) -> Option { - if let Some(trimmed) = raw.strip_prefix("git+") { - let no_fragment = trimmed.split('#').next().unwrap_or(trimmed); - let no_query = no_fragment.split('?').next().unwrap_or(no_fragment); - let without_git = no_query.trim_end_matches(".git"); - let normalized = without_git.trim_end_matches('/'); - Some(normalized.to_string()) - } else if raw == "registry+https://github.com/rust-lang/crates.io-index" { - Some(CRATES_IO_SOURCE_KEY.to_string()) - } else { - None - } -} - -fn extract_git_url(raw: &str) -> Option { - if !raw.starts_with("git+") { - return None; - } - let trimmed = &raw[4..]; - let no_query = trimmed.split('?').next().unwrap_or(trimmed); - let no_fragment = no_query.split('#').next().unwrap_or(no_query); - Some(no_fragment.to_string()) -} - -fn render_devspace_section(specs: &[PatchSpec]) -> String { - let mut grouped: BTreeMap> = BTreeMap::new(); - for spec in specs { - grouped - .entry(spec.source.clone()) - .or_default() - .insert(spec.crate_name.clone(), spec.path.clone()); - } - - let mut section = String::new(); - section.push_str(PATCH_BEGIN_MARKER); - section.push('\n'); - section.push_str("# Managed by `cargo xtask devspace`"); - section.push('\n'); - - let mut iter = grouped.iter().peekable(); - while let Some((source, crates)) = iter.next() { - section.push_str(&format!("[patch.\"{source}\"]\n")); - for (crate_name, path) in crates { - section.push_str(&format!("{crate_name} = {{ path = \"{path}\" }}\n")); - } - if iter.peek().is_some() { - section.push('\n'); - } - } - - section.push('\n'); - section.push_str(PATCH_END_MARKER); - section.push('\n'); - section -} - -fn strip_devspace_section(contents: &str) -> (String, bool) { - if let Some(start_idx) = contents.find(PATCH_BEGIN_MARKER) - && let Some(end_rel) = contents[start_idx..].find(PATCH_END_MARKER) - { - let end_idx = start_idx + end_rel + PATCH_END_MARKER.len(); - let mut removal_end = end_idx; - let tail = &contents[removal_end..]; - if tail.starts_with("\r\n") { - removal_end += 2; - } else if tail.starts_with('\n') { - removal_end += 1; - } - let mut result = String::with_capacity(contents.len()); - result.push_str(&contents[..start_idx]); - result.push_str(&contents[removal_end..]); - return (result, true); - } - (contents.to_string(), false) -} - -fn to_unix_path(path: &Path) -> String { - path.to_string_lossy().replace('\\', "/") -} - -fn run_git(args: &[&str]) -> Result<()> { - let status = Command::new("git") - .current_dir(workspace_root()?) - .args(args) - .status() - .with_context(|| format!("Failed to run git {}", args.join(" ")))?; - if !status.success() { - return Err(anyhow!("git command failed: git {}", args.join(" "))); - } - Ok(()) -} - -fn workspace_root() -> Result { - std::env::current_dir().context("Failed to resolve workspace root") -} - -#[derive(Default, Serialize, Deserialize)] -struct DevspaceState { - modules: HashMap, - patches: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -struct ManagedModule { - name: String, - path: String, -} - -#[derive(Clone, Serialize, Deserialize)] -struct PatchRecord { - source: String, - crate_name: String, -} - -#[derive(Clone)] -struct PatchSpec { - source: String, - crate_name: String, - path: String, -} - -#[derive(Clone)] -struct DevRepo { - name: String, - git_url: String, - source: String, - dest: String, -} - -fn repo_lookup_key(source: &str, crate_name: &str) -> String { - format!("{source}::{crate_name}") -} diff --git a/os/axvisor/xtask/src/image.rs b/os/axvisor/xtask/src/image.rs deleted file mode 100644 index 6b83511a9..000000000 --- a/os/axvisor/xtask/src/image.rs +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Guest Image management commands for the Axvisor build configuration tool -//! -//! This module provides functionality to list, download, and remove -//! pre-built guest images for various supported boards and architectures. The images -//! are downloaded from a specified URL base and verified using SHA-256 checksums. The downloaded -//! images are automatically extracted to a specified output directory. Images can also be removed -//! from the temporary directory. -//! -//! # Usage examples -//! -//! ``` -//! // List available images -//! xtask image ls -//! // Download a specific image and automatically extract it (default behavior) -//! xtask image download evm3588_arceos --output-dir ./images -//! // Download a specific image without extracting -//! xtask image download evm3588_arceos --output-dir ./images --no-extract -//! // Remove a specific image from temp directory -//! xtask image rm evm3588_arceos -//! ``` - -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use anyhow::{Result, anyhow}; -use clap::{Parser, Subcommand}; -use flate2::read::GzDecoder; -use tar::Archive; - -mod config; -mod download; -mod registry; -mod spec; -mod storage; - -use config::ImageConfig; -use spec::ImageSpecRef; -use storage::Storage; - -/// Image management command line arguments. -#[derive(Parser)] -pub struct ImageArgs { - #[command(flatten)] - pub overrides: ImageConfigOverrides, - - /// Image subcommand to run: `ls`, `download`, `rm`, or `sync`. - #[command(subcommand)] - pub command: ImageCommands, -} - -#[derive(Parser)] -pub struct ImageConfigOverrides { - /// The path to the local storage of images. Override the config file. - #[arg(short('S'), long, global = true)] - pub local_storage: Option, - - /// The URL of the remote registry of images. Override the config file. - #[arg(short('R'), long, global = true)] - pub registry: Option, - - /// Do not sync from remote registry even if the local image storage is - /// broken, missing, or out of date. Override the config file. - #[arg(short('N'), long, global = true)] - pub no_auto_sync: bool, - - /// The threshold in seconds to automatically synchronize image list from - /// remote registry. 0 means never. Override the config file. - #[arg(long, global = true)] - pub auto_sync_threshold: Option, -} - -impl ImageConfigOverrides { - /// Applies CLI overrides onto the given config (in-place). - /// - /// # Arguments - /// - /// * `config` - Config to mutate; non-`None` override fields overwrite corresponding values - pub fn apply_on(&self, config: &mut ImageConfig) { - if let Some(local_storage) = self.local_storage.as_ref() { - config.local_storage = local_storage.clone(); - } - if let Some(registry) = self.registry.as_ref() { - config.registry = registry.clone(); - } - if self.no_auto_sync { - config.auto_sync = false; - } - if let Some(auto_sync_threshold) = self.auto_sync_threshold { - config.auto_sync_threshold = auto_sync_threshold; - } - } -} - -/// Image management commands -#[derive(Subcommand)] -pub enum ImageCommands { - /// List all available images. - Ls { - /// Show different versions of the same image in separate lines. - #[arg(short, long)] - verbose: bool, - - /// Filter images by name pattern. - pattern: Option, - }, - - /// Download the specified image and automatically extract it. Use ASCII - /// colon to specify version, e.g. `evm3588_arceos:0.0.22`; omit for latest. - Download { - /// Image to download: `name` or `name:version`. - image_name: String, - - /// Output directory for the downloaded image, defaults to - /// "/tmp/.axvisor-images/". - #[arg(short, long)] - output_dir: Option, - - /// Do not extract after download. - #[arg(long)] - no_extract: bool, - }, - - /// Remove the specified image from temp directory. Use ASCII colon to - /// specify version, e.g. `evm3588_arceos:0.0.22`; omit for default path. - Rm { - /// Image to remove: `name` or `name:version`. - image_name: String, - }, - - /// Synchronize image list from a remote registry. - Sync, - - /// Reset the image config file to default. - Defconfig, -} - -/// Converts a path to absolute; joins with current dir if relative. -fn to_absolute_path(path: &Path) -> Result { - Ok(if path.is_absolute() { - path.to_path_buf() - } else { - std::env::current_dir()?.join(path) - }) -} - -/// Returns the path to the AxVisor repository root (parent of the xtask crate). -fn get_axvisor_repo_dir() -> Result { - // CARGO_MANIFEST_DIR contains the path of the xtask crate, and we need to - // get the parent directory to get the AxVisor repository directory. - Ok(Path::new(&std::env::var("CARGO_MANIFEST_DIR")?) - .parent() - .ok_or_else(|| anyhow!("Failed to determine AxVisor repository directory"))? - .to_path_buf()) -} - -impl ImageArgs { - /// Loads image configuration, merging CLI overrides with values from the config file. - pub async fn get_config(&self) -> Result { - let mut config = ImageConfig::read_config(&get_axvisor_repo_dir()?)?; - self.overrides.apply_on(&mut config); - Ok(config) - } - - /// Executes the selected image subcommand (`ls`, `download`, `rm`, `sync`, or `defconfig`). - pub async fn execute(&self) -> Result<()> { - match &self.command { - ImageCommands::Ls { verbose, pattern } => { - self.list_images(*verbose, pattern.as_deref()).await?; - } - ImageCommands::Download { - image_name, - output_dir, - no_extract, - } => { - self.download_image(image_name, output_dir.as_deref(), !no_extract) - .await?; - } - ImageCommands::Rm { image_name } => { - self.remove_image(image_name).await?; - } - ImageCommands::Sync => { - self.sync_registry().await?; - } - ImageCommands::Defconfig => { - ImageConfig::reset_config(&get_axvisor_repo_dir()?)?; - } - } - - Ok(()) - } - - /// Lists all available images from the local registry to stdout. - /// - /// # Arguments - /// - /// * `verbose` - If `true`, show each version separately; if `false`, merge same-name images and show version count - /// * `pattern` - If `Some`, filter by name: try regex match first, fallback to substring - pub async fn list_images(&self, verbose: bool, pattern: Option<&str>) -> Result<()> { - let config = self.get_config().await?; - let storage = Storage::new_from_config(&config).await?; - - storage.image_registry.print(verbose, pattern); - - Ok(()) - } - - /// Downloads the specified image and optionally extracts it. - /// - /// # Arguments - /// - /// * `spec` - Image spec (name and optional version) - /// * `output_dir` - If `Some`, write the `.tar.gz` to this directory; if `None`, use config's local storage path - /// * `extract` - If `true`, extract the archive after download - pub async fn download_image( - &self, - spec: impl Into>, - output_dir: Option<&str>, - extract: bool, - ) -> Result<()> { - let spec = spec.into(); - let config = self.get_config().await?; - let storage = Storage::new_from_config(&config).await?; - - let output_path = match output_dir { - Some(dir) => { - storage - .download_image_to(spec, &to_absolute_path(Path::new(dir))?) - .await? - } - None => storage.download_image(spec).await?, - }; - - if extract { - println!("Extracting image..."); - - let extract_dir = output_path - .parent() - .ok_or_else(|| anyhow!("Unable to determine parent directory of downloaded file"))? - .join(storage::image_extract_dir_name(spec)); - - fs::create_dir_all(&extract_dir)?; - - let tar_gz = fs::File::open(&output_path)?; - let decoder = GzDecoder::new(tar_gz); - let mut archive = Archive::new(decoder); - - archive.unpack(&extract_dir)?; - - println!("Image extracted to: {}", extract_dir.display()); - } - Ok(()) - } - - /// Removes the specified image from local storage (both `.tar.gz` and extracted directory). - /// - /// # Arguments - /// - /// * `spec` - Image spec (name and optional version) - pub async fn remove_image(&self, spec: impl Into>) -> Result<()> { - let spec = spec.into(); - let config = self.get_config().await?; - let storage = Storage::new_from_config(&config).await?; - - if storage.remove_image(spec).await? { - println!("Image removed successfully"); - } else { - println!("No files found for image: {}", spec.name); - } - Ok(()) - } - - /// Synchronizes the image list from the remote registry to local storage. - /// - /// Overwrites the local `images.toml` with the registry contents. - pub async fn sync_registry(&self) -> Result<()> { - let config: ImageConfig = self.get_config().await?; - let _ = Storage::new_from_registry(config.registry, config.local_storage).await?; - Ok(()) - } -} - -/// Dispatches and runs the image subcommand (ls, download, rm, sync) from parsed CLI arguments. -/// -/// # Arguments -/// -/// * `args` - Parsed image CLI arguments (subcommand and its options) -/// -/// # Returns -/// -/// * `Ok(())` - Subcommand completed successfully -/// * `Err` - Subcommand failed (e.g. list load, download, checksum, sync, or remove error) -/// -/// # Examples -/// -/// ```ignore -/// xtask image ls -/// xtask image download evm3588_arceos --output-dir ./images -/// xtask image rm evm3588_arceos -/// ``` -pub async fn run_image(args: ImageArgs) -> Result<()> { - args.execute().await -} diff --git a/os/axvisor/xtask/src/image/config.rs b/os/axvisor/xtask/src/image/config.rs deleted file mode 100644 index 90544a737..000000000 --- a/os/axvisor/xtask/src/image/config.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Image configuration management. -//! -//! Handles reading and writing of the `.image.toml` config file. - -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use anyhow::{Result, anyhow}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -/// Default registry URL for the image list. -pub const DEFAULT_REGISTRY_URL: &str = "https://raw.githubusercontent.com/arceos-hypervisor/axvisor-guest/refs/heads/main/registry/default.toml"; - -/// Relative path to the image config file under the repository root. -const IMAGE_CONFIG_PATH: &str = ".image.toml"; - -/// Default auto-sync threshold in seconds (7 days). -const DEFAULT_AUTO_SYNC_THRESHOLD: u64 = 60 * 60 * 24 * 7; - -/// Configuration for the image management. -/// -/// This struct is used to parse image config file (in [`IMAGE_CONFIG_PATH`]). -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] -pub struct ImageConfig { - /// The path to the local storage of images. - pub local_storage: PathBuf, - /// The URL of the remote registry of images. - pub registry: String, - /// Automatically synchronize image list from remote registry if the local - /// storage is broken, missing, or out of date. - pub auto_sync: bool, - /// The threshold in seconds to automatically synchronize image list from - /// remote registry due to too long since last synchronization. 0 means - /// never. - pub auto_sync_threshold: u64, -} - -impl ImageConfig { - /// Creates a config with default values (temp dir storage, default registry, auto-sync on). - pub fn new_default() -> Self { - Self { - local_storage: std::env::temp_dir().join(".axvisor-images"), - registry: DEFAULT_REGISTRY_URL.to_string(), - auto_sync: true, - auto_sync_threshold: DEFAULT_AUTO_SYNC_THRESHOLD, - } - } - - /// Returns the full path to the image config file. - /// - /// # Arguments - /// - /// * `base_dir` - Repository root directory (e.g. AxVisor repo root) - /// - /// # Returns - /// - /// Path `base_dir/.image.toml` - pub fn get_config_file_path(base_dir: &Path) -> Result { - Ok(base_dir.join(IMAGE_CONFIG_PATH)) - } - - /// Reads the image config from disk, creating a default file if it does not exist. - /// - /// # Arguments - /// - /// * `base_dir` - Repository root directory - /// - /// # Returns - /// - /// * `Ok(ImageConfig)` - Parsed config or default if newly created - /// * `Err` - File read or TOML parse error - pub fn read_config(base_dir: &Path) -> Result { - let path = Self::get_config_file_path(base_dir)?; - - if !path.exists() { - println!( - "Creating default image config file at {}...", - path.display() - ); - Self::write_config(base_dir, &Self::new_default()) - .map_err(|e| anyhow!("Failed to create default image config file: {e}"))?; - return Ok(Self::new_default()); - } - - let s = fs::read_to_string(&path)?; - toml::from_str(&s).map_err(|e| anyhow!("Invalid image config file: {e}")) - } - - /// Writes the given config to the image config file. - /// - /// # Arguments - /// - /// * `base_dir` - Repository root directory - /// * `config` - Config to serialize and write - /// - /// # Returns - /// - /// * `Ok(())` - Config written successfully - /// * `Err` - File write or serialization error - pub fn write_config(base_dir: &Path, config: &Self) -> Result<()> { - let path = Self::get_config_file_path(base_dir)?; - fs::write(path, toml::to_string(config)?) - .map_err(|e| anyhow!("Failed to write image config file: {e}")) - } - - /// Resets the image config file to default values. - /// - /// # Arguments - /// - /// * `base_dir` - Repository root directory - /// - /// # Returns - /// - /// * `Ok(())` - Config reset and written successfully - /// * `Err` - File write error - pub fn reset_config(base_dir: &Path) -> Result<()> { - let default_config = Self::new_default(); - Self::write_config(base_dir, &default_config) - .map_err(|e| anyhow!("Failed to reset image config file: {e}")) - } -} - -impl std::fmt::Display for ImageConfig { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "Local storage: {}", self.local_storage.display())?; - writeln!(f, "Registry: {}", self.registry)?; - writeln!(f, "Auto sync: {}", self.auto_sync)?; - - if self.auto_sync_threshold == 0 { - writeln!(f, "Auto sync threshold: 0 (never)")?; - } else { - let threshold_days = self.auto_sync_threshold / (60 * 60 * 24); - writeln!( - f, - "Auto sync threshold: {} ({} day(s))", - self.auto_sync_threshold, threshold_days - )?; - } - - Ok(()) - } -} diff --git a/os/axvisor/xtask/src/image/download.rs b/os/axvisor/xtask/src/image/download.rs deleted file mode 100644 index 157743806..000000000 --- a/os/axvisor/xtask/src/image/download.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Download utilities and checksum verification. -//! -//! Provides async HTTP download with optional progress reporting and SHA256 verification. - -use std::{fs, io::Read, path::Path, time::Duration}; - -use anyhow::{Result, anyhow}; -use reqwest::Client; -use sha2::{Digest, Sha256}; -use tokio::io::{AsyncWriteExt, BufWriter}; - -/// HTTP client with 30s connect timeout and 30min total timeout. -fn http_client() -> Result { - Client::builder() - .connect_timeout(Duration::from_secs(30)) - .timeout(Duration::from_secs(1800)) - .build() - .map_err(|e| anyhow!("Failed to create HTTP client: {e}")) -} - -/// Downloads a URL and returns its body as a string. -/// -/// # Arguments -/// -/// * `url` - URL to download -pub async fn download_to_string(url: &str) -> Result { - let client = http_client()?; - let response = client.get(url).send().await?; - if !response.status().is_success() { - return Err(anyhow!("failed to download: HTTP {}", response.status())); - } - let body = response.text().await?; - Ok(body) -} - -/// Downloads a URL to a local file, creating parent directories as needed. -/// -/// # Arguments -/// -/// * `url` - URL to download -/// * `path` - Local path to write the file -/// * `progress_label` - If `Some`, prints download progress (percent/bytes) with this label -pub async fn download_to_path(url: &str, path: &Path, progress_label: Option<&str>) -> Result<()> { - let client = http_client()?; - let mut response = client.get(url).send().await?; - if !response.status().is_success() { - return Err(anyhow!("failed to download: HTTP {}", response.status())); - } - - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - - let file = tokio::fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(path) - .await?; - let mut writer = BufWriter::new(file); - - let content_length = response.content_length(); - let mut downloaded = 0u64; - - while let Some(chunk) = response.chunk().await? { - writer - .write_all(&chunk) - .await - .map_err(|e| anyhow!("Error writing to file: {e}"))?; - downloaded += chunk.len() as u64; - if let Some(label) = progress_label { - if let Some(total) = content_length { - let percent = (downloaded * 100) / total; - print!("\r{label}: {percent}% ({downloaded}/{total} bytes)"); - } else { - print!("\r{label}: {downloaded} bytes"); - } - std::io::Write::flush(&mut std::io::stdout()).unwrap(); - } - } - - println!(); - - writer - .flush() - .await - .map_err(|e| anyhow!("Error flushing file: {e}"))?; - Ok(()) -} - -/// Verifies the SHA256 checksum of a file. -/// -/// # Arguments -/// -/// * `file_path` - Path to the file to verify -/// * `expected_sha256` - Expected SHA256 checksum as lowercase hex -/// -/// # Returns -/// -/// * `Ok(true)` - Checksum matches -/// * `Ok(false)` - Checksum does not match -/// * `Err` - I/O or read error during verification -pub fn image_verify_sha256(file_path: &Path, expected_sha256: &str) -> Result { - let mut file = fs::File::open(file_path)?; - let mut hasher = Sha256::new(); - let mut buffer = [0; 8192]; - - loop { - let bytes_read = file.read(&mut buffer)?; - if bytes_read == 0 { - break; - } - hasher.update(&buffer[..bytes_read]); - } - - let result = hasher.finalize(); - let actual_sha256 = format!("{result:x}"); - - Ok(actual_sha256 == expected_sha256) -} diff --git a/os/axvisor/xtask/src/image/registry.rs b/os/axvisor/xtask/src/image/registry.rs deleted file mode 100644 index 8c3cf34ad..000000000 --- a/os/axvisor/xtask/src/image/registry.rs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Image registry data structures and parsing. -//! -//! Defines `ImageEntry` and `ImageRegistry` for the TOML-based image list format. - -use std::{collections::BTreeMap, fs, path::Path}; - -use anyhow::{Result, anyhow}; -use chrono::{DateTime, Utc}; -use regex::Regex; -use serde::{Deserialize, Serialize}; - -use super::download::download_to_string; -use super::spec::ImageSpecRef; - -/// An image entry in the image list file (one row in the registry TOML). -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct ImageEntry { - /// Unique image identifier (e.g. `qemu_x86_64_nimbos`, `evm3588_arceos`). - pub name: String, - /// Version of the image (required in registry config). - pub version: String, - /// Release timestamp (UTC). Optional. Entries without it sort earliest when resolving by name only. - /// Serialized as RFC 3339 / ISO 8601 in TOML (e.g. `2026-01-06T03:10:51Z`). - pub released_at: Option>, - /// Short human-readable description of the image. - pub description: String, - /// SHA-256 checksum of the image archive (hex string). - pub sha256: String, - /// Target architecture (e.g. `x86_64`, `aarch64`). - pub arch: String, - /// URL to download the image archive (e.g. `.tar.gz`). - pub url: String, -} - -/// An image list contains a list of [`ImageEntry`]s (top-level structure of the registry TOML). -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ImageRegistry { - /// All image entries from the registry. - pub images: Vec, -} - -/// A single entry in the `[[includes]]` array of a registry TOML. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IncludeEntry { - /// URL of another registry TOML to include and merge. - pub url: String, -} - -/// Raw registry as parsed from TOML: may contain `includes` and/or `images`. -/// Used when fetching from network; local saved registry has only `images`. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct RawRegistry { - /// Optional list of registry URLs to include and merge. - #[serde(default)] - pub includes: Vec, - /// Image entries (may be empty if registry only includes other URLs). - #[serde(default)] - pub images: Vec, -} - -/// Merges multiple image entry lists into one, deduplicating by (name, version). -/// -/// On conflict (same name+version, different other fields), prints a warning and keeps one entry. -/// -/// # Arguments -/// -/// * `sources` - Iterator of image entry vectors to merge -pub fn merge_entries(sources: impl IntoIterator>) -> Vec { - use std::collections::HashMap; - let mut by_key: HashMap<(String, String), ImageEntry> = HashMap::new(); - for entries in sources { - for entry in entries { - let key = (entry.name.clone(), entry.version.clone()); - if let Some(existing) = by_key.get(&key) { - if existing != &entry { - println!( - "Warning: conflict for image {} version {} (different description/sha256/arch/url); keeping existing entry.", - entry.name, entry.version - ); - } - continue; - } - by_key.insert(key, entry); - } - } - let mut out: Vec = by_key.into_values().collect(); - out.sort_by(|a, b| { - (a.name.as_str(), a.version.as_str()).cmp(&(b.name.as_str(), b.version.as_str())) - }); - out -} - -impl ImageRegistry { - /// Fetches a registry from `url`, resolving `[[includes]]`, and returns a merged registry. - /// - /// # Arguments - /// - /// * `url` - URL of the registry TOML to fetch - pub async fn fetch_with_includes(url: &str) -> Result { - use std::collections::{HashSet, VecDeque}; - - let mut all_sources: Vec> = Vec::new(); - let mut queue: VecDeque = VecDeque::new(); - let mut seen: HashSet = HashSet::new(); - queue.push_back(url.to_string()); - - while let Some(current_url) = queue.pop_front() { - if !seen.insert(current_url.clone()) { - continue; // already fetched, skip - } - let body = download_to_string(¤t_url).await?; - let raw: RawRegistry = toml::from_str(&body) - .map_err(|e| anyhow!("Invalid registry format at {}: {e}", current_url))?; - - all_sources.push(raw.images); - - for include in raw.includes { - queue.push_back(include.url); - } - } - - let images = merge_entries(all_sources); - Ok(ImageRegistry { images }) - } - - /// Loads the image registry from a TOML file. - /// - /// # Arguments - /// - /// * `path` - Path to the registry TOML file - pub fn load_from_file(path: &Path) -> Result { - let s = fs::read_to_string(path) - .map_err(|e| anyhow!("Failed to read image registry from {}: {e}", path.display()))?; - toml::from_str(&s).map_err(|e| anyhow!("Invalid image list format: {e}")) - } - - /// Prints the image list in a formatted table to stdout. - /// - /// # Arguments - /// - /// * `verbose` - If `true`, show each version separately; if `false`, merge same-name images and show version count - /// * `pattern` - If `Some`, filter by name: try regex match first (partial), fallback to substring - pub fn print(&self, verbose: bool, pattern: Option<&str>) { - let entries = self.filtered_entries(pattern); - if verbose { - Self::print_verbose(&entries); - } else { - Self::print_merged(&entries); - } - } - - /// Filters entries by pattern: regex match (partial) or substring. - fn filtered_entries<'a>(&'a self, pattern: Option<&str>) -> Vec<&'a ImageEntry> { - let Some(pat) = pattern else { - return self.images.iter().collect(); - }; - let re = Regex::new(pat).ok(); - self.images - .iter() - .filter(|e| match &re { - Some(r) => r.is_match(&e.name), - None => e.name.contains(pat), - }) - .collect() - } - - fn print_verbose(entries: &[&ImageEntry]) { - println!( - "{:<25} {:<12} {:<15} {:<50}", - "Name", "Version", "Architecture", "Description" - ); - println!("{}", "-".repeat(102)); - for image in entries { - println!( - "{:<25} {:<12} {:<15} {:<50}", - image.name, image.version, image.arch, image.description - ); - } - } - - fn print_merged(entries: &[&ImageEntry]) { - let by_name: BTreeMap<&str, Vec<&ImageEntry>> = - entries.iter().fold(BTreeMap::new(), |mut m, e| { - m.entry(e.name.as_str()).or_default().push(*e); - m - }); - println!( - "{:<25} {:<12} {:<15} {:<50}", - "Name", "Version", "Architecture", "Description" - ); - println!("{}", "-".repeat(102)); - for (name, vers) in by_name { - let n = vers.len(); - let version_str = if n == 1 { - "1 version".to_string() - } else { - format!("{} versions", n) - }; - let first = vers.first().unwrap(); - println!( - "{:<25} {:<12} {:<15} {:<50}", - name, version_str, first.arch, first.description - ); - } - } - - /// Returns an iterator over all image entries in the registry. - pub fn iter(&self) -> impl Iterator { - self.images.iter() - } - - /// Looks up an image by spec (name and optional version). - /// - /// When version is `Some`, returns the exact match. When `None`, returns the entry with the - /// latest `released_at`; entries without `released_at` sort earliest. - /// - /// # Arguments - /// - /// * `spec` - Image spec (name and optional version) - pub fn find(&self, spec: ImageSpecRef<'_>) -> Option<&ImageEntry> { - match spec.version { - Some(v) => self.iter().find(|e| e.name == spec.name && e.version == v), - None => self - .iter() - .filter(|e| e.name == spec.name) - .max_by(|a, b| a.released_at.cmp(&b.released_at)), - } - } -} diff --git a/os/axvisor/xtask/src/image/spec.rs b/os/axvisor/xtask/src/image/spec.rs deleted file mode 100644 index 39b6eda26..000000000 --- a/os/axvisor/xtask/src/image/spec.rs +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Image spec: owned and reference types for (name, version). -//! -//! Parses specs as `name` or `name:version` (ASCII colon) and provides -//! [`ImageSpec`] (owned) and [`ImageSpecRef`] (reference) with common trait impls. - -use std::fmt; - -/// Owned image spec: name and optional version. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[allow(dead_code)] -pub struct ImageSpec { - /// Image name (e.g. `evm3588_arceos`). - pub name: String, - /// Image version; `None` means "latest" or default path. - pub version: Option, -} - -/// Reference image spec: borrowed name and optional version. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct ImageSpecRef<'a> { - /// Image name. - pub name: &'a str, - /// Image version; `None` means "latest" or default path. - pub version: Option<&'a str>, -} - -#[allow(dead_code)] -impl ImageSpec { - /// Parses a spec string: `name` or `name:version` (ASCII colon). - /// - /// # Arguments - /// - /// * `s` - Spec string (e.g. `evm3588_arceos` or `evm3588_arceos:0.0.22`) - pub fn parse(s: &str) -> ImageSpec { - let r = ImageSpecRef::parse(s); - r.to_owned() - } - - /// Creates an owned spec from a reference spec. - /// - /// # Arguments - /// - /// * `r` - Reference spec to clone - pub fn from_ref(r: ImageSpecRef<'_>) -> ImageSpec { - ImageSpec { - name: r.name.to_string(), - version: r.version.map(String::from), - } - } - - /// Borrows this spec as an [`ImageSpecRef`]. - pub fn as_ref(&self) -> ImageSpecRef<'_> { - ImageSpecRef { - name: &self.name, - version: self.version.as_deref(), - } - } -} - -impl<'a> ImageSpecRef<'a> { - /// Parses a spec string: `name` or `name:version` (ASCII colon). - /// - /// The returned spec borrows from `s`. - /// - /// # Arguments - /// - /// * `s` - Spec string (e.g. `evm3588_arceos` or `evm3588_arceos:0.0.22`) - pub fn parse(s: &'a str) -> ImageSpecRef<'a> { - match s.split_once(':') { - Some((name, version)) => ImageSpecRef { - name, - version: Some(version), - }, - None => ImageSpecRef { - name: s, - version: None, - }, - } - } - - /// Converts to an owned [`ImageSpec`]. - pub fn to_owned(self) -> ImageSpec { - ImageSpec::from_ref(self) - } -} - -impl fmt::Display for ImageSpec { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.version { - Some(v) => write!(f, "{}:{}", self.name, v), - None => write!(f, "{}", self.name), - } - } -} - -impl fmt::Display for ImageSpecRef<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.version { - Some(v) => write!(f, "{}:{}", self.name, v), - None => write!(f, "{}", self.name), - } - } -} - -impl From> for ImageSpec { - fn from(r: ImageSpecRef<'_>) -> Self { - ImageSpec::from_ref(r) - } -} - -impl<'a> From<&'a str> for ImageSpecRef<'a> { - fn from(s: &'a str) -> Self { - ImageSpecRef::parse(s) - } -} - -impl<'a> From<&'a String> for ImageSpecRef<'a> { - fn from(s: &'a String) -> Self { - ImageSpecRef::parse(s.as_str()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_name_only() { - let r = ImageSpecRef::parse("evm3588_arceos"); - assert_eq!(r.name, "evm3588_arceos"); - assert_eq!(r.version, None); - } - - #[test] - fn parse_name_and_version() { - let r = ImageSpecRef::parse("evm3588_arceos:0.0.22"); - assert_eq!(r.name, "evm3588_arceos"); - assert_eq!(r.version, Some("0.0.22")); - } - - #[test] - fn display_ref() { - assert_eq!(ImageSpecRef::parse("a").to_string(), "a"); - assert_eq!(ImageSpecRef::parse("a:b").to_string(), "a:b"); - } - - #[test] - fn as_ref() { - let spec = ImageSpec::parse("x:1"); - let r = spec.as_ref(); - assert_eq!(r.name, "x"); - assert_eq!(r.version, Some("1")); - } -} diff --git a/os/axvisor/xtask/src/image/storage.rs b/os/axvisor/xtask/src/image/storage.rs deleted file mode 100644 index 0b42c49dd..000000000 --- a/os/axvisor/xtask/src/image/storage.rs +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Local image storage management. -//! -//! Provides `Storage` for managing a local image directory and its registry index. - -use std::path::Path; -use std::time::{SystemTime, UNIX_EPOCH}; -use std::{fs, path::PathBuf}; - -use anyhow::{Result, anyhow}; - -use super::config::ImageConfig; -use super::download::{download_to_path, image_verify_sha256}; -use super::registry::{ImageEntry, ImageRegistry}; -use super::spec::ImageSpecRef; - -/// Filename of the image registry index inside the local storage directory. -pub const REGISTRY_FILENAME: &str = "images.toml"; - -/// Filename storing the last sync timestamp (Unix seconds) inside the local storage directory. -const LAST_SYNC_FILENAME: &str = ".last_sync"; - -// ----------------------------------------------------------------------------- -// Path and naming helpers (free functions, no Storage instance needed) -// ----------------------------------------------------------------------------- - -/// Returns the path to the registry index file within a storage directory. -/// -/// # Arguments -/// -/// * `storage_path` - Root path of the local image storage -pub fn registry_filepath(storage_path: &Path) -> PathBuf { - storage_path.join(REGISTRY_FILENAME) -} - -/// Path to `.last_sync` file in the storage directory. -fn last_sync_filepath(storage_path: &Path) -> PathBuf { - storage_path.join(LAST_SYNC_FILENAME) -} - -/// Current time as Unix timestamp (seconds). -fn current_unix_timestamp() -> Result { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|e| anyhow!("System time error: {e}")) - .map(|d| d.as_secs()) -} - -/// Reads the last sync timestamp from `.last_sync`; returns `None` if missing or invalid. -fn read_last_sync_time(storage_path: &Path) -> Option { - let path = last_sync_filepath(storage_path); - if !path.exists() { - return None; - } - let s = match fs::read_to_string(&path) { - Ok(s) => s, - Err(e) => { - println!( - "Note: could not read last sync file {}: {e}; treating as no previous sync.", - path.display() - ); - return None; - } - }; - let s = s.trim(); - if s.is_empty() { - return None; - } - match s.parse::() { - Ok(ts) => Some(ts), - Err(_) => { - println!( - "Note: last sync file {} has invalid content; treating as no previous sync.", - path.display() - ); - None - } - } -} - -/// Writes the current timestamp to `.last_sync`. -fn write_last_sync_time(storage_path: &Path) -> Result<()> { - let now = current_unix_timestamp()?; - let path = last_sync_filepath(storage_path); - fs::write(&path, now.to_string()).map_err(|e| anyhow!("Failed to write last sync file: {e}")) -} - -/// Canonical archive filename for an image: `{name}.tar.gz` or `{name}-{version}.tar.gz`. -/// -/// # Arguments -/// -/// * `spec` - Image spec (name and optional version) -pub fn image_archive_filename(spec: ImageSpecRef<'_>) -> String { - match spec.version { - Some(v) => format!("{}-{}.tar.gz", spec.name, v), - None => format!("{}.tar.gz", spec.name), - } -} - -/// Canonical extract directory name for an image: `{name}` or `{name}-{version}`. -/// -/// # Arguments -/// -/// * `spec` - Image spec (name and optional version) -pub fn image_extract_dir_name(spec: ImageSpecRef<'_>) -> String { - match spec.version { - Some(v) => format!("{}-{}", spec.name, v), - None => spec.name.to_string(), - } -} - -/// Returns the path where an image archive (`.tar.gz`) would be stored. -pub fn image_path(storage_path: &Path, spec: ImageSpecRef<'_>) -> PathBuf { - storage_path.join(image_archive_filename(spec)) -} - -/// Local image storage backed by a directory and an image registry index. -pub struct Storage { - /// Root path of the local image storage directory. - pub path: PathBuf, - /// Parsed image registry (list of available images). - pub image_registry: ImageRegistry, -} - -// ----------------------------------------------------------------------------- -// Construction -// ----------------------------------------------------------------------------- - -impl Storage { - /// Creates a storage instance from an existing local directory. - /// - /// Loads the image registry from `images.toml` in the storage path. - /// - /// # Arguments - /// - /// * `path` - Path to the local storage directory (must contain `images.toml`) - /// - /// # Returns - /// - /// * `Ok(Self)` - Storage loaded successfully - /// * `Err` - Directory or registry file read/parse error - pub fn new(path: PathBuf) -> Result { - let registry_filepath = registry_filepath(&path); - let image_registry = ImageRegistry::load_from_file(®istry_filepath)?; - Ok(Self { - path, - image_registry, - }) - } - - /// Creates storage by downloading the registry index from the remote URL. This method does not - /// affect existing images in the local storage. - /// - /// If the registry TOML contains `[[includes]]`, those URLs are fetched recursively - /// and merged (deduplicated by name+version). The local saved registry has no `includes`. - /// - /// # Arguments - /// - /// * `registry` - URL of the registry TOML file to download - /// * `path` - Path to the local storage directory - /// - /// # Returns - /// - /// * `Ok(Self)` - Registry downloaded and storage created - /// * `Err` - Download, directory creation, or parse error - pub async fn new_from_registry(registry: String, path: PathBuf) -> Result { - fs::create_dir_all(&path).map_err(|e| anyhow!("Failed to create directory: {e}"))?; - - let registry_filepath = registry_filepath(&path); - - let image_registry = ImageRegistry::fetch_with_includes(®istry).await?; - let toml_content = toml::to_string_pretty(&image_registry) - .map_err(|e| anyhow!("Failed to serialize registry: {e}"))?; - fs::write(®istry_filepath, toml_content) - .map_err(|e| anyhow!("Failed to write registry file: {e}"))?; - write_last_sync_time(&path)?; - - println!("Image list saved to {}", registry_filepath.display()); - - Ok(Self { - path, - image_registry, - }) - } - - /// Creates storage, falling back to syncing from the remote registry if local load fails. - /// When local load succeeds and `auto_sync_threshold` is non-zero, checks the last sync - /// time (stored in `.last_sync` under the storage path) and syncs from the remote registry - /// if the threshold in seconds has been exceeded (or no last sync time exists). - /// - /// # Arguments - /// - /// * `path` - Path to the local storage directory - /// * `registry` - URL of the remote registry to sync from when local storage is invalid or stale - /// * `auto_sync_threshold` - Seconds since last sync before auto-updating; 0 means never update when load succeeds - /// - /// # Returns - /// - /// * `Ok(Self)` - Storage from local dir or from synced registry - /// * `Err` - Both local load and sync failed - pub async fn new_with_auto_sync( - path: PathBuf, - registry: String, - auto_sync_threshold: u64, - ) -> Result { - let storage = match Self::new(path.clone()) { - Ok(storage) => storage, - Err(e) => { - println!("Error while loading local storage: {e}"); - println!("Auto syncing from registry {registry}..."); - let storage = Self::new_from_registry(registry, path).await?; - return Ok(storage); - } - }; - - if auto_sync_threshold == 0 { - return Ok(storage); - } - - let now = current_unix_timestamp()?; - let last_sync = read_last_sync_time(&storage.path); - let need_sync = match last_sync { - None => true, - Some(ts) => now.saturating_sub(ts) >= auto_sync_threshold, - }; - - if !need_sync { - return Ok(storage); - } - - println!( - "Last sync was {} (threshold: {}s). Auto syncing from registry {registry}...", - last_sync - .map(|ts| format!("{}s ago", now - ts)) - .unwrap_or_else(|| "never".to_string()), - auto_sync_threshold - ); - - // backup registry file so we can restore on sync failure. - let registry_path = registry_filepath(&storage.path); - let registry_backup = fs::read_to_string(®istry_path) - .map_err(|e| anyhow!("Failed to read registry file: {e}"))?; - - match Self::new_from_registry(registry, path).await { - Ok(new_storage) => Ok(new_storage), - Err(e) => { - println!("Auto sync failed: {e}"); - println!("Restoring previous registry and using existing storage."); - - fs::write(®istry_path, registry_backup) - .map_err(|e| anyhow!("Failed to write registry file: {e}"))?; - - Ok(storage) - } - } - } - - /// Creates storage from config, optionally auto-syncing when local storage is invalid. - /// - /// # Arguments - /// - /// * `config` - Image config (storage path, registry URL, auto-sync settings) - /// - /// # Returns - /// - /// * `Ok(Self)` - Storage loaded or synced according to config - /// * `Err` - Load or sync failed - pub async fn new_from_config(config: &ImageConfig) -> Result { - if config.auto_sync { - Self::new_with_auto_sync( - config.local_storage.clone(), - config.registry.clone(), - config.auto_sync_threshold, - ) - .await - } else { - Self::new(config.local_storage.clone()) - } - } -} - -// ----------------------------------------------------------------------------- -// Download and remove -// ----------------------------------------------------------------------------- - -impl Storage { - /// Resolves an image by name and optional version (latest by `released_at` when version is `None`). - fn resolve_image(&self, spec: ImageSpecRef<'_>) -> Option<&ImageEntry> { - self.image_registry.find(spec) - } - - /// Downloads an image into the given directory and verifies its SHA256 checksum. - /// The output filename is derived from the image spec (see [`image_archive_filename`]). - /// - /// Skips download if the file already exists and matches the expected checksum. - /// Re-downloads on checksum mismatch. - /// - /// # Arguments - /// - /// * `spec` - Image spec (name and optional version) - /// * `output_dir` - Directory to write the `.tar.gz` file into (created if missing) - /// - /// # Returns - /// - /// * `Ok(PathBuf)` - Full path to the downloaded (or existing) image file - /// * `Err` - Image not found, download failed, or checksum verification failed - pub async fn download_image_to( - &self, - spec: ImageSpecRef<'_>, - output_dir: &Path, - ) -> Result { - let image = self.resolve_image(spec).ok_or_else(|| { - anyhow!( - "Image not found: {}{}. Use 'xtask image ls' to view available images", - spec.name, - spec.version - .map(|v| format!(" version {}", v)) - .unwrap_or_default() - ) - })?; - - fs::create_dir_all(output_dir) - .map_err(|e| anyhow!("Failed to create output directory: {e}"))?; - - let output_path = output_dir.join(image_archive_filename(spec)); - - if output_path.is_dir() { - return Err(anyhow!( - "Output path is a directory: {}", - output_path.display() - )); - } - - if output_path.exists() { - match image_verify_sha256(&output_path, &image.sha256) { - Ok(true) => { - println!("Image already exists and verified"); - return Ok(output_path); - } - Ok(false) => { - println!("Existing image verification failed"); - } - Err(e) => { - println!("Error verifying existing image: {e}"); - } - } - - println!("Removing existing image for re-downloading..."); - let _ = fs::remove_file(&output_path); - } - - println!("Downloading: {}", image.url); - - download_to_path(&image.url, &output_path, Some("Downloading")).await?; - - match image_verify_sha256(&output_path, &image.sha256) { - Ok(true) => { - println!("Download completed and verified successfully"); - Ok(output_path) - } - Ok(false) => { - let err = - anyhow!("Image downloaded but verification failed: SHA256 verification failed"); - println!("{err}"); - let _ = fs::remove_file(&output_path); - Err(err) - } - Err(e) => { - let err = - anyhow!("Image downloaded but verification failed: Error verifying image: {e}"); - println!("{err}"); - let _ = fs::remove_file(&output_path); - Err(err) - } - } - } - - /// Downloads an image to the default location in local storage. - /// - /// Equivalent to `download_image_to(spec, &self.path)`. - /// - /// # Returns - /// - /// * `Ok(PathBuf)` - Full path to the downloaded (or existing) image file - /// * `Err` - Same as [`download_image_to`](Self::download_image_to) - pub async fn download_image(&self, spec: ImageSpecRef<'_>) -> Result { - self.download_image_to(spec, &self.path).await - } - - /// Removes an image from local storage (archive and extracted directory). - /// - /// # Arguments - /// - /// * `spec` - Image spec (name and optional version) - /// - /// # Returns - /// - /// `true` if at least one file or directory was removed, `false` if none found - pub async fn remove_image(&self, spec: ImageSpecRef<'_>) -> Result { - let mut anything_removed = false; - let output_path = image_path(&self.path, spec); - if output_path.exists() { - fs::remove_file(&output_path)?; - anything_removed = true; - } - let extract_dir = self.path.join(image_extract_dir_name(spec)); - if extract_dir.exists() { - fs::remove_dir_all(&extract_dir)?; - anything_removed = true; - } - Ok(anything_removed) - } -} diff --git a/os/axvisor/xtask/src/main.rs b/os/axvisor/xtask/src/main.rs deleted file mode 100644 index 99d799799..000000000 --- a/os/axvisor/xtask/src/main.rs +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Build tool for Axvisor hypervisor. -//! -//! This crate provides the `xtask` binary for building, running, and managing -//! the Axvisor hypervisor project. - -#![cfg_attr(not(any(windows, unix)), no_main)] -#![cfg_attr(not(any(windows, unix)), no_std)] -#![cfg(any(windows, unix))] - -use anyhow::{Context, Result, anyhow}; -use chrono::Utc; -use clap::{Args, Parser, Subcommand}; -use std::fs; -use std::path::{Path, PathBuf}; - -mod cargo; -mod clippy; -mod ctx; -mod devspace; -mod image; -mod menuconfig; -mod tbuld; -mod vmconfig; - -#[derive(Parser)] -#[command(name = "xtask")] -#[command(about = "ArceOS build configuration management tool")] -struct Cli { - #[command(subcommand)] - command: Commands, -} - -#[derive(Subcommand)] -enum Commands { - /// Set default build configuration from board configs - Defconfig { - /// Board configuration name (e.g., qemu-aarch64, orangepi-5-plus, phytiumpi) - board_name: String, - }, - /// Build the ArceOS project with current configuration - Build(BuildArgs), - /// Run clippy checks across all targets and feature combinations - Clippy(ClippyArgs), - /// Run ArceOS in QEMU emulation environment - Qemu(QemuArgs), - /// Run ArceOS with U-Boot bootloader - Uboot(UbootArgs), - /// Generate VM configuration schema - Vmconfig, - /// Interactive menu-based configuration editor - Menuconfig, - /// Guest Image management - Image(image::ImageArgs), - /// Manage local devspace dependencies - Devspace(DevspaceArgs), -} - -#[derive(Parser)] -struct QemuArgs { - /// Path to custom build configuration file (TOML format) - #[arg(long)] - build_config: Option, - - /// Path to custom QEMU configuration file - #[arg(long)] - qemu_config: Option, - - /// Comma-separated list of VM configuration files - #[arg(long)] - vmconfigs: Vec, - - #[command(flatten)] - build: BuildArgs, -} - -#[derive(Parser)] -struct ClippyArgs { - /// Only check specific packages (comma separated) - #[arg(long)] - packages: Option, - - /// Only check specific targets (comma separated) - #[arg(long)] - targets: Option, - - /// Continue on error instead of exiting immediately - #[arg(long)] - continue_on_error: bool, - - /// Dry run - show what would be checked without running clippy - #[arg(long)] - dry_run: bool, - - /// Automatically fix clippy warnings where possible - #[arg(long)] - fix: bool, - - /// Allow fixing when the working directory is dirty (has uncommitted changes) - #[arg(long)] - allow_dirty: bool, -} - -#[derive(Parser)] -struct UbootArgs { - /// Path to custom build configuration file (TOML format) - #[arg(long)] - build_config: Option, - - /// Path to custom U-Boot configuration file - #[arg(long)] - uboot_config: Option, - - /// Comma-separated list of VM configuration files - #[arg(long)] - vmconfigs: Vec, - - #[command(flatten)] - build: BuildArgs, -} - -#[derive(Args)] -struct BuildArgs { - #[arg(long)] - build_dir: Option, - #[arg(long)] - bin_dir: Option, -} - -#[derive(Args)] -struct DevspaceArgs { - #[command(subcommand)] - action: DevspaceCommand, -} - -#[derive(Subcommand)] -enum DevspaceCommand { - /// Start the development workspace - Start, - /// Stop the development workspace - Stop, -} - -#[tokio::main] -async fn main() -> Result<()> { - let cli = Cli::parse(); - - let mut ctx = ctx::Context::new(); - - match cli.command { - Commands::Defconfig { board_name } => { - defconfig_command(&board_name)?; - } - Commands::Build(args) => { - println!("Building the project..."); - ctx.apply_build_args(&args); - ctx.run_build().await?; - println!("Build completed successfully."); - } - Commands::Clippy(args) => { - clippy::run_clippy(args)?; - } - Commands::Qemu(args) => { - ctx.apply_build_args(&args.build); - ctx.vmconfigs = args.vmconfigs; - ctx.build_config_path = args.build_config; - ctx.run_qemu(args.qemu_config).await?; - } - Commands::Uboot(args) => { - ctx.apply_build_args(&args.build); - ctx.vmconfigs = args.vmconfigs; - ctx.build_config_path = args.build_config; - ctx.run_uboot(args.uboot_config).await?; - } - Commands::Vmconfig => { - ctx.run_vmconfig().await?; - } - Commands::Menuconfig => { - ctx.run_menuconfig().await?; - } - Commands::Image(args) => { - image::run_image(args).await?; - } - Commands::Devspace(args) => match args.action { - DevspaceCommand::Start => devspace::start()?, - DevspaceCommand::Stop => devspace::stop()?, - }, - } - - Ok(()) -} - -fn defconfig_command(board_name: &str) -> Result<()> { - println!("Setting default configuration for board: {board_name}"); - - // Validate board configuration exists - let board_config_path = format!("configs/board/{board_name}.toml"); - if !Path::new(&board_config_path).exists() { - return Err(anyhow!( - "Board configuration '{board_name}' not found. Available boards: qemu-aarch64, orangepi-5-plus" - )); - } - - // Backup existing .build.toml if it exists - backup_existing_config()?; - - // Copy board configuration to .build.toml - copy_board_config(board_name)?; - - println!("Successfully set default configuration to: {board_name}"); - Ok(()) -} - -fn backup_existing_config() -> Result<()> { - let build_config_path = ".build.toml"; - - if Path::new(build_config_path).exists() { - let timestamp = Utc::now().format("%Y%m%d_%H%M%S"); - let backup_path = format!("{build_config_path}.backup_{timestamp}"); - - fs::copy(build_config_path, &backup_path) - .with_context(|| format!("Failed to backup {build_config_path} to {backup_path}"))?; - - println!("Backed up existing configuration to: {backup_path}"); - } - - Ok(()) -} - -fn copy_board_config(board_name: &str) -> Result<()> { - let source_path = format!("configs/board/{board_name}.toml"); - let target_path = ".build.toml"; - - fs::copy(&source_path, target_path) - .with_context(|| format!("Failed to copy {source_path} to {target_path}"))?; - - println!("Copied board configuration from: {source_path}"); - Ok(()) -} diff --git a/os/axvisor/xtask/src/menuconfig.rs b/os/axvisor/xtask/src/menuconfig.rs deleted file mode 100644 index 0a6604edb..000000000 --- a/os/axvisor/xtask/src/menuconfig.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::ctx::Context; -use crate::tbuld::Config; -use jkconfig::{ElemHock, ui::components::editors::show_feature_select}; -use std::sync::Arc; // HashMap is unused - -impl Context { - /// Main menuconfig runner function - pub async fn run_menuconfig(&mut self) -> anyhow::Result<()> { - println!("Configure runtime parameters"); - - let config_path = self.ctx.paths.workspace.join(".build.toml"); - if config_path.exists() { - println!( - "\nCurrent .build.toml configuration file: {}", - config_path.display() - ); - } else { - println!("\nNo .build.toml configuration file found, will use default configuration"); - } - - let Some(_c): Option = - jkconfig::run(config_path, true, &[self.default_package_feature_select()]).await? - else { - return Err(anyhow::anyhow!("Menuconfig was cancelled")); - }; - Ok(()) - } - - pub fn default_package_feature_select(&self) -> ElemHock { - let path = "features"; - let package_name = "axvisor".to_string(); - - let cargo_toml = self.ctx.paths.workspace.join("Cargo.toml"); - ElemHock { - path: path.to_string(), - callback: Arc::new(move |siv: &mut jkconfig::cursive::Cursive, _path: &str| { - show_feature_select(siv, &package_name, &cargo_toml, None); - }), - } - } -} diff --git a/os/axvisor/xtask/src/tbuld.rs b/os/axvisor/xtask/src/tbuld.rs deleted file mode 100644 index 6b7f85b27..000000000 --- a/os/axvisor/xtask/src/tbuld.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::path::PathBuf; - -use anyhow::Context as _; -use ostool::build::config::{Cargo, LogLevel}; -use schemars::{JsonSchema, schema_for}; -use serde::{Deserialize, Serialize}; - -use crate::ctx::Context; - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] -pub struct Config { - /// target triple - pub target: String, - /// features to enable - pub features: Vec, - /// log level feature - pub log: Option, - /// other cargo args - pub cargo_args: Vec, - /// whether to output as binary - pub to_bin: bool, - pub smp: Option, - pub vm_configs: Vec, -} - -impl Context { - pub fn load_config(&mut self) -> anyhow::Result { - let json = schema_for!(Config); - - let mut config_path = self.ctx.paths.workspace.join(".build.toml"); - if let Some(c) = &self.build_config_path { - config_path = c.clone(); - } - - std::fs::write( - config_path.parent().unwrap().join(".build-schema.json"), - serde_json::to_string_pretty(&json).unwrap(), - ) - .with_context(|| "Failed to write schema file .build-schema.json")?; - - let config_str = std::fs::read_to_string(&config_path) - .with_context(|| format!("Failed to read config file: {}", config_path.display()))?; - let config: Config = toml::from_str(&config_str) - .with_context(|| format!("Failed to parse config file: {}", config_path.display()))?; - - self.ctx.build_config_path = Some(config_path); - - let mut vm_configs = config.vm_configs.to_vec(); - vm_configs.extend(self.vmconfigs.iter().cloned()); - - let mut vm_config_paths = vec![]; - for vm_config in &vm_configs { - let mut vm_config = PathBuf::from(vm_config); - if !vm_config.is_absolute() { - vm_config = self.ctx.paths.workspace.join(vm_config); - } - if !vm_config.exists() { - return Err(anyhow::anyhow!( - "VM config file '{}' does not exist.", - vm_config.display() - )); - } - vm_config_paths.push(vm_config); - } - - let log_level = config - .log - .as_ref() - .map(|l| format!("{:?}", l).to_lowercase()); - - let mut cargo = Cargo { - target: config.target, - package: "axvisor".to_string(), - features: config.features, - log: config.log, - args: vec!["--bin".to_string(), "axvisor".to_string()], - to_bin: config.to_bin, - ..Default::default() - }; - cargo.args.extend(config.cargo_args); - - if let Some(smp) = config.smp { - cargo.env.insert("AXVISOR_SMP".to_string(), smp.to_string()); - } - - if let Some(log_level) = log_level { - cargo.env.insert("AX_LOG".to_string(), log_level); - } - - if !vm_config_paths.is_empty() { - let value = std::env::join_paths(&vm_config_paths) - .map_err(|e| anyhow::anyhow!("Failed to join VM config paths: {e}"))? - .to_string_lossy() - .into_owned(); - cargo.env.insert("AXVISOR_VM_CONFIGS".to_string(), value); - } - - Ok(cargo) - } - - pub async fn run_build(&mut self) -> anyhow::Result<()> { - let config = self.load_config()?; - self.ctx.cargo_build(&config).await?; - - Ok(()) - } -} diff --git a/os/axvisor/xtask/src/vmconfig.rs b/os/axvisor/xtask/src/vmconfig.rs deleted file mode 100644 index 5732d07bc..000000000 --- a/os/axvisor/xtask/src/vmconfig.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use anyhow::Context as _; - -use crate::ctx::Context; - -impl Context { - pub async fn run_vmconfig(&mut self) -> anyhow::Result<()> { - let json = schemars::schema_for!(axvmconfig::AxVMCrateConfig); - std::fs::write( - ".vmconfig-schema.json", - serde_json::to_string_pretty(&json).unwrap(), - ) - .with_context(|| "Failed to write schema file .vmconfig-schema.json")?; - Ok(()) - } -} From 8bbd84657a32d2cfe28ad45347925df649887fa1 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Sat, 21 Mar 2026 02:24:39 +0800 Subject: [PATCH 129/132] fix aarch64 axvisor --- components/axvm/src/vm.rs | 2 +- os/axvisor/.cargo/config.toml | 15 ++++++++------- os/axvisor/.github/workflows/qemu-aarch64.toml | 4 ++-- os/axvisor/configs/board/qemu-aarch64.toml | 8 ++------ .../configs/vms/linux-aarch64-qemu-smp1.toml | 2 +- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/components/axvm/src/vm.rs b/components/axvm/src/vm.rs index 6eda0795f..d8bf3dfd1 100644 --- a/components/axvm/src/vm.rs +++ b/components/axvm/src/vm.rs @@ -29,7 +29,7 @@ use spin::{Mutex, Once}; #[cfg(not(target_arch = "x86_64"))] use crate::vcpu::AxVCpuCreateConfig; #[cfg(target_arch = "aarch64")] -use crate::vcpu::{AxVCpuCreateConfig, get_sysreg_device}; +use crate::vcpu::get_sysreg_device; use crate::{ config::{AxVMConfig, PhysCpuList}, hal::PagingHandlerImpl, diff --git a/os/axvisor/.cargo/config.toml b/os/axvisor/.cargo/config.toml index ab9edbbb0..4bdaa6e66 100644 --- a/os/axvisor/.cargo/config.toml +++ b/os/axvisor/.cargo/config.toml @@ -9,13 +9,14 @@ rustflags = [ "-Clink-args=-Tlink.x", ] -[target.aarch64-unknown-none-softfloat] -rustflags = [ - "-Crelocation-model=pic", - "-Clink-args=-pie", - "-Clink-args=-znostart-stop-gc", - "-Clink-args=-Taxplat.x", -] +# Comment out because it's specified by runner in tgoskits +# [target.aarch64-unknown-none-softfloat] +# rustflags = [ +# "-Crelocation-model=pic", +# "-Clink-args=-pie", +# "-Clink-args=-znostart-stop-gc", +# "-Clink-args=-Taxplat.x", +# ] [target.riscv64gc-unknown-none-elf] rustflags = [ diff --git a/os/axvisor/.github/workflows/qemu-aarch64.toml b/os/axvisor/.github/workflows/qemu-aarch64.toml index 8ecf3ebfb..7b147d465 100644 --- a/os/axvisor/.github/workflows/qemu-aarch64.toml +++ b/os/axvisor/.github/workflows/qemu-aarch64.toml @@ -8,8 +8,8 @@ args = [ "4", "-device", "virtio-blk-device,drive=disk0", - "-drive", - "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", + # "-drive", + # "id=disk0,if=none,format=raw,file=${workspaceFolder}/tmp/rootfs.img", "-append", "root=/dev/vda rw init=/init", "-m", diff --git a/os/axvisor/configs/board/qemu-aarch64.toml b/os/axvisor/configs/board/qemu-aarch64.toml index 77eeb6781..67f4aca43 100644 --- a/os/axvisor/configs/board/qemu-aarch64.toml +++ b/os/axvisor/configs/board/qemu-aarch64.toml @@ -1,11 +1,7 @@ cargo_args = [ - "--config", - 'target.aarch64-unknown-none-softfloat.rustflags = ["-Crelocation-model=pic", "-Clink-args=-pie", "-Clink-args=-znostart-stop-gc", "-Clink-args=-Taxplat.x"]', -] -# cargo_args = [ # "--config", -# 'target.aarch64-unknown-none-softfloat.rustflags = ["-Clink-args=-Taxplat.x"]', -# ] +# 'target.aarch64-unknown-none-softfloat.rustflags = ["-Crelocation-model=pic", "-Clink-args=-pie", "-Clink-args=-znostart-stop-gc", "-Clink-args=-Taxplat.x"]', +] features = [ "ept-level-4", "axstd/bus-mmio", diff --git a/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml b/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml index 6e40040d5..b9b90e6c4 100644 --- a/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml +++ b/os/axvisor/configs/vms/linux-aarch64-qemu-smp1.toml @@ -23,7 +23,7 @@ entry_point = 0x8020_0000 image_location = "memory" # The file path of the kernel image. # kernel_path = "linux-6.6.62.bin" -kernel_path = "/tmp/axvisor/qemu_aarch64_linux/qemu-aarch64" +kernel_path = "/tmp/.axvisor-images/qemu_aarch64_linux/qemu-aarch64" # The load address of the kernel image. kernel_load_addr = 0x8020_0000 # The file path of the device tree blob (DTB). From 553e46b1256b123ad902574990103a1c86a6d18f Mon Sep 17 00:00:00 2001 From: aarkegz Date: Sat, 21 Mar 2026 02:34:17 +0800 Subject: [PATCH 130/132] update `crate_interface` to fix starryOS error --- Cargo.lock | 2 +- components/kernel_guard/Cargo.toml | 2 +- os/arceos/Cargo.toml | 2 +- scripts/repo/repos.csv | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 441d592d1..797598e59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4175,7 +4175,7 @@ name = "kernel_guard" version = "0.1.3" dependencies = [ "cfg-if", - "crate_interface 0.1.4", + "crate_interface 0.3.0", ] [[package]] diff --git a/components/kernel_guard/Cargo.toml b/components/kernel_guard/Cargo.toml index c988af88f..0be3376c6 100644 --- a/components/kernel_guard/Cargo.toml +++ b/components/kernel_guard/Cargo.toml @@ -17,4 +17,4 @@ default = [] [dependencies] cfg-if = "1.0" -crate_interface = "0.1" +crate_interface = "0.3" diff --git a/os/arceos/Cargo.toml b/os/arceos/Cargo.toml index 33ed3f775..ac13f588d 100644 --- a/os/arceos/Cargo.toml +++ b/os/arceos/Cargo.toml @@ -125,7 +125,7 @@ bindgen = "0.72" buddy-slab-allocator = "0.2" cfg-if = "1.0" chrono = { version = "0.4", default-features = false } -crate_interface = "0.1" +crate_interface = "0.3" ctor_bare = "0.2" event-listener = { version = "5.4.0", default-features = false } kernel_guard = "0.1" diff --git a/scripts/repo/repos.csv b/scripts/repo/repos.csv index 29f87e941..cc4802e68 100644 --- a/scripts/repo/repos.csv +++ b/scripts/repo/repos.csv @@ -56,3 +56,4 @@ https://github.com/Starry-OS/starry-signal,,components/starry-signal,Starry, https://github.com/Starry-OS/starry-vm,,components/starry-vm,Starry, https://github.com/Starry-OS/smoltcp,,components/starry-smoltcp,Starry, https://github.com/arceos-org/axfs_crates,main,components/axfs_crates,ArceOS, +https://github.com/elliott10/fxmac_rs.git,,components/fxmac_rs,ArceOS, From 8d4dbb88f5fc5923080ddee69d5dea845b17f26a Mon Sep 17 00:00:00 2001 From: aarkegz Date: Sat, 21 Mar 2026 02:40:06 +0800 Subject: [PATCH 131/132] fix `fxmac_rs` fork repo addr --- scripts/repo/repos.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/repo/repos.csv b/scripts/repo/repos.csv index cc4802e68..ea2d802c6 100644 --- a/scripts/repo/repos.csv +++ b/scripts/repo/repos.csv @@ -56,4 +56,4 @@ https://github.com/Starry-OS/starry-signal,,components/starry-signal,Starry, https://github.com/Starry-OS/starry-vm,,components/starry-vm,Starry, https://github.com/Starry-OS/smoltcp,,components/starry-smoltcp,Starry, https://github.com/arceos-org/axfs_crates,main,components/axfs_crates,ArceOS, -https://github.com/elliott10/fxmac_rs.git,,components/fxmac_rs,ArceOS, +https://github.com/DeathWish5/fxmac_rs,,components/fxmac_rs,ArceOS, From c52ba021bf6ed7eb6a7c15e623b265f522344edd Mon Sep 17 00:00:00 2001 From: aarkegz Date: Sat, 21 Mar 2026 02:43:17 +0800 Subject: [PATCH 132/132] Update `crate_interface` version to 0.3 across dependencies and adjust `fxmac_rs` to use the new version in its configuration. --- Cargo.lock | 35 +++++++++++----------------------- Cargo.toml | 3 +++ components/fxmac_rs/Cargo.toml | 2 +- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 797598e59..a3357cd28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -779,7 +779,7 @@ dependencies = [ "axhal", "axplat-dyn", "cfg-if", - "crate_interface 0.3.0", + "crate_interface", "log", "smallvec", ] @@ -1097,7 +1097,7 @@ dependencies = [ "axlog", "cfg-if", "chrono", - "crate_interface 0.3.0", + "crate_interface", "kspin", "log", ] @@ -1169,7 +1169,7 @@ dependencies = [ "axplat-macros", "bitflags 2.11.0", "const-str", - "crate_interface 0.3.0", + "crate_interface", "handler_table 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kspin", "memory_addr", @@ -1411,7 +1411,7 @@ dependencies = [ "axtask", "cfg-if", "chrono", - "crate_interface 0.3.0", + "crate_interface", "ctor_bare", "indoc", "percpu", @@ -1471,7 +1471,7 @@ dependencies = [ "axtask", "cfg-if", "cpumask 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crate_interface 0.3.0", + "crate_interface", "event-listener", "extern-trait", "futures-util", @@ -1576,7 +1576,7 @@ dependencies = [ "clap", "colored", "cpumask 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crate_interface 0.3.0", + "crate_interface", "extern-trait", "fdt-parser", "flate2", @@ -1625,7 +1625,7 @@ dependencies = [ "axaddrspace", "axvisor_api_proc", "cpumask 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crate_interface 0.3.0", + "crate_interface", "memory_addr", ] @@ -2314,17 +2314,6 @@ dependencies = [ "bitmaps", ] -[[package]] -name = "crate_interface" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70272a03a2cef15589bac05d3d15c023752f5f8f2da8be977d983a9d9e6250fb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "crate_interface" version = "0.3.0" @@ -3373,11 +3362,9 @@ dependencies = [ [[package]] name = "fxmac_rs" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cc278a5f88871e3800ca269ddf40f6504d29d702f3b47375954b5a64d0cfd4f" dependencies = [ "aarch64-cpu 10.0.0", - "crate_interface 0.1.4", + "crate_interface", "log", ] @@ -4175,7 +4162,7 @@ name = "kernel_guard" version = "0.1.3" dependencies = [ "cfg-if", - "crate_interface 0.3.0", + "crate_interface", ] [[package]] @@ -5845,7 +5832,7 @@ dependencies = [ "bit_field", "bitflags 2.11.0", "cfg-if", - "crate_interface 0.3.0", + "crate_interface", "log", "memoffset", "memory_addr", @@ -8511,7 +8498,7 @@ dependencies = [ "bit_field", "bitflags 2.11.0", "cfg-if", - "crate_interface 0.3.0", + "crate_interface", "log", "memory_addr", "numeric-enum-macro", diff --git a/Cargo.toml b/Cargo.toml index a53df4080..4db2625f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -249,6 +249,9 @@ axhvc = { path = "components/axhvc", version = "0.2.0" } axklib = { path = "components/axklib", version = "0.3.0" } range-alloc-arceos = { path = "components/range-alloc-arceos", version = "0.1.4" } +# === drivers === +fxmac_rs = { path = "components/fxmac_rs", version = "0.2.1" } + [workspace.dependencies] # === os/arceos crates (0.3.0-preview.3) === # ulib diff --git a/components/fxmac_rs/Cargo.toml b/components/fxmac_rs/Cargo.toml index 341044bef..0b90f7b45 100644 --- a/components/fxmac_rs/Cargo.toml +++ b/components/fxmac_rs/Cargo.toml @@ -17,7 +17,7 @@ debug = [] [dependencies] log = "0.4" -crate_interface = "0.1.4" +crate_interface = "0.3" [target.'cfg(target_arch = "aarch64")'.dependencies] aarch64-cpu = "10"

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:` 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 zÐ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 G>i+?Y093*N literal 0 HcmV?d00001 diff --git a/doc/phtpi-eth.jpg b/doc/phtpi-eth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9f9cbb82b27da1ca3ce9fb104952dd01947dafad GIT binary patch literal 81115 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�f^)B~_a!zHn2#lnKDDB*>razqWdS)W5^RvCnAn$*Dc!W`*7fa53i_k^aDcDVn zH7{5qfAMQAlBMq90zB~|=z$iR&ci;0IA1tTO|X%Q^&VD7nd$Ny@)ey5g&oXe?kW&= z%}D4tHq4qHC!WE1bRpiA`?-Kl6usJ(oM0BGv7j`pi7M<{j=AAhulIEvrc5m>BNh`o z@`=E}sgJBjs46Mv52AF;;fFLts+0nhEVl4o>mxFMv?Hm#SDXl;c;rg-4AdJw`H13En_ted$C>@vcP2=S}$0KB`T; zJg#q|0O$&$~ALEm)I%c_-31axQ6Z-pR(_!tZ+bW`V|gehP>KPKe)jC8tvP^A-4r{s>H-%^$`g|HoNldQz_ zB2@`?bt6iWL#!*QSQs&V!m+Y$IB=W>3Zq|w{hU&a-sobyvwuM><7lCPHjd_3hT#6r zDX5>yRy1=6=h%=?(;bXZGN0=Kc_Td(Mt30riHlpE_A3x?wewjnjLS}8tV*&?qM22u zfGP_pCs-s1P_nlxyDS#9QxWK#VS+?snn}etzkw_1x5G=Y^U|zWGt=OKepjt4`>Fvcpx#0Dj)vHu>gTUMudyrE$T}( zl4=?a_=BEZ=84CcS}$%BT_muaS_y=0?#M*N`~u!_Zyz82{G`u`NB1QPt)w$5-XmU- z+c=!4tf^uFL{pg$MKag2Mb2Yor0Ba-AL9*HlJdI3$NZ>W22dE)jE+-#yj6#zD)hr zGsE&;QE+3}VSro(HKiD*Q)R!lXxFOGzx8* z(7F?nW~p-pL~41#NapQHJ49#1-STk(y55-_>P}HDgjt|Xp(DyyzAAoC=h8W=^V_n? zS3kU4x#G+kU9Su++qNchO@DprDbuhMS*_)Wa3Ed&()%_xAaHDc)FgWkajk|VLe0=~ zO2yd=eJP`@TMQX3ws}|J1gd!IMevG$&GBygcu|w%V-Hw;R!&;w#Dg7`E3~MD4lbB` za2;wmGOEJDU^(+c)=XgPhder_e|li5knucO9CHyKHA_o@X;^Pm`L76Kd;){oWr9~fip=N@l{dwKr4kaW9Pf4`Mt3P(>GVnS2 z&ZEcdonzB+dnsJISWe$@T=n|{K2Hcwzo{=8&gX~W#f^Q7nCEDKVXltjxDv? zZnJ9DL5tf7jJ3K>1;tIfnm1e7N_$L_e{WO5r?%Czv7w?kntRw=9@Tc937u(3N}^_8 z=IB&3($b#nkK?pz6Gt*R;U}6pBFM!1Ey^- zZX|}ZH-qzjP|$wCiq7B4C7QF*k+x<`(h(d)6-zU{<$`lT?hPC4uW>5n%>UZ;NC>Cp zdV5B!h9`=E1mXrwHBZH4Y-B~l-eK+?3OL7A74BJ>%>}aq;7ahXS2zn=m z?2}MwBKCknnj(lg6;FT2Ht)76;rfBgUDvB$XxpZXn;wdx4x4UR2{<^?>m3-pE(MZw{ z#eLhrv=HdsIKdEfL>^fY_%VG^l~wfH59rug>Cnzl&PgketAa2A{gJVYo04C|juz+I z$3?h2b`aKqh|t6-bJ?s(<`wq`2DS5Mqt=*UH>BSo+pS{}j%BdXVgWiFo&DDQpY`P# zCqJvkeBd#~XM#PAB4t;$G>SX%M}G=^b0u?1cGIi2#QpG5#s9dxWuH+KPSH7wem}77 z_n)S4!Z5jFQQECzFIFgR9CZX8aFhV_zR<-m@SaPTcYY_?1SwyCU5E01h1<}D1>ZfL z1%=Q>gCZA~#KNK<&75%|=EZq4Z+7GN(TxLkB_&R>l$mDT-{s?RU)BXieid-iTYi8d zimNU)Fz<@31Rs9QR3?+SnyH$ogP2K%D_By#pEtSd5#c*iuxRn9SJtW)Qgv39;gJk# zj(Cbpa|un4@FjR-eRQZFSx2laY`D&tE=rm>PIjqVL+h(=rN}9`7nGlNMd`bxI&QKT zEHP}fHR#JOrG|+e&bjxtMQ2{a##F`(_Wj(V%0?AOig#el2efIV7Fmb$`PB-W1yLcT zX^1VT79!ImvABaI<#mZM>kC_cV@Z!$V%EuzRu^iSs+$)|pfod=F(6^y$%1PmWY?1!R2_RfPHg|1e=h+o<-S#H1X6_Vl6O->C%w!pci!<0#Wboz!oY-!xc z4c5KjCsSqdkI)&4+; zbkNH&T;UKqC3DJLNNlRad68NA`%)n@H-l+F!;17a5Fyb|@(bjBTXVA8-h3cp|A!4e zd_)2*^_=`Zd2MbjrvM)2Q~tU%5$7kN?Urj%^OTw1SM*Wx0pcHd8%{3irXh5C1i^2K z@S0>e@~tf4ji+oW2?|3k3|A}*Ntr>^s0hsA^#*K&2i4V0+}xbaJnxYBgKDZN`MEon zq_0Db53!J9v^@ulG%gPJroH!tQB%}SL3S8I%Fb(27hc1QE>u{#x-pf9oy$vBQMSWl zxzG%$riWi7Owwc)zuJ`*tC(TE&eDsWS_%BBYhQ(oYGo|(*V%nKqC~k%@Zy}K4oo$J z?>6?0j_PAhq~Le;=yDce*mtF<#G4&wC4t-UMkC+1s6bFM3E<(#(S|xRu~_fB5ANeE z`<0uI78f6-Yj`Jzqr?(6rrFv5EFidb`Hf;G-U&RUh|-7_SH~AAR>-tkTE`gvutr^~ z)@(_Rw$MJ;T);A;u+3_riL@;Ado#JW%oNP?Cl{9TrjlAjG{RuNM_q#aOl9-9WGSWs zPcBC=EWvsciwfh@=Yv`jB+gHfQkpt1H67SNT1=$c#xS|Ig|Gw+6-I$=<8xv?ds3X`qAa~|3EIDE|M+R50(XcNQJgUUzM6(;Xu zGxa9bJf+trOUTG4V_V7juRhDG>4}n)>WTBc8I)S2C&RP4*Px>9v$xcl+B{>kfjYO``eT{3@s(8 zKO9BEFbXuM+Mr}T)hSw4i84}ix}nWvk|P(_?WaR*c{HZ0snk5xedsYGN0;f?3z>nO zU`$|qDOI&Q&F$4R`8k|$8Y36&>}-2RsWgx+k@iT68p{{SN&x#r^5`zASQ~eSTXu((gIa z(F7`e{;CI*uNhzU#npD_kGU(6c~&iBTlVI=iP=^16*F5uJ>m5~Yew+>%rdVU^+r^-Z5-hET67tiPM08H#>Cf57AxM*H^V zgtaH@4hy&f%kHIpq=ih7oOw4Giw$i-tP8r=Jep)1kIqSQK~{p8MW!t#S9G;M@-142CoF^6!l+Y zbZesN<5`$_#JoWh$EWhrZ}Q~1u7HS#oV#EP^gJc^zN&%0Tu$y4Lx1E(>r>vIz^Z)MEN(g?&Ed)%>k;)UckONVwjbLoO}u<7N9o`95y}Dmj-eTx&zZ z632l{Gg&atE0;J$5{D(4q0bf zJ#v5R1G3n^moS2E!+9s>#pL!+ z&~%V_-gmA`Ei>teh;H0w6t+0yLe-i{QLwcIL}(%QU025(NGp)k(#T&Phte_khLQ@9 zDO8Ww)7L64Sd3-J3(l1#43wFSutG%d>sB3ti68sty;ip@sOD?NWAqG)@iHZcQ@t<{D$}m zX|$@8spPAIm);M)7A{?`lf8_LkNyx74*5tIw~HCWh5YxW^T^F_IfCT`PwEsV3zOpa z!Q^);J7>f<#|UP{<#uM>dY)V@{E=+1PO;{)Z*E^-)bF(8AS{<>s;DhYThf?(ZN7uO zJ<%!)OIp#y3rQcv`0Ra*AKee3UOwzHULL3QQ;Nm+`l;R*ih_||+dcg)U;qr_~|)rB2gpcGc)`hg?s{ zTak+pC&@Jj2fFDC&jzt0V*LXU%L`9}TMT)g(Ry^GD14Uu6|{xG&SMmYEXYL;*NS=& z(VFQzCCd?Ln|dodA*Ch5Lf*oH)KrF?eX>T|h}H5?6hi__=`+V?XQ}DYBazx)9lJq) zWWX$Fo$pX<3oL0D^u7F;$fp%W5bMEIQFta=1;0H^a0^R8#S%uxE6c-VR)FQ zUQRQuZ(i46mkm}Lm0C&Gbxh2HA()bw>%+^79k%Pj{q`o$tZ9|uNz%wm8n3onx?xZB zv)`Yed-P|r9=OaI4rr&*&Qn$2?KV|q3EW1Le6R=pT8Jo8$a?-JxYeDg=XHC5)HWVa z&#z-6`)-)I`!ehK?7aC|hwGIOR^RN4d(LS44(R5k|G@LraFK$~^CeMd@`;{Aw4nFneqQu_ z-A$}ft6SVx??T^2{l}t*82)Fg*O92_WppX7cXYPz z70_dW_a%C&YdTqN^L||Kc>CP9)O2#}OY-E(BqcEG84){bEcE%cY-(kYdSqy$ zpid8U;gbq(!k=Z8vfZ!8%y^*?>`>?+Q+2LC_KiQ^oQ*!_!EQU+z0PM&4`!q+Fa_%%b)-_D;$UhQ_AM zVx}&Z#->V=BFrL|;IE3NPNMd<4)%7Yc3{%a%pg0Ux#(d3x4B4aW@z)(^#9RsWdFp? z&GFwGj)yC*UO1{GC#RHwSftYvqRP@Al`1v%jEc#A;JkSCD)99SQOwYf&~F&td&Q*W zq(3xR*uMyc=K?3azQ_kjdLWTaV`w?~a_#mlJG$1{%&&|jjo(MNnvG>}dz!_qWZIkU z$Xe0;&vjw{e_YwE3izBHDn6^pQlsr%cPxeE)6@M4x~1=Ix#nm(m;DX`8X+etIyyQU znzO{F^i-kTa00Dzi^~ye1=SL->#669aLFUd0rnU##L9dgw&JZ z;B0MrOD8eFV%mVyfZ;k_I-c&zh$1`g_RF*epP#M;X)PD)OGFpPvjscq8&{BcT#p$T zqX{^M@FVG!i{vjsC?1VfweII;ngtp)M(7Kkf5);(MuPQ^kB^hdrQ-3qRbPBvZFL{B z(7D}C*59I9b=yQ+Qg8OWW`g}aHl76m){hMR`=G#Y2_3Q(ou3YdUEf~8Bcc|oSLvm) znrb!KeE@z439Y>u^Pr#w|Nyn!=2G*=HP=Lfz+)B>Yq}Wj|Ucja#^{DJuTPG(ck&uvf=%q7N z%hZ55DwHOE{L;<$TSJy>u2NUK-eU5-zSrp}*P2eN+n0{L-(3M;cSd5x%hao8uXS7| z7aOe2N7FbwuMXi~qwlGI0&xuv4nnbM7g}7a)OOFu1(V~r*1VLLX!8G_)7*(~1zfb< z>(=FHQCwV{Q5SLnOgWC=;+>5G7qD;6>vDJl*a#7gU^*WZpDP~Gem=$6>KPaoVqDFau}hq}41pf3-g9xL)J?yaZi#ONAO64q85N9D zY;`;R{o5dfJ&~9@m|3+{C0LDCyU|8UDpdK}(8K*>wH$* ze$eotKawz+LDT5dij)sGcu!GQcBJ@A9wm`#v7)d@I7qobCfUd2b*;(Z#=$|^Nh>9T zr8EhDdAuLRhnVC8XhB5#GUbX+hmWp!dUbWRVNWoJ#e`6ysSH)0*ZolwYbL#VWoT&V z^_s6fzrjV_xIlbE8karO6fnKmo2fc;h19WlnsL}vk!m_MOad!+L_U?ZPud>D1>BsT zIJ&iWtB{UP#`%?@h{(5I9&O0_$%%c9j*`M=f8=X40(O&M7$h>VLbVDl65od!gz){D zvLe^HzhP~=On5i;7LP?pde!?{cd6-mD3Sj5d^b6P(t8S74mw(EIy`ONmMy{f4v>U+ z_6kl-#!}^?s1dbtjZ?sFOQWRu_p=B-*)4ybDeMEE57K07!07%uduQ3!P@)X_v;3mL z_3_F;)Q73$!4~mCt5o7w);Lmope0=-27Q*Zo7bDEAZtg z39YqzlPvhRx}Wcl|1~y03yY#ltcy>tL_D?J+3mHeAvLT}v(5}RU=b{ z^W%x{KW4qk8fgcPJ)riEkA!&NH#SyD{Wb5ZC{!+rh?`i`_IziQ`|A!7yG4u|`RjS7 z;^hWwb&kJoc=^_y=8}!$D8au~?DlM%tv(6Niw%v8^tnIcTCi8^$8~G{^2%=cC+7S2 zMS}5h#PRobYE^oSkxf=JLygDJzOKj1NN?W!N@7fRS&IQdQn|ktDlE2f>hrN1_kRag zV>q`|wXBq(kTEWUeBt)ybdzq(D~HYJ;bv>gWTp@V;r6^L-x9k$NEAe^Fb3i*=Dn%P z-$%<%Ok@CA&~SzdqiZ&ZaIYrC)ff+yYCHn!I4& zek;*nDL@Y19m_1p2PDl(@f$XcJdhbAqzmOUOX*_%s%C3ABTNEK{&HGbVWq=YKe1e+ zMnqaVkuEVIAzvC*mdnx6(fjVg9nkG3-MPqdj zmvj21wCA6ucw7#P<=5(M7L?FFC|2q5-}2t>X8Tgw{VLzeo0w4K;OqX5&t{Uy-0=XV zISR{&B;Y_sLW+fn1R;MA^wHt4$g=yJ+rGU*xy$E2D+Kh>{cI~1Hd~=xRvj<>_w4jP ztkJSuTz1Qf1GxvY70Ogjy~R)V&yUw@)-&Iwxu78onzeiOQy(>J^F12#L?DxFATA(b zG`W>UL_Rz|z9r@>PGeQz0(?IrG&H6-mu}d4a4k*?4EQVH$fZuVC>b2qNsUSM7yi~X4cz)KgE{w6Fxb$1g8KBr9z5lQ<6R7NWdlGm!& z=5c2goS$wzR|#^9-P|oME{-JRs?_fg;N#QTS1D7I^Aw_lNsLpjP*PF#_4nI(>Ch-- zO{dk~uq!xj{lY9M@%>#$zF-ancI~e%+U$+9?cuZgMTm3R;5mq~FnzlI4fgx@G;$yd zR(-L5EJ8dxH5!IZV`AB8vrun7r4IpvB3l}a2hhDoFQB7qzR!=7k|dEIcwK<}L>+<4 ztoN`ULL#5SvJM3K-C1vMZ;5izQ0^-fT#LEx0xfAS;E9ODI3QhEQaO!0EPovb<+#ZE zgY(Q!Ewj*L^&pNvE})U`{z)!XM*J(q^vUED6b*4rs~$4a5Z4nsr70swD!-@8)!{-z zS@7NUNmcMX5V$VDay6WJ-JVrw)=5;<0)uSyMOa7dPnX2U#QeZbElLLxo6H~}beGI@ zm+u+1@*`y=Ad?h-6Gdl$CWy=T;WPx90mw$mRKAaw>PZZmFX7<7ytq1=Md5O<)H`4QS3noq1Ece?B4qBihYso>GDIzewfa z2rD4u;H*L*IvV*EMnDl@|HyJr^7mzJPR;Vaq&m<`f@Ih3k&xg)C%$?YgVbxa6dBv zWc-!9>2+_j0-4!6f`con1MnrTc$ZEZ>kCz!1?AigpBFTj8;FTdV$vg$y^FP`G9DoZ z-uG9Rll*Oyf$k94Tvc|$FX4vvMSx^5v9QhnjVjgzBBpZ%jYMGi347rzvMO@qEnL=+ z5%BTX?-w9Xp)j?x(*Y8hK@{fJ7(Lz{SfOrKskMn-9)eg)Ih;b#QTrng@(F!2=vz&gmU=+`dm<1_nRGut3K*!H`Cr^Mu-e+5{bngqv~705x31JNYo8N`(u zHM!>FS^N4-eqU4|w^vK{BrQQemWi`7_z47Xz5Ut~poJ2y2w5Y1E3JU!oW1yLGtW}7 z1<)d3MC8^)7lx=e{Ju{QKYoy2K9hs+MMKb4CEoyqs0#s~USF$6P!5;tcj#4+zmP^t zq^2wl3<{#6rY;r7of-vdiKPbXWGWflhVb4DBy8$EdSHsl&QA}w1MU410-~%%@c^~N zB=Z8GwbM#ufC)p)$^mrl%=M?$3|$`YO9ityJ79iAW8T)uJ42HV`QL%0uLl7SO7p__ zN6()g?*V*{g@-@SV5->$xEA;Mh3DB8NE3&Iq>8yd#V#Q*5TS&*%1B`21q{(a^Xy9f zi>a2xFG4Xjq_=O!GvsFhSk^lR_+l(08J>ZBrIW1{ApSGgn_=b-ycJA0Rv`FFRq3;R z1>li2$ZaRPZIQf0_wU#*ACAAxcCo&W4_fae$Vf#Q=DWgon^phJ^!MfxPstg{cWzS z6czD=3-{V8>J;fxTIvDC)<=Wz?#7 zJ2e6rJB!5U#t;TAz}FJzS5fi&SrkF>?-=}^S1KV^_tz&}np@N0uRsg|_qQ{iy|yJt z`F9*?h~+|UK^ni?pC92YD-dTkdw(#zx)9{W;q}FoLcAT^9ANNf=L5AYK6jOJJOIiD zm39l#O^1^JBJbL`)rX>eHhl1A^)0?Tzw| z&V;ozj14*pOkU6HRGePDDXl2Iw^xk2F$1s{fa`#`6XN5;I-lUEZDhG}fpSsl1|X62 zdLf8^-DoB+7XyRM?@oU-&LHrfYZyy&zR+G@yJ|LYP@9vsYExvKLADTM_3Z$)x8sPv$@o=0^C5k_U{IUF| zipl&66|yURnSt^xPck~G8(5*~En8JU;%Sq!CG3&;lI{qn;yadrL*2o96NbH^m~lUw zY?o$hj3uFH1~gE#-?a>d=|G%<3;}*SS?hRSaa|h(N8mDEyxX##^uQX#IIl8}FdIqv z%k#H-=txL4ctO)$G{04p3uPxe-m@myAp^E@I9Z2XP}J457p5+;E6i8v_XUFb6 zIsbw1sHiK@z4|y%HZ1{$0sIJnpeG>d-*t#q%EBK?LTX}(N?#!&nhpN|q!U21`33KQ zqJQa(Uh#*Y`_CA7&Wb3u;}#%L;=JD9-`y?7*~#YT=V$P`+5trc`r%uH?*4l_KtI0# zLQl)*@Y@fBvg`_P9&cO$47s2E#y$!RKOYS?rf{Y$e^Z}Ys^9MAZhnoHH3T4oe!Gse z)l6x_RZc3Kxin~@*0jN1b~iG$(fMH3JlmTy4~F;)p)o%lC;|h6gQYIfBxW|Lj!QQ0ppS{ULLivkxA+evW%N9~oBxYk?^z@~Rsq=M7f%%iRh!~j1J zyex8`mCt*Esni(tp}c2GX#(7oo^%E7iehCx6dW)DWb$wzHvxW?pHJz{;e2NXEQ8f- z2~48d<8t5c1w25oAT9??K&t_wx#-a}8H@vdVZzOm1B$y!AQI=t*`Sd`Cr7JoOC`*5 z5aAs*1ukHRNWkSV1=Ns`kkA+Lo`#aLcmdFlp?2?kz$L|iDO9J}1-3Gy3j#81U+^EQ zQJrm`Pq@Cjd)-fHpTXli1Y)-Rv_b@I|vV0f3dISi9fIQe1wIeAFu+fgJ00(sm7m^3I=RP6aNf&*o2FfdDL9zLVmr zj@Qml2W~Oe&fqf?jSn7%Se+eGKuGoRqXl3A*OOJ}-!kb%>422V3|d#UQLhDug@x5h z>8VTOGU*fpiPV55(XRI`{^=O+NsQfZTPdzTsQHAQKE$bZ2>^gS-fTukd057S0n&`2 zkc{&4^E;TUI%wJ5-Q}{FAmxyO_-Bp*Ai0gnHrrsk#QBv6^v}X{vi(&HcL&H9U=Ib@ zf1>AzkB_ff%#lmyyaTg&-+@!THzT=ZJ=_4`Txzl}vxK4@()e0>v)Og}y#O>6Na(<$ zw8$!uRCY^f^MGo#fiM&eRT2=(-BXx^<#chZkIuwEVJ2$$_wNY{0QYIb7-5L3et@Eq zhOIs+xH&#OxaV=b(u@4NvV>4MAt67n)0qnGYb4%chkCDDr%MRk%19>f+}}NiKZt;D ze*5|Jr&fa%ziIaIO3T4O8v-k!tku~AMd?u=cmYj@J|%NDpO^s%%z6*dMY>80fNek? znPLBTV7RCa7nnqpd!yY7go+}+eUPo>#lYEbFhzQ!4gn%p2H2SLh8`abrgK#hG^?*RYn1|c@E@~bUm@ak8R@?} zGZhB%g1ZvLa;h-$=%ng9pfyIoUd$Q#C{W}B_Y_Mmj)+cF+@S~<+#X13aFS#|be?|) z#{*2f=5s$<@;3@JBq9D>K-l`_{q%%10~;QinZwD<>C-d-O1MctECJZ7(@z7#Yy2(G zBpt88%4KSd@!C1VNsRY1>iU+KAg*5sS+RC$oVNRi$-qt+ZtOos<6pVCy&WvSLM?$7 z$PAZPRaJ2Y1OAZe2gp~V-Nng*Br-ZW^-AaFKQHGaUD+DX*4H;YoUc(LBPSObjkAkE zlH%e_V=?@8J2BO`2UJ$T!JT-aV0d6v-C<@xc2UX!!q+MZfEOW1%?h9$W2O_pP6k_! zv7bLs+Wh`_oMkut?CdLD2cQYC^98_+%;45wyehCRtiZbX@&EHGVDclcaeq2)2A2v! zgsJ~zfxkZ9Icq+)oV4Eq<$IIeipTwxt*Xz_VuN@DuJ^S)uvz&@Q`cn@NCL9Rn;RP{ zkAUQ&6LQMR4fOvnw!S#{ zS+a+cVi4NwWXqC4WtYfODa-GC#!U739>0Iy-s3ROJkN9A*K%Ixbzb+p;TI6FY=5rX zKm@tVtP7PjWq`*^O*iztLFs#895D+eZRxd$uQTgwzB^*G2@>M&o8n@*5s#DKN)b|& z*{bhJ+X6`j9%$x$%xmT}^>ds*cOW6ktv1qt&GI0z4`010b+RhKJ9LHA6K|tD6+{S8 zL+mM;rx!p5!bxw_4=XGzMEos@xhrxeY$mFxyw#YpFmA5UXF|_2I(@{4xl<7~hyyp?KE6=|sY@4N*@7x)1d#hfArr zi=6{F?O*2x?)0>QqT)@%6&m^?7EKw^%6=R6JjI1#zd#x%^y5AD^YQZTHxgD)yl=Fe zd-o<;{{Zk!rTr0z{{dTXrKb;}?o2hmGo~Y%GM)oV0`GPwfK%K^!3)%7Imq6Gx{J8Y zl4LJH&E9g2XqOQa=S@;lnu=T4`W(F691hFNhedt498C9~OL|m)3x77$o0`lw(s!TA zs`q~tElRL3asPqxPNvunckCAysRuK@p|;@1$6ITJCCus-?wU1b+6}g7D3PjVM!ipe zmz$aJJ8c#~zP4-weu8=PmV-x+f;$e{kwiI3rI@;Y0K>}br7H9FP%?X;(d3V>$>#$e ze*E+)%Z4NC6^Kub{~W3Qd(ZT7<*_kWJRPUko$dH?eR^_KRoW_m?R>GX2HjrTZj;p` zfMaH0>fO(-%ZSmqcU$cf`bXrn#I|h;ZBJXyoT|P{;lt7e{T!lW=*@?3DrBPcen`PA@!sauaA&I{woO-@KbHcazw^F?ywXoaX$Tt zSZjU!LRW27G|~DM2uIK3rN#A=(*(iz9y)aOTSaUAb;0{+I(XKZw>&8|)D^tL#wYK6 zJDYd)%j?TYmNWi+1c2;kj|>S(R3)J}3Nk%%VrL-7RO7gP*m@}nM=zVQTO&OKg{s0fWW{6<L0E?JpavKCcWb*jIF zfYODi5(;)(x%l<#q{BkAl4~hz-uP}v!RcB4{`~3L1}_&3?AaKJppHQJrLWtc@njMp z-tI}5o3l3cHa50E(H1nE>l8|){SjaL!sHM03QCy1yGybC`tfiUb-mo7 z2jM7{fAx}k)JW~738AU?*3{Ihq+I`((!0sD3z;hUN)RP5BxKvf>+0%sm`$z5IB8Q< zqFIt6N$e5!$lMfcf^C$$e(0JtYvS#LsqdDxc@qzJfPe-TfO|;oH8F=Tyasi7reH{( z#%_FI?yaMeXKH$_C#)Jo7Ox>agAW|E3|&H*Z`1k(Ze`7jEq{>`n6Pl6g7@(cMvI#%aJrb)b5T< z_45U~gW78Ap9GM%tVdqzstk8P!6=t70SjiGJGZl=z4LOoR6G}NgqFd6F7byzX<~a& zpgfe691D%}J`$Phm`54Ku9wGv0*%_3OjKLIDx~rDKKXy|@6A>qDJ*AXdLJqae{5&s zY<53LRr8EID#R&a@)lMm20jH)CWXtY^L8p+O6+UV!i_F5d=m#WCx>4yzo3S~ZtUTB zZ_PnA0hO>g(I>Jrh5Fh^;bg^+A3wU(Ms_5TYd1>U7=qQgPja_n_86KDqNBSo(qR69$JyZap6ciTLgNcR9QMJ7I(E1qIbFe~oQjL{w+)X+6ao z-PI=!2>W}S6}u5L{^7n?+~U%UBLhn52Rs+BS3DDzkWiapxNxCT8v1&I zV8B1Go~oV0x&Y?Vao$>0=%=mfFvtQHtACy;k^25ac~&F4dwGd6^53hreCMr)4jn%1 z!ztc9kY2FYZ8BS^{fX&Gr1F^Q!S|L0L7!Zt&k~8EGCaUupS+^_dD)^9J$^!I$EBa?`izrGDBS@wa~FjN}n+M0Rd z5;8R)Lg?~e_8oso0N{qrSY&^S7hO z0*?>~y-5qL$+0d_PH(p%zRF3 zys^fQI#sz+bF4x`m)SGRb~o@-afS#;*X-ALJe#czSu(Egi4RBF3!3-nGo;;zswC8; zcbaj(jdE@>TeohNl6s2X^oh+$#ZAjthgT7`4_^42RWSu`x6#g5-cC=6=$e$EhhJSl zRQlNPH}>m1h)j&~6SsK(AiS$QSn2v`SGfa_)Z*PgDS-h2F^Xd^UcTHYbLp&k)SOF; ze=ndGRodapy|V4m(b1Vkl|ou+_mqzuK8&y^Dkyl=NBnrk*&={!*Y7lkk$mod{@Jp% z-DT9LX2Ze`WYF2Y`k>cm1oD4xUoR>MfQAd3ckDQpQegGrfM8#1PUdCT1swpJzdy^r zYZtmB{s5R?oK3HIdOjEhRYkC_HL*t!j!+qokCV)sHgOQ`arNtSE^++zth)?s+Ew6~ z6~3I}{DOjlk|Jz&y>Is`I(`3QVM3a7X^G!a5?X4VkM-8UU>E{BL35Ht3WCP)7m`TA}H9G(-R4j zAo6nlT$b!?6xrpM-=SLc{<5g!!``30e1TuTAyJxB63~f}QN(j}Z4;J~de`gA68X6z zR6NI~OYA5KeYza04_9h*aoY1R_g2L_w_{VGZQ!DODUS~{TE4x1S-Ir%Ez#$RYm&4D zgC9hj8u}|248K@nrpa%p$<~mX+JIdRH9?h^%Clm{{g(Q&2aPaZwE z?ca0=Re@&;`U_yvWnM(NPn&*yDJ3oK`1&S?{P!v-Y`R02^PkTF)%?oqB+cs47W8iO_XsZY=PTwbsDTFP7ovNG zoH^=nb4mesX=9@U3^a5mV!q{Dg!|Z;!VQ~n1Nk9^LZt~*qs|%oF3;s%9;CRMn_CAQ z+k{}20iyS6S1?6>O?;vVE%>_)-he(9hc}MzbQ~ z*O=~6(XadXMdJX^;br2T!KVNhXD+3o$Sae&cKteGRqO54M?w3CxHo~;cns~1yu(gb zs58hH`bzxyEK#*hv}+G9Ao%h;zb&5(Tl8~fJ0mvdi=2qN5%v(K^phK(yI!+r)$j>W zFTvkCfZSklJo>=XldEE5V!XfHSe^7Mf-`IJjxS)9RYhsp8>EkkQ+^>AbODI()jnwKBb6+Kl)b#43@3 zSBQ3ac>%N5>|J2|@Fj#58y;M>dNm$B&)@k$IO3I}C6|*hBgM>&b*N5}-ae#{$SjX;s-zbBq+^cNWc8+|JS^*PVt@dItR zB+E=gp2K5cG=$z2ZN9yH?~g+IUCvWKw6b^nTklHJ3GIGYA7lzm1tQq7INz-7p#?jt zuso;*%u9sR^UhI@bojIi({?7abeJiSc=q`S?KG?37Q5%;A?TpcxEOboryf;SR=;ix z&nHnuhvmNnk=>>zjO964blCdduqO7r9WB5<6noDiE}+Z$nen!wU-iUsrrqI9(*n$_rr6pLOUxg=JD&B zBVE759}VOO4sYz?>@xzRk)Ahl(_G>DvJK0)cb}y{$1Yk(y8~z^^1)y8FNsEW_svnK ze*OH5{Nlw61(KC37??v8$`kVv)UJ%3=d!!7z6TAMqBt9 zt6!LEtRMO=QH>AmH~rJ)@kECw^iD8TC>J;*yBB9Kd!szc(%?gE>hvyje@&M4t zH``*$ORQ;QbS?XLTDGL+r7(f&+{D8qRIImn$KQs7qZp?h^`y3Xg zw{9#N&>Bd7Xi@NRjo_vg`;tyn%st0pSR&f1m6CGo;Xz%O4X0oKt1evf!#T{@+Rb!T z+U3g1wqLE^2mDfRCX$mB?`RRY(b(26U| zeHM|h^Hl(WR+6gn@pGP<95Jc&X*_2jS&gJuqrvh8Yu)XlOsZgfcL?lFr%7v+d)K*n zy7M~-yTR2bj~~D9e1)iJn?7Hd`-7B5yIQD1qW$yJ_bs@Eo)-32SWU%52>J2j#}w7u zWaQ=s`Uk0OSNCmC?GC+rSAOaBUx*FT8|zenSL~|mlTLm*X7){N&LkIK-GO~a;=O*g zsMy;}$=1!@w3(8Lx+-=i>4+cCdc#h?J8iaQcLb>dd-t}HEB94wrpVo;YhATg?WMmd2&JcI#8+fe+Eg?&f(8#*bwA;wWY@8$IKDvprtqF8T^j)S__<}> zRQ0p7hNo|n(KwLFm1Ny@%aO!;Uw8hC{5BOewUx{@D59(`LS2J^dXm^er$PQ!-TB~4 z-YnsuFX5yj32pEoGjTC7N+5{e1Y*1O>abH^gf_YF&bFW3DYpmV*Lj>2k}FF~YbrO< zK(W3i8|GZ%s58LED5*C2R5y#t%Uxull!8Xnfcnehd?M)Oazg6T!xF} z>r~Lh8(g#C)%IGIMXwG}{pGlAeEsC6vj-1&C=i=dP^)tI@caQ9%Q0SJedEGWcb7|p zo_WtSV?0PM8!9db#;xw}_hgzc-tFW=hnFQ1g==QN9zBm$jP={WO+2oL2;HtJ=&zru zW!Ov*8Dd9R?!5!l#g|VhkhHb6ZG6GCKha&ZV)5+THD_DLs)t8JJPe1Grt-}`+1MCG zx)|(0?O#vk951O;nTdF8zl8PNnm+z{=XhzI-QBeeGshmKBHmo1te*SmHW}KW>O8J& z_)y5GFzu}dixvCq72aGVv<&Hvg;zqdlcct>^VPAOnO$~p)kmtVF(~nL$(+q-4s_#d zsbK=T^O=J|UyDhh+Ob$h^7DaM{odsSRa(s%WkQ6MD>uc`I1=V*Fr5&p*kDv{bk9auvc zSGTMT^Eb6ZVj$W?KocCib_gLzLn4vNQHfyXYY>F z;XW|KV-WdEP7jZ60$T7F*DIB?Z^CooEZ|x7Ot;C{31YzR~ zk}-?vLT9Nt-&!s{h;y6zITf`4w!$V&=$B|ZR7#(8&pmkGcD9Suq~zqVy{^>Br~d7S zoi6X@<2&fqa3?6pD}6{4kzmP=Y!6406>|8_p0m`WN=mo(s9xcaBZJD}#+N<$Yd3pE#TrX|aQ3%{JM6PHon6}Hej}5nD z^&L#UypH2BWpt!mw5PMvVXk8ty1^icL-ZoYkn8sXNq6yz2e? z`~D}(diL?p9lZSxx@;Pu3qrWoHBcm>)cM?)u5Z;4pOJC=dH?$FC<*IVW;&UGP8iX! zc?&x6Y;J!hLZ^=}+oJZw16P;MZg|Z&`3fb|yjg52ZvPRJ!n;tOI4+0ofM(BYT!d_M zf|LiXehi3Dn=3ZwLH6x$t4Wbg&dWo4;nUM4BuUFQ8R=j~rbll4D-EE~RLUATqDb`0~7d|@7S_OSe=S6$_r{?hw9=t9tE{{a??QOM-Pa<$I%(3#|a@l$lYIF+A*<119E0+GyMh^t?Atgmc zF>&$nwYQe3aY>jQhXB&r`jKHMEaPaRDF|mZH8nKI#>jB%Ox;|;4PDMX5e_ZDDZN50 z3^xjQ8YSaJkmC5~@zqhhP6kCqrPElq)Fad?rsZ^8S%N|(A|PP1fk22W-xfAqr@jYI zFOo2>U@jdD9ubSDw&n62xE0-)w5T-=GT5j+2Lj1u<>gynDA@G9jrN>A=l`B)U#PwY z?H7fH<{_S(gC|Y|%-dMH2P)Qqrbn@@50P^5%?}#MEtD7Y~^bb~)Q`rvhR9Ljt^jeC20qs3o3-P+oE z<;oSb>q-W}Pc+*P#&=hQu3r0XoV+7%_`sh$eX4S`l}~(laZI$eIJhV>!``f$+ zmu>jh@Yho1*Dvz^e_+K3y?qJ?%e{}zRR4A%s*ACU%cNwZPHaThmdp}Mj>_F58ja6E z1pB8`&2Qebb8~zC>RzM1JMA0iz4-*jOL6R%JEx-J^H+XI>Za`!W6yZq?ntWM`^y~e zGrf!x+J5jwmtyQ)T7ZZ^Kqjy&Q*MM+Wf9VFmqGO5uddJW4+-hDj88ezL%W*GCjn%H zmipPMbjnFf$?CIftk80yW3znwPg+z*C}q&Di!Obj8nqV-o#adbc0(=3)=gxva?5pt zJMR}*85S7c+v#IX{X!l1_~Ln3PspzGs150UWkhwHp6K~5VFYH?I_qLjUpK^SV=u3{ zC_d5^TM;Wk`%$~{Clr{tZJnUhhJPtPN>$x#pn{hlvM=7?5cl9gDUBjU-L`Gp?R0oY zPJeRBi(Q*Uh~lRI$4emWmL2nMKto9fA!7-0K6_LE&&rVq z#56d$W~hz67tBm4U91e*b+GpB^^I;#G7VA*;bf}d*eF>dz6PC$`lv9?QMP{<0a-Gm7HI5V@0 z`}^nS^SLWFp{L5Q4Ark4p36~w-_lVc5m3Db!jFRHeA2q(+sR9uE^@ z5tYch)c;Z}-md>#<8KPY+w`%jQ0CNNGM1*F1YjO*kN>s#ZO1lDMdZWk6l{}({xK24*HhPO`=Apfwv z*61X1cvreH=#02)$X6nIc|Y`Leg+3A=Ed{Xy}+--q0D1ge*@ZZd;H#If&Y&8HIisVy{`+49g(9ce5;`W}gc=Q(xy9|`Mme5s-}nm6_By~Wy9htc9nVBmNl zX$ob`l!)CDXQ+hs9|H^s_FGgN5p$h^{DwcgtZ}Pd7d=XTLDM*kNg9AhX61^`Itc8M zjS@y9ouwxRLC2?rqeys5se`cTy$0(p#X1%Bg_HR0P8YP7`}+ER>T{AXLw3pFF|_?>+BQl3%*XB0@s7mC)?=8An2inCmrXmr^?IP;5#e$YVF8wti<9 zz%i&PUTM3tZPOj%hb8rfE?`0gC{Yx%(3PTc#)YFGA+8r*KT6&UwtvW_XAdELIdV)z zg_ugj}k1_mDx(5f^Y zO>U;7KR@j@0!BVVSK%LUDtahWh^i2YM~B!y$K})0-wWh2Eoa`hPfJ~Vx83!2MMVWV z%8|5@xBoy+YppZ5z6`G&gO*&mw5FEUdq!x*X52GtV@J(@Mj}M!XK!r6FCTze;C53R zo5wrOjmK`B!c4?LwQVc(RT_^+0=^F(h%o^WW!hE&AgwZ&v`rlSC8{>j2opkh0{2n> zI@zc?atbZkBMdV{kBblpZ2$f_d<+VQV4d37;niFB?tT7WU-!djZBzt?aoP7E z9J?KI&m$g<_=#abhzn1jK7HqI#Qdtwv!M?Gczs&;QR*CXQ$?-I;PA(C+o#gfyopI3 z{i%K}zzB<{Y+r*g{V6abF*B||a{1r^2CYGy#Zu$ z{`fo9y%TYcOI~T+@=BZguh_%*_&3bN1AYVUja{~zWpRuEgxQx{Kxzy@=TH6!07U{M zO?E{K@~ebxnUQbQu?0Gax$%dLsc|F~l{~?xwB>d~OZtu=cxs`lh!@Yp+f%RJEUB(; z&ouUXh|LcpV!{24jN42!jI2HI@R^=jMY`L#aFUd$s45B4nV5}H^a9;*le{URb-YF9 z*=^N99Z0K-K!aQ#{nxLbiU&1sq{pdI0Nw~w0zU#MNKZ(feckApqF(b>W;I_dAMXk& zjokECKXhHwElbhNaryds?bDQlfxUU*Rt_8`6&0O~HAGM&ba9~<3|WaT+SJI`qa=u! zA`#Y{A?mTvVt=(wh4ie|=GM2kfJzAZp2IDl4!kgZ+aP0H5n@18C35!6s>Hai5OqYE@z|p_c9)d| zy9=>Nm4A09lNi93LsY!Q@9Qx5#!Q!}yYxzHN!1^l*T~1S^T>oS z_xe0_vF4~uQ6ATQ?L5@ytBfTY6$%|kN(DR*QlC_!r6h89;lY1Dxk0=~#6%{t_v=F$ zyR#{_4}I(;&L#8Cl^ogVDuJnTTv5)2IB9`)A;4ma^TK@l(Wdi$&B-geDTXZ!*IqAf zgzA<_TK+o}j{*Xvj2Z#`qNY9@quz_mdyRI|+8sJSnvJ6*^9X{r2{j6YWux0^97i0SPiPw^rZnkgOr-0+&M**7N2}-?w{TdQK<<4INd0dW` zQmT$OuAzD2^*m)CnVfd-{9pJSBru&=UX74x3kcnY?)3O+Wlha@FPF=;-WjU>Ri{yS zu|<5ab4&mnb3@yqV#2Pq2qu|n!V>hLVSFgGywvbU=^K*qAX%IXxGpi2V!bG7Qb(p z)457l>%!an`%(8!J-*B1)D@(4#nS!be*%WpAYDd+RJZu=-~_ZpTB7V|i#)NgK~fq0-^&euREeOXE%d1 zE)m$4^fWP1fpnC_yD6%Fe53X2qu50)^mK*?@-f9{^GiQ#ED~LGWUB^CLQSQ!`rRpa zxLBvS%kBALr^2PO(OSiT?ss=31Edla9ShIr$WDiR+b~=4xEU;+;3?l7dU>Y=u9%iL zW3NjG-;;AyR29FZE-5aF=t{g>0~mt2Q-!DjNXUHmgtiicT{#iAZ)bdzn?mfF=#iVz z9xBd)tfu+SjYrT~$KSj2k2|u=Q`0*6=Spr>L~``p*d1CEiE5!wGQwJJHYL|UEBGnI zfwhOg%zNX*&q}I-*f*hqNl>6gtmp2u{q^6QTAx!?N>W=U@;eCH+qoQI7aGB^BnWyn z)Yq3p)Ya8NNW9|nJ+TWnKVvYz5i8!MA+YiDfZM5`s5nMxTOtZ}!LgkN`q?_(r_T7nuG`p7p`H(zwJG0g{h9FAl{PK`gCc*H7 zRQs&64mQRFlegrQ5VDL%HL(FsoxMJ!wo{ffAm7$#?HP~%z=^qFnUH&{?$v|m79@4% zqW&iGkzIkBSPdA_aV}5F$?v^5y`bOzbfPi3MmEoQWSx+*zf_z~IUL_-gg|XP{!pyi@qYbG35$Vr=n&Fx=S!Uz;L$0EVeMXpQ z87YC3mOPfYjOXGHcN!ST8z~#!r}Nd}xDy>+-G^(*Fo);Y1jaE?-&aEv9@aC`V&F$i zMeADtYlnUIQ~jp^Sc0QG?$v;`Soh&jo6D$*VirZRtI!!$^ zlg2}U6GYxaYr{UmnktQ!Tt>XIM6J)EASfb1bRU(V9$9rY%@r!LDx_fo@{oy>1d5*Z zsz+eN?Vib7@xktJOWza=qqJK;>QzVw)_#=s#7MZJ($AvhZf}WOCIneRi>^6mr`CxR zn`drdTJh0eH4?_Y2E6NhE`iJ#o4-P0@ryk zCQd$rXx6+Y<~1>d*$bxYqgn8;$5lu42 z6CJwUlT%a8o5TkJfr1wC@BbWX9BKN|F+%tBH!$j{GJC3SqAD(v`v_);z^nnJfBrJ) zy}v$-|Hnln2UWZ4@O0E}u~{choY+C_jiDZq^yn?ytx}VsC0e>cMRW)Y^3HKS-g46N4_i(2$-x~0zSh>BkdH4Sz&*C_U1|9cG%eB%HN;su z5e{uNAlav$n!n3^3F%DC%lxp9mhQjax8oqEIw94_{Kt=NQ2vhOBz{oOjhb)RQIN^w1Zh>(@;*i(3!wdm-47;k0AO1JO1?MK_1N220>tTz61-OBzEbOx_p zIFY)#x|qy(dR--Mp48V3-XxP~S3cpBXe8&wPrXXZ>0S>&R0FmV!;5rw(vCXO()c{S z53pQ{-mM*=H}o9>!@?99Y!sTCGU$t=+~#as7qO-VL6P`NOb3UW1Yr$ah5Pu4<+H-88T~bL0_iG$x&vAchJ~yG6Kxz#^6$5!<*ri&~ z*&`z(odZ}WTU*=lr|Z;}EC+Ze)|;0_Q@<}=R$A4uzcYoWtfB&phz0tnodf9J1>U=N z?|6!CuKnXs40c=(xR?bDgRb@ClgmKBl=3#mUhmv%qc9}cF6_7I`q+NMgm33lpXqPZ zks8>WF7sJyZ+h!8q0}=js2IlMIzMB2Mo51laL(kmF@ab)A1Ap7RJpbefk1~7Rwigc ztS-2@Ly_x?ziD%GGm~V1igoqKhe^tNk3tpLV8)=<$$vQh`R5hFFUS-k{1zTEFo@1u z2zhQsx-W*Z{{6B3^Wu5oMXr&K-JJQlCr_RnI-ZeqeJm2a;-W^p%pfgS%ePD`$J4yb zLiP!onIJ5B<9PzOW}e+`)t2hoYjbepA^U#M-uTwLL$3Wv37u!IOg+~AUNr7#cXC-N z-#mxAu|GRT@`^lrPY#Th`E)pT#q~$ny^IQXan9eEI=!vG?cs;z5Bv1or|jN~KIml< z9>jiZ+p**B_YSZ+X2kyae86A?UI86pmYtX$Tw7aL_X*4b9Th?G%a2jh14c#=W-xP2 zA%xy~Z{_S6*TIf;oCgsx)YNLm7GNqx_&OnzA&4ia!1sV3!F--5v*bmo5TJeRnSY;S zPlR!<*(M@~c|o>^F;co1r-+VHu)i8oMyN$jSeTk7k(aHPP^g0^S(pgiBTvs7lGLhWGui7^tl+WO{n6p;JXbC4NI1c8xl7smN%D_7ny~MvH9?U=d zCDnkGUzxZ)$U(88*UNGc(i5eOje^nh`US0$@$>27LwOGo%`!=&dtwjZ`7_Lrm$>E?9pgAdA@;|x^ zqBbEAokGLZEoMlliY zS*kXKnXRUZ$BsSKm0S@^Jk_5KLAyRw&i(lvs6(FKzl`8e8V%gk+wJgtbc4kR!LVUk zdkKcGwlEqPnRH;tzPy28MDzOGK+B>_rE?H#{#W7v&)Qoe0h|A_2}=Rnpm2CFsd?ymT}!mC7uKoFL%$=$!FFoA5gn*pVv&v=^UXVs<2WudZ3IiT zA=;C!Pk^+KZWULq%w+%rLNewYk3+nA`4M3dzkWv;ECD3TW=obVAu1xGKZH|I)>0yk z?Ci4s^f}Ro20H~J@azU`pMt7t@Y;Te8ZDTTG8AW3xkq7er`N8aKOREh|Lh^b8)H&O zG^353x$_L`C+Kv%(A+;AP_)T9>+aAgYT`{zO$C{{JG^6xqZq`vnB~#yZOhNiJN{Q- zgRl+tDHL6-q9)X4oPPM{`74wDJy0jkPPYPZal05%1TXJ2Xas_sT51;QVMmxXh9Vhq zDwu(~y1GIZMsVLD&TQGT1uV^M2!R>~G3zhh3n3!I0Fz3DEDZ>`fwCG$Cp_D^$%t@H zuv`qEhe@EdM9kQ#_)L6zRv^+49~akaM5l%_h_Hj82MYzi%nVLQ07aKF<1aQO2N8l8 zB0Au_s6vKR4auY;N={!#8Mq15mJGqdEp<1@q8 z<^KLGYDk+zupC8^7?~zKj>uyOqsR3UB_$=9?}HTw6!}Z-MV#8R1e`G4(xGrdP1>)U zTSIx7#|7med)`jB$xDACX8*%(L9`xKpiv^O!$RVW0oUho;ov+>F!CR$inE>EY&N46 z=VuiaH8a`sw3_upC$0;9-ac=c0Z)jvsGG&%e$z<$mu-EQ2y=@#Ph(8>pSZCy{8#^eZMSIE&6L}@`IH<$vHApI#)pdyN)uUQpnwa zDj<<7@obf9d8FxZ>!L*BkQ>*@c2uO?6Fd7C2>7N^7w<{gXKO3oPGcdfk&Z`GsbSR1 zGfx^Tp-Ytabk+@PH; z6c6EY6(o7zwG`@~ux84>5KXE#P$E}DFwNIG_`Gd|wbX0TGm zds*!~S0B-$J$dpZ!+3oMNB;t$kW4m;om~#kq803RtW9+qq=ye5inX9!9DcNa!MbB8 z=s#jg3q(XF1YoX5WKgSjyZ?u+A;zh4K{Z5P`uLw~)Q=iJxB9j=9_8uJi$;eoU%rfk z=%OY&1xA*O=YaVnIE0a_-E$sW7ff`AR~lMcrdOy>b5)GRhm-sJ`gR41weK0qvqcUo z%Ex~OK7W>w6P^LrGX+YUyTSp=1G%X_RnJmXkYka~Uv>qKB&5sO?!1Q1P`L|E%(=2~ zremR86Aq=?84K=EghDiF(#^kmP7o{*`UOU{6t?2 zT;sYjG2O3K5Kv0M_ET+V$T?kW3w|8lsn1!k-D|>Hy9q}R&#T@p?%HvA;eoZ93pl-p z!8lH!*|)wz%ZRu9q8s0V1)D})G1hoxqHI&w_jCM1xkGy=9xPPdYU^L!(~;ED(o*pv zY@&=01n_i68$`V$K&s36-6zWR%a(9?Yj-;%4VK?pu{||!Z_$gg0seHo;mnbgr4i|Q z9nqsf`qxvh8i@%Dmrw3mBhw^PFmc4)-F@dtalTN?j0`bB;hyt;vTL@dr&32+78K}c z7j*_LkJNlOxN+u3ad-RUYpo#94~nGb1sOgcdCF$ACB-#^TyEIwtjK@Cy0g^NcuCty zu?5;ao?aXyCmq^d9flPgtGF!lc7@!LzE-IvzxDhQy)~SflVMk!yBY;V<3qi5PB}_m zY1Gks5ZzW{^xR-bkW*tgGQf-7h&oiVP@#!SdyUkkWG(cBf#< zqwVQ=L03Yr?MO}aQI#E6Z+daesbi|SsuZSo5W6PXH^(B zaW%VmTz&4}l`mNR?(N%6!pV8px@9@?j*oZ{LpI~`9VcrB_)l@**pk8M$jj6s>#v|E zq@SYhkQ*xt(*KVE|-P(mcL7K8?HD~V7+v2R;T9y`8^lP{uxague5X%-)$T= zQQzWV$ai7yK0U`Sjz&@Goi-&KSv~bE*jRQhMH8XJY0q#{biWZgkJ0BNaj?|!>V34r zHn5avxwhu3kI6ma;+Yv4 z!xiGy=H}+xMMQ!IPvg*2sEL#b=6BB?XHWqn%R_PWm)o&p$H-)|wsxp8x_BTVN?DJ| zO;JKqlaflStAjTNbK;=0Z6SR)t)dG24W_#i5)#VY(b8(dLX8GFUwHnw4`+&)Ho4#^ z5w3sRM@DSK+pAr_*ouK}HtpVnsm_*`(-VRd+KvYtTW|nLxyyzBu6RXAUer8&I*jv# zt80E}x_Q}!=1P~$$B#QXJEglqyCxD;qjro2NtbnU8sFm`4cZm%6*OllHN&%taD+4J z>RTL2UApW;)0fJ-9q$%a-8>q!(LW)`IB-sh@&$WX_S`Tpc0EJwQ{wuLaoT#0JlfkP zwDJG`T@UUCt9J_r8a)?d36&TPGTtf9eg>bKNUzu$k}kvFvk0Hc>GVg3Cvc)HZgQ39 zM467^^X^8G^NWmx()VR#jFg-;xX|U_C!^J-r zxl~d&RA{|f@o!zn;bmn8-QTzg|EpY9;OQ0Av;0g`2Fa$$1>N$^!pRfk zUTz*GVtavh(w?zKtxqN!`UJeMv{Y%O)TtblWwm2eKD5YU7yNb%t}>5&38qe9^u{i#JhQO zc)`31{Z0B4Wm*Kw+bAlt^M!RQyZX*tgYVzJ=Mt%-7!ymB4GydAB~_~yvF|=Y+vkNG zv%5-&Wk`+WA**XAUavhrk*ko%H~^|&Y3!3EvHtEy#XP^x5a&+Ijc_K?lNe5>;3H?G zINIdTei(&Ov2{MzB~@oy2;ZU?nEocZJxafG9+V5#I+d%3k>xlDojtA4y=9==ch8p) z8Als*->q+4yLJt*>AGwHwdlXt6f)xQ?k@|5+3XL2ZB1$Lk^aKJ+w~{Z z=qfaCocummh{6@G;_bauTg}q`!Yu;p5$24uiH7Jcid{JUe5J@PuQ zfWY-3vm9He3_-6cr#L}Ae^X{{jUnHEfgxyVwL72Yu$tR} zkB8`;ZRO=P`0-}prg>NhoRcgh+z(xjE?y9g5CDS1dX3$uK&|EIE%R2U_Va&sBF#*9 za@Xh#EufD_Pi~tJjwO_Z zi7dk!T~2X4Fki%OG!!XzjrQ%_FVyn!eJgHZ@qu}7V$^8U%Xo8lhdYs67}q7Rkdg#wiGMOSuTfp5tPi$GPmhr?pZto z!YCjYvO~`^od(>fc)ca4_h|>vrecT0qbkAG*y|sU~Ig_{kGcH;?MsJMdu+bowqF z9vmFBlU=cC{j6E&xQ;gzJLQ~#Z_sarGD=$wvgSU<+f=`#uEa3xPbE+PXa9Li@$tP> ze%H`2|MDlNl6~GVo&y-zGUo80-#p4r{2yD*%N^O_gkC2OD1A=aankV*IAW|jd)9Vf zs~MSW5+qVM^#h7QVc3g_iOKz0D@{ltS2W1j+SpVW9w)h=$Ryr~i=}YFam8>rL z{pxV4=-;oN9VXBLqY~PF=BfAZ2f+7KR8~p`5~f0gF${ZPrx*_|Uf&?oEY42%fbP_} zG#ZU?xr0RTYfx!wkSX|mAJ?r{#ZZ$v*$ennd5ZQ#inbxxCly>gwya z93(1O=aqy@XBwZjDnGbf_Ry@O6`6ldob?{ZGb8|De+Mt!S*lC?04w2%yiopV)~BWI zXk?ue6HVW4{?Dm%U${8ng@}zY01`pNt&}v;q-t{Z_5vm7b6CgpvdN4l0 z|Jf{(IA@Mw`$9rOiA)>hsuPR0dx^IoB3nsGNrzb^{S8rXJT?f(1-@F*T3b`Y`)rGV z!17Nlb6jqs;sN;%n`A6~T|>iG2Q=H~J$v%(Svk(?6nshA%fd?(UQ+hR0Fko>OTc;? zE#!8qTN7Wf=0p)RS8Mbt8zW8(kvr3qI@a_ruz~}#fr!7iay|X=dEQlS4c6IwP-w@F zG{57Tn*0tCE+eHz#Q8)}_1q`3T*s|$2L){vCK#f>b~ISmUb}VIdO5ee0X5cL#B+ne z4dTd3Pp=-|=V3A%BZ#Ius(M+u5R_qMCgo*i6`cr_%Cs%~{8Yxq?=-IB_RVERmDOH( z?FXwDS@^zP389wV9*{0X^vq=2Hy?2%;So7GIl02n);2SVD~;i}(D15}n9FL=qr9a5 zS|%HPCyR6{LEl9DWvmVk4!xNBp?*l6OSdfO;qGwQ5mEcMLHp!{8AEjx+L_B$C=EA( z0HyrqzdLt!{0X~65lMKEgp|}@;Ziz<#g6rdCsv}HZEiig43Xw24VA}>Y4Bqr0F_i$ z265PdX87x7&Eh_v$Sm$KJ6>5?UCnx^%d-Czr3PA~ab%Vr!ada0Uun;$=U6Pl=Jf8P zj41N_dE_UCYd<3YK#C`F1uj68!QF|1*ySMn8RX&r_#`4kTKD1~*YLDZY$c~ALNMs? zC%jci*IkjV+mSzZ;v@+t>5&&=t`sj^0g5D(t+8D=zHtMqk(HHI&&+^CY@1QRLc;_NAkF;! z2cL;A{Mlg%CYF{7vmSC>M+bqKxjne!bU3!Pk#N&0p`CN5ONq8LpQPbnj`O@Lk$Rh@V#(;MDYPCsL6}x9wTT&zZ}K2@pcH$Us@+o7 zoBN&-OL**93m3q!0ca+^%w}oECp(-F9*|_zNG#^?B4(t3^JVPEH$%)|l|LJf8K2-4 z7eA?~-h6}UW(c57_W@3!Eku$Z7_em+5Az!!4+F-cW1>VZ7AdTDq4xaybCIW*SK>&- zr2n~v0XW;oF~rQv|9O14mHklLATyHK(Ol4bm$VZ%crh>G)7*1w@rJ0F4G(Wvx6X?3 z7(K?s`GXyno}k9=)I>skWkejdN3sjNbElJujn1Fv`?F`J7cVN|G^9Oi(EPTMMVFDV zUbGV#)(hkvAJ1R6{q;&LW~G~n(c}B~&!Sn#1c_kMwAC=G!i1UC@2Oxl|H~8poXu6! z)^=<*%_3Sw*v`5UjOZ*8^^oA1X_K)xiRtM*j3~g|9()d##k>USxyLa$8jW5|{aI0h zl1@#OmHizk84JG?V}?vK?_c1-j<`_Y(14ok@7>A3VLAV6On(6*1S9?Fk>CqBQ0b_^ zVTEJI?&gAswBQGs@zEFAPHWa<-_mHzO+uW@!K}#YRjalbyg79H{e>qeSd7HJtVVjY zVyteFi^EG`7Xo<$yTmMdFJ9@g=mz?mR0c_U{LC4pS)Wq1qtQVr=H~<6U!JpzNXMu* zu=tFG&o|`2`EXM50>3A5h?zAb0TyvP010$44M&Scad0R%Nl8hm+yZP)0Ji@Nx)9(; zX(*q3LYMXvCt@Bep8skG7EE1J^D(0a19;QYS_^Vk4`|^9jvGS;qpu{w6eMe*jMM|5 z@jILOG>cncQpzX*L;bM`rcO@(GJ#ndJ*ip}(FhvZ=ZuUFXp2+ak%jUIj14B{H^xSc z!=ib}{pC=AMcvuq2nL{XaeU5W<^px+63M=L7Db=;%|(GoN4!8(h!P12#Z1J3XK*5U z5YKQsFp$_10#4#vjjAOWRaAFX1Y;+vtEq$%ckj9i^m{Kn2?`2gI2Li(UgN<9=l+2= zq5@EdSvUhGK}{485b)90OQvl+5xpB!YMC)7NX%Ui3ky@uG?Wk7Z5`8!Q#eid37U_9 zD8vVwQJYZ_7nu>h?VXB}l2WH`w^=0eO_2zG|BQhip_zHZ8U2CCbEq<2RE6^MICidt z(bEe!XoJ2fd(vwlK>r?Pw9DAY2n?Mlafp(*BXur47|w1!js=0kR-r~A{+9Ug;e+2b z05vzaD-Dm_vBnpNgCH$lk^W)?mB{}f6OqRRprE6E5N8Nz_|>RCE4|A(zEuBYkqAN5 zGpzV=%(A6RnQ@ia%-ry|bgLlXuZg)iDwW^mNvldPXS6iFp12mk^>=P1vfguo!$(gv zH4#cjduAjwpb@`cbWH+>k@c;N2N&dkpS^jfY-D8gX2bqjIK+!yo@PB%SX2gxfdIQ>{u(jj*`UIgeizu!}Xxz`q36-s5c}AFFaIk=|nY(+zz-5-2 z*>a8`i-0wJ@!CmANeF*qW51W|6bVWOBe6sRo6)fnj(%YVJY75D%ICRs$9sX}xUhJP z3d9J#1F@FJkH!9KfxJNL0=vif1o)+c^5|z;Agth+Bk$jyl^y;+l@Nx}56kbt&$1BT z)QDy}u@V1+n4Ua+T4n++luQID1qCYx2NWG24=l;BOjsm0rCI{dN?iW}KWFy4A|lRK zEjNL1v0@AMxr%^4+1Y5uFqbbxwr;auu0gc~eYdPM=g@&HBmN&he!TFHQ*)8X++(S8 zu{-po!tC-N-)M#$yVshuR_{`DQuvP_m!7fW&=B}DxH9+yv&z)N!LvX%x)LE}Wr~~@ zI6XP^o~*MJ=ma|KN=;?f$Jgyf2^Hy=b<6_rf2we1N&4vAVbyuIjY$cSX8gHSDuJFE zpwiLN@qhB%=u?6~*gVwV+k0b4PFMm#6frm!oVc0!J3bRZ=Rmd4@Q`X%%$o+8IYfG5 zq7V3&g@r|QW9~}1U(MA7RY5Q>s7x7H1Ti3I7xN(_e`Wtap&O*Y9UpZorHdDZ8BYhnC_)}Rs-7Z&{wWeM(wBsV5<4&-|Dy6BSP9}} z#HL776q$7QHagtpG#e<18y3?$(TsS90}K9#S^76*y^_cx35CqJ(3O2cW{ zA*C2jZ1awq1*tD^Vg}Q=dEWUn@A(-*@L=@PysT_D!|V~Xao{)vieb(R&;eHD90VC& z8P|OJKgfr3809B(Iq8WzH>?L!OhnKlq7pGM!C`myY~i1vH`~z6ObW+D9W%L~_04U5 zbpHAu53g%(=5)#rzfa~@jr?qXc3%Zic4%teO-%$1@+ZPp5`&zvK*5rM`)T)V$^@BJ z7A8wkVbU}4EIDga{RA^HISeq7fio-q1ckbmmi11Wa~Kij??76K@i~BHI_HbC=nN0j z@bf=^o>IKtngq=*6Jg9oPVk#8f8G`u=2ok?kL@hI21h-mS&wr5udeBz0=3Y37Kej) zgOs<#1Sg~u7^bwMg2KQsycit$pE_s#z%?94G!5`Q3;6!G1ieOWtHgfMmO-U~^np=s zDG(Zr2*XVCf9^^k1bR7(6IB1?IeZC`Pw0FbCh?e66!^b!G68?L7++mL0GTV&Df_gv z{#qSOE-wIWO`w+fqcoH{Md0}e$`v805M10}>GU^3-(`nm4bJnt0jeQL!BbDoInjhB zP|Nax_=gWgczJorJK=(!ks4=l-W9ocDZQxv-;@N%7O#DC`pnzi1iw!d^Gpmf3mTdR z!4Jh>0C#Oh-nDV=IdEw&_bRi@5XBCPd}auILtR`YWJR-U#O;afyHi+*`3~o4hj<(? z9vxz^k>6v6D189^m{ryK0saxQNNB=&z8TE@K@iK3mi=*ge`5{>1%-9YzoCru@s?Y^ z>oM_?K6a}e=?RJ+^obt=JUlow^qW+8LpnUyIeGa*AE;%$&Jix45ptPoPhQYvFIWhi52sfdcgcRua??sxxw$MOC5eI5JQ z$6J=Qp7lKUeP7pgUgve54)+t2k~lmzA-g4l%xN^h|1M5=DE|R=|Gu^O*6#m_S4WZi z-p_`K?B83p@&Ws~R{91MBI#-VjWYZL zY{OhEvK52d3D;4w`~i7EVflAtTr`nVbZ~XuHD3W>>Cah>%M}O>EjIzZhun4+LJvtn zJ6~V{f@b>{umu8MzE@g$8$nk^rO(`>S)xXa8eCeh!yL6l&tr2vfqN6#H5dD4Anl*J zhluI{UmlU?a(NG?KBkAb#wLIie;^|LZ(;%6zb}N2&&59t5nJnyW^l{RzK!>Qly4qE z{ZI`b-d~wezUEIZ|KH67;s~jNGTQ+1d&7g2z_X;M- z5#R5BG#z*{iBka7n<-zeU}c06);3UT!s;rt`V5aBPpnUM!5~1;g;3H819ln=*eI7tYb5H($xL6@9 zVqOG1ijt^{q7d3AQj%KjRPSVIq!h*&`FU5O!BT+V(z&AOjeDSsX%8+m1 z>r;Sr^ga`s2G@O@u63W*PlKO9_q*>chUVr%il)d!Ml8T+aqs%Q_^o-wtIOiG2{q|4 zCVW1UNUwE{A3r{Fea7NK->9$o>vqt1e?Q+OLbocp>EW}aRILgOh;)hU*+N0(5e0H2 zIdFg=HnWGI#OzwP7$7}U(hiiuJYwyOp-q-QpPRW>F)yC309g#VO9f}&x%Rhjzk=8L z=M7V#ZuI!67BR{FJ{3Vsxt}*>hL{(X)PYQ;>}2{a;7LSq#wU`VS!&)Ey3y|Up;-p3 z7^pSuJmiMpi%S-2I0swU-||8h<%V8;!SCL^BLb6ejd#ixnEO9Ct~`q6N?K40Xui?N=Zuk4Qtdg&P}92dmt2n zADzH+=>4H}08~$LlPwNOgk0ak2@*;58nnkbEK#gs0}*2r7|Q5L5}Aj$GHQnii---R zuW03xp3Qn;-Wn61tCKnb?uR2k4q^nKK{(hAvP6||l*m`5A`v|iycZ^vra+f9j~M>> z6Ln~lO+gqj%0aw>lMrOVL>7Mbl*9jz+7)h$x`xIQCK=@E)~zEwqXCUx+~EMmt05r& zVZn{`j0e4ex!EVK19Ess9j#r>VCgu@^}ohJ{O32e5tH>#7aq9QZ`UpT_;j(^)trHb zUS4~m*fz|0dwW~kN&f{S;?*>NUL8$nK9z<7l>(j|KK+pN%ng7Y ziA5dLFi%d^tnE3J?DftBys9W%{X4K&qCyc_zV_f;(PJ5fZuxv zj-EYA{a_Rki|WO7;=`4uW@fatv_c~y0zN%_jYEg{8f|_vl$Dc>e%{Lv_JS{FRZk;6Fiynl>CgKzA zrlzOaWzIX}RzZ?HVv+{Y&&EmcT*t*qnCKiyE<mG>-&X^4rQa2Kd4h9M7_dRBIex`NYbSaWP(D|_If;%&Be+W~RfVaM zem|SCq)%F(6n`BP!?gH#@zh4%GZG?IFke zJn87st@Y%XBvXyNk*^z-S@gK~F4jG~+Ofx7?sI$Oo^ii}2d}nBlA9cCn+hKmpB;GZ z+7i4WBrlEa@re;ymVouvrJL`XW$nZa&j@&YT|LJP|ki(3dX zHDM8r70C%N4v=qP!o+3rv3fk&Y-!v@8qJ9`r5h+^H?xZG;o?$QVe+tJBawc5#HNbW zw1dLD`|1S5Ar<7Jbbif+U=Hh%UC}}3?zjE@{WUd1SZVLP;v0>qt`m0Tl*N|BYZq2I>0h>gv|k)+N(6`p5xzAM@752pDOsZfzlk z{^s%^z$7%iWUXvZB5_Ipz}d>zBsVL-NX(S0%#QAax|LIR0w?sC%BQvghG*P58-xp4O<~=_w+g0=_RneA$AWnhtTpM zIgIXOHu6SrFebFF_gxC)Z5I!5NGi~C@7}=5Kur266a;n_FZckzDwfIS>-!ev6}VVJ@z};ALtw1K+a+!3t?< zX=0=_*Ad|7$&)9S1eSM*sZ3^qySx6@g&r!{t%$dsSC@Kr1^jo?GfF0!((Vfwb&qq_ z%onq+>dpt0u)iowo+snz#I>a$ zwsO$A>WwQ#fFKF+*Y)eyKWVT;@!&ejIJTnxg1b21 zf1N{!AI&Dj1O&yjr3B32CZsWg(d*;qF|8%^eygMPFC3jlM(Bm#vJepw>EV<)e}CnL zBINRSJ9RoeFl(d#8VvCx(${QO(NlruhvW8}WyY#;96A@bWGIiu%mZueD3uGDIjQ#o zPVJMzTaQPN5^iC&e0?9vy)@4Y8s(<%EJ9t)uh+6e9=Tc3PBymOT!88&uRueJIa_a@ zt3jQDj^d>5ftt~cq7tZ=Dp@#oJNm{N;} z_wx3U7`T~vo2tw63Qt54{@g~SZR<#bLt9u`bCT^?L5iq0f3A!aExi%}W67Q3=`q>H z+w(w=62zMD zj<=+^*7?d$Li^wP0>=7_%u+^kVU|%%o<}B?xRKpfl;AbcJ6%O>Kx+& zCz0&@r`0}Ln>#qGuvP#r-}ZN6Db%I;sxWq*Fy>?A&%|n zK487oFs{y}ak%g0v?STWZ;;7Sbldn=KB&XiaW+`Lrt2_SWd@Wcc7PvTZi@Sj1K@^N zmvW^2-mdxTp)cW^LW6KSx1qDss3>v8Si0Sl2Xa_5!^3HtXXocT zL}>03qjA~~Sh?p5+PBW-8c4IFR!wD!aOX+USbTHrLPtO zJg3Q`0s>QjT3{K}(a@X;Yb~|o?!o05+E5(nev2Dg;%Wyag8Q+cGs}?4>PFal0<2j zlY=z8e#>b6z*UZl2Kt1o^97&ae}q|uFX%bIZ+-#azPE&k+f4ideZe|-0!`)_8qA45 zoNL82lQ-*SX_c?}Jb`<#3h_T2D<6QJXDI#w%F!J5~|RN@$Zgzb{A zM(E!v2(i#6NnO@`me?Knh(%C!`1<|3BnEIueBxLd-+rA@i1I?nc)l{bjzMpl0#*3;Rul$+zWfE9N08k!SEmO9R)3|uQwfoT`EH@_qk}J>dq|1jg1*gt zNclt0udJS}YEnrn9V`A8S?!pCm@2{smSQs;4NEfe)+=cvy`$*=| zYqTnx_C9v*X^VX1_!@1bCb>C6b$MEaNJ~hhItDbRaWilk;sOI0fyFtOf3ixz2=Q~y zr@FTaH^Zhc_|KJ*7--tGPeR|=?zqkQS!t5DCmiW#x)wi1$V1kmX!YdMj-<(*@|Vmd zz;Sqn-cT_+x0sk7+b5}W@P*u@I@*cH>fSDd{ZzrHq^fFXZ9U;+^8<2=nFgKhmWSHf z+N`Xsnl{JuF=!v_`LrZU`dWveP>*0a^XU^26xws;{Fmyq2#dkX2^&dF5)jZ7yZEJq zQAejGJDYF$FG3N?qN21q3=bCSDTL;%v<8N8i>7;OYL4V{5t&bUg-pS*6uJFX{;2M6 zCuTsR3_h?(8H@IlU-tK(Jf<}8tffWdb&3k)xiWTjo>cMl@R0GDOK(+hw~4xK>Aw)G z=X3PvQS6Tq8ZY^Mn@9|-ZQ9AuJ5dSC7D4rRT*QJ|JmX&%2;UXL2TM?Tis9on9oc&# z9Foo_&<#J|0g6=49d#{TdI(qz4h`+*h1Yj<>gj_wnKU{D_e!7t#Mia9vlF2CmvJRj zj81lTD8tB&{0>Ba&=^W`))&#dFIq-wIK`fA6j)hVsb!a!00HKAmyoHnt$lu^UQl8f zGa--Why>%;Q#bgMi3BE)o~=7lbzlhyT#v+0Kqcva<>gI74eq`O4w0DDJw}99U+eO- zXqOS4hduqM%@FvG+u7Oe*s-JCRbeh2$EtYwJaRo%SWHFg-r}!cn)hm7n15#=MO?<7!zAU?Lc4PjC@ytY%vkjwXXft0fINbF=gGuxb*%*VNZ%Q593OcXBVBQ6D7cieOV_ zqVZEck8E-}uNQ{O*MQSD&zP(s&Ao;Nmnp%)8y<+h_T*7je+&?@*ta*q1c<4RPyJg|J+|y9 z{dMGhFyX$t{kGzeK)!is)3J*P8T!941yuK4Q>1Fodn7KM zf83TC8z)sBFcjLgaBezG$11MWOUTTbs)tXGA!z(aajAu5Nk$6dNc;n%h~S2dBQw1M zeRR-9*xXuhjLTm`9SJ=Bk4tf$I5+rxhOX^!A0U&s!yqbRDJf$Hav6JE$&nyY7m!Cj3>IQ;Sjl> zTv~*DP4S&6O{S6K9P+sKy{>tl#jt53D+ybh-Y{=Xw?@&hdGlt_@ZYHzy8e)4V>&qH7k2i=#*cohAqEzECa0EH*kZMr|Q%KZVnF`DQ} zMfR_!V@VZ>AsJoR|lz%a?CD=?xp3D7u^VzO~sKb>m|wBB!y}LID3hU>+f%Mm^Xclbhqv;mZU+&pU(`KeARk2 zBt#SF*hug0>)l_OFao>x#eTrqf_f3B5qR|nnx5ZvKrN=#`S5p;u2{f|AGrbYUhGbw zb{FkrX3kjVgUc9A4=1$V!`^HqMMm5-Eic3TGW2EU<0J_OLBzID*j`rvW4w>L6q@xb z%|Mfdun3^7oZ*N}@%F{wu&`KLIB9ihxSJb2D+LT_y*4_r<1-c=2M347Z6SwwX-Tug z7kJQxu&pyqJ8<}L71HgkA4)36W@h#q5w^w!(iRA1{zdxo3J7NrJ1S_2#ch1@fvH5^oAo5!w*U{mx)mn5bTm6a-0o5iF0qWM@zk+yadb%bn@$FmP1Ur;+*haVdihq}C|IsA5&9#A!uA^aeJT(^j4DWAx ztRXQ~et-qjmw*Iu8`@J=q(Iq+NiIl$rapm)HIi-_r4MT6JoKH|w6hGoL0&Vtg$-eB1Z81n#C-Sf@vl?t_E6;Bt@iCR@NMSgUut=!c{v~}qsXJ-Z zlLssUA^J}^dwn6cO0J(!Ad`e1^MlwF)6UJL9B1XqSKd8pM;tnyYl;_LLwp}1fuMT zDwXp6TC@wKunaTpf&TTZxBa88oKBe7eom|cAe1cDoWQodFlr70HdmjrXew_1mMfu~Fu*J|ZqP0; zhuHr8-TU(M^8wXm?z{v;8(WcU0jtZ8V=mwqjK`@-b*{?fJ~-I=|DJuFxhgV?USREl6h zmzS4sU**ZN9HBuP{ER2NE|V~Hfi+F6Id0gz5w1P}*N2UBX?6KRXDWglwJ0xLDFkQXnrY zD~rl?WO`cW+;A;`-kpFs{0B~Zm8)xOFGJf8rT!r}mVqUMHfT9OlU_GW@E#3s--Z=- zKt>mx4Nw{oF>5m*tEnX|3kzdCg5P2PB6h#oyQ7!?e5^Hwb)3lg7ZWG|xfal&MSA0- zEv0?uVAKagoD;uN6p?8(Z$1c=D2FgWcGe8PHG`OyAQ=KgGB{XwU=IDEu}hcIiF-w> zY;OJmQzF~Pt>F`38t&%lNeBTVHzmU@+Vaw(az*U)rO#To+#aIHhv6J>=n1rhe$36a z1$~>Htv~1ce4d?EIXZ6lG&5atax&0sfsh1$^}o=;Nk14_68mG<9(+z9jUu{CaEatC zgVXGximsaOuVpxyLJ@Q{J(qBZLBI9PLJ7DDqo@`NZF6#h(PWGeo|I=J|{ojZrxEFvT%5hk!prgp$pEumzE9Ny-!b$xVC)n7zS z_lzekJA-{;3*nw%Z@7ZtZ;eGlO6t}vz^&O;69$LR5$)yQiCgjFK$@d^Fom9+#G6#$ z>h;QE&`F&~q3sB!cQGO)#P(V;L?P|5Iz~Lx!6SMd1P%j)9g_USKMWD+1kgXXp8#9; z6yk{pON9Y&OrM2*!<~WRwSlS{D8+<0_2)z$7q0%o-|KK#WF5Pkd-y>L1%S8OtNJef zHP3huV{2c$coBFVL#|zz8Q=8lsW{#_l?$`q)yViCeT{T{cfSV=E82hk1V8-${3lRW z4IS;eXaJ>I>%axy`Z+d{bE|uFajkKFQZLd99#WfsDe&i@dY-PYmH@eoEJ6L2!6BV6)vlSX=!O`>LR)y zR~^ljU%z;v$V@cRvEVX4mY0{a?X=Pek2OEl=Of|Vt-G`e9jg6Oc?MbG2a)RPT3Mwz ztF^Uutv~`o?63q6w%wugpW+<@5ceHEO>#zg_fk9TDU6qL6UJFx3MlQSiSo}}JU24mgEnK@+GIDVoVW3}> z&9p6SPwH>-gH=0Vu;TNu{S_*S`owdVNXrtu8UTbf-kr$E%+wH#F*vZ);D8fDfS|}6 zvE*cC=Dmh9ui0jmxHCmO^~!P<0boqnI>GrG6?CeOFybx5cC?V9XMBf#q(pAR7f+AT z2Ih?}z%rtXi=228j*aal^R=r?s-W|!vbpE6Yf825B`X19P+G&X+P0aE-w%?%?gR3~ zhZGAK-{(uo-!J<<8tfw&V~eTIj7jtbgMoU3W$VGTw6yur#k(#_$Rup65P8JR1an?P zMYq0Q-CZ|xri~!0j7AbV#hZ{E+;7uH9z8!wWVg=)0|P(&V8CZQyFZ?M@tB;&m+pr= zaWfCpq=upH8n+%5ch`>X3V)P~Lf@BrITb&O_DwHE7UtMxJV`Kw-PMpEl|%!yQ@`=} z@#A#ajRxY8#u=QOY3b>k{r}vgrX_UZN7}uRQfj4~muf8k12HemhgBDn+O$ngO#$%( zR{>^x##Aszb(JghK^uZp<&`UnIro{=j`|0Q=aV)&!{>OeWF<8{L+UGc~-{xb;$euEYyCZxpC8`Tjy}= zo#(A|>t*!iFbeRX7KMOig<-B?y?ru}!O}=7^wRS!^X>tfE4m2LPeP0(h$Fw@HApTM zC}IkGdWhq_t_kz*SxBtDN3)@nL1#?J{K;GU8Zq`vb{QF&E9Bne?4@bdNlR$#muyv+ zo)QaK*sAl=xjX@G-973W2itc!StF}&8^a53&vbX6Sm2Ffdk*?vd;2$hql62tVeS1? zwD_oJLFl3&n0JF|A#T%<9WjdpUmh0j@16xszCypfvvc8XRY^sevpw#*xF`Cc#3yDp z#+f|%qns*n2gq9bHAV`BLT@5MYObs(I)Xt(BO{zJ_y-1UjG{oc^1=n6j*ohBh_5Fa zm7FZhs8E%giHBiY);XDp&DN$8&}C91d*N2XLj2RK8#Wrin(a96)G=B0!%Mc3exFtF zbQL#O0xCtOW%Cg+U2n8E-#5x0h0*=P`|-9UpUCKFeYCF@w^TzH-N?$KyzKnU9e{H< zG+@v6fO-3gaz+5Hw^?xyj+gR&RBMZ>Dt?Pkbbne)0O$Y|XX{DliD%#JgTFU!{ z<wFI{5+0J23QGPjS8=9A^%8!z}*MIFY+?xo#IW=iBvMesN^mBPWY;y)XAW zs!95^;(eIP_r7?skz-}$muvl@^?jOl2T@nRZ@uF-*W2yt1|3@+pV!pXO#I>@zPd4C z+eTEwvOERJ+X$Y1?<4NMY*#VI&RiSAlaZ4u*%z9#Z5sAW##bw}mrkn0h*+M?U|}`2 z%s&~x!zSh8to)dGpC*FTF>>ZS?1IcL_S5${krW*n_A1g3P z3^uAfqE2!VHh*%;)zwwfg-=pPiZHZido!UaxUhMr7;HfV%ZWR8${5`Pg!GQOXmtaGPnLB$IK~GuF zPC)&2$va4ALDhPx{zQCwtu*EMH32Zf3}2|XEY0DJ%9%re?fLqN_&&&tb>c;jRQ2iW zxN8G4AUag8@TfVG!v+hkYQij0D*lYC>j0JodY*T7cHy3W(o;EHH}C9~iPQbShl*#9 zPD0#;O6<{G;MlJ(Pg~AKTY-&}xsx!U&qbA@mDiycCt_Jz??Z&Gw;+lkSNnybz%1(o z=%}GR(^N`FM;96v)){Ax>IYLL2A>xYG?bN;POzLq*oQXz5K{BB!$v0qY7GH-mm430EG^Nt;}z1YCy_oO*z@v9Lw($ANv3FPaPq(qz?Fs^Ti9b zmF8nfhaMKL{y7?H>kvihyuGV1)^=?xR);gRi2|0mavrckTFpc_Fe%D-0`% zQWo^IHZIm4p6)iM&;I#OS1U()T8V=aT%ufm{!>;~#L&mhM&yK!m7~oWPZ0wzD^KF@ z>Ss^e*@zr>u=cd)5)%_6i>TQ+*x7q>NgfarQFL{7bvJN3ea1#a$>zMn85@1oqasHg zJUw)6+!bBVy1^~p#gprhh`NiXjk}wx^Jz~TF5A=29yTH>4$k-|5f$Q}lx)tpTHAtW?)vy?+5K9zEBc})Tgvyav-jqs4t+CzqqJP6M;Y%- z9di!fK5n+Og}1g%Er(0(vTt^;sq+4e?2OuDFWJoM1boB?rX{*W#xEo#kUd&tZzUX* z`yxLlJucT(HZJG!rs>0%4~fT!lJfuapWh$ro16R4$${-8Wz$+x;TkmiWdc?zDVl$l z-`5C?<*QIy;dJt@m3j@SJJ5Gw3Z$1+cTfI5m$ni`Fevt@LPkIAUvFToGJj*`OTg;% z#N|J)`28)AV~uV+{*U4 z4cVf(~D0DXkQ_ zzM421y1`XL7LdU>IXDQIp#=_dAoQ@+F%s_|XjI67rV1jkKM)W`MncThm$Zt5f*in?>>=IuY>l9Z>%>M;*wQ-0;=8F2nUHG&uB!+@T^43=05 zsMnEOK~mY9p=PTj;aCyJc%p@PEyyDnL;v#r@fRQ!Drf$_Iahk7XtxEq$0Q|vLeB@R z&fG?RQOfiGJZ~)CjShDyCl2GO2Y_B?{}{r69EH~PTQ@@r(!-gPm|k&y_!s?O|5^Ll zqEU&7)02}qEDwktDNxyo`KghanX{fs0iK>El*Y}@>;7I&#NGf2CbW)6NXQu}0Q&fC zt&rZt72DN@XeG-oBHP(4bf9o|xH|VA;%Dlm%*46^1lZd8R$SqngLq0b6WU2WADno)dT3krTc*+vij6c+5V9!IOie9FxhoHn*{1n z(ZqdZ@9ic?op*%7HbU$rS_&UpALoB1{nwSACYx4zKlJSexeblX98QAo(vS=4h$KHt zLKN^nDYiV@>{f)3zHy?n>eBK#vCI zC`?DKdwN8|k(o@paU;KedL4KubPW6>_UKFAEZc#U2KzXE13KNjJQ-WByTJw07lLsxf8#zDspdz9QIzd(KPAowjfbt#@{ z_c)N2g*~r32Lz-YHNTFIPVY`Rzb8zmG{RAX{N%-4uGIX#iitf$3+WR|*2DTqR)*mytrTzI z!4LIe-n7jH2MJRl@HY)&Y@4Z!e`EsW4ATdtID)LDla5B~m)Pq`|3Jo;1QY{wpXSSo zC-+K_)LCMA%D3~M!l4wSk9PQ^*KXBP>i>ie9(KGM27pg?Au_gjH!;>TqP?F*=yjG< zfF}NupO!3r>GzYTPoH*t?SPUA4v&f_PreGDeH#KVAqCphK@^1=xE$hkF&(g~-LNRQ z^7RD+b`k2FwO{T;^XQW&jX*%y@lvnY-4onZjopkNE$X70|2Zn^f>;^|mpshr0srOq zM^0XXzL<9JceLK#6$4l5g-_k#v-18+hSGYwRguv1cnrPq2Hjv2J$)ahT|ujK=<4Z7 zkOatpXK-}A5EG0sGtz|p^@uI)SKZlxl0ZwVL65-^@njAyni6y{lLXb zUHkH-UIQ!-n5Yfv^pJ$|HFbn~gzAymWW7asv8{d#y|`{@ifUs~$VYk|S==|_(xprK zQb+&SP2n4XMR*Rx?-A98w(rv4TEy;P8kFnXZ2)M^z5*Z-O7Ss|Z${w)$MdyLXYOq{ zLpRFD!*j)4C$AGJFM&Zt|qr`5;afzY@Ubh*37~oMALx9!4S{}YgU zwlI^q)woMcA5`kxNlbi?b31JPcpk^(e3&s7~xCFZ$EwN{Yz=?5MS&rbT2Wclp9 zFNtZG+?cdTo_?&$`Ms&_jxqs zBms5yUkTsX$ulD`5^UW7d~b!r;C@? z{^@Rf^Gz4mhRn$tl0z3 zy}={?AE96M5rp&hfI1q?O@a?$?(daFnjr)>FDR84JZ%A5Xo zupYrG#OZz3cGDfnv!=O*IfH|z5=V>yrU)42p77MbF|c;sx*V46ca4x{fe>NhNt@6L zt_g?#G%dqFpYl(#!4i#0i{aXh%EREUZ#y{x7~vXn5&M7dAHKT!`i*pS>C$cZei^mc zpaR=>^CA!5tx95I)2b(qIq176-u#oi4=SLI@(%Vd=TngWs7;n3@72@0GB+5AMEqzt ziSHmwLo50km#ZyD;m$B{Y*qPDxCC)9Z$XE2bZW}+ ziLpDD&jepz0oIb5q!Ji?FWcKaKtlu*RwBtg3nNANHnEo-Du7_MwF1MwdX;LD$d?H= zdiz3&Q`ct@XXX#Z3S0CRs)7HvPk#9GovjE4*xp;p+R#I8(1F%X);&kHf=9 z%T?dE@tKm*T-s+;Qh#wXno~9k%Hey!96|D!Dtb>mR>~-OQWx#{^xOq(Fa8YF5P0Ck zm0wz#+_z$SX0Ov8fX5L1Mmg7+0X=7&VpKF<2)70m3pZr&@O zM#1Tkc6MtdI&rb#`KFI<>JVA$PJdYKfkkKk7|RbSyGjVb#(^pF;_f%MF#nyqEzN(8-X9;F$iFUwfft+mle;@zkQGug82cBKjT>UbHK^k$$E>Yn3c#Z1(3a< z07K@sKLUpW!^i~hX{08G+R;B=t+}?9;=U)L6*P+srR2OCKEi3?VolAeqDA_ur@S{P{euSv0yV(@USd&Z{=qv9P6Pv{x0yajM<0H^* zBPnrzA#-dGQ=ur_d#xKDdWVs4PJ}Za-Mbfuo%uscfzLL@o*mqBA{_YQ6GI&^H<8_Q z2eF67gRkeo-N@+wZJ_Z=Q<_>7_dIU^68NRwac5?e*{5~YSTINumlhXWB{v$Pd5>`h zUQH{q03@q-@JmaZn(B6EbLi3W9s{7ctU*5-yeC{AugSWo7ye|~3EY#IZ#en1v=0(z zO13C4h$RWa?toaElHf%yu3mTefqB2H9949>8Nn{|Aa-zAY>9lzH&PeF%AZ=Ezy3@( z?fS|McazYS6tn`q*>YX&M7k0ty4;^IBBwSe=|irkY>Y~c;<;6@SI%c{k3(Em*1i;i zjW9XnpTD+N7Qz9V+#6>_vgxV?kofieMqKxa^;o9&Kto{j%)7WTvXQZKcbFtE%bUNnW)0&$zkghChSp;6 z9>W_{eEY^2kTGJAEot7=_B96qK!KW-*Eu)ei*#+&lX50}4}5ph#Ux#Vt3HBK5R|2M zB~fUnN3G^$a|*Qbctrs%L`Ev`|K_l2BH)tiB(kqMpFt*FpMCBHayb`QfSzYwMZoIH zzCIMBvgBraSsh~pUs z44n}O{P~E+Y5z+ACp?#KRP%6ir`IUa{isi;Q85n(ZckLcj6aVfwVRmzsQFRu(x2eI zd5!yg^cE(j^dpbU<4Cby;ei=R(6GYEJ%7pf;>BB@LM^+}C=_+A^c^;2$DX3m2$7>~ zX^jU`!AdE93FBJ}$}SGiU*FFbyO)5BR=qu%t3gy!vJ@=xnCF6gvs&lSCr0Gr|9&NjRW=wS8>^h_Y=is2fi7kGdP? z8Sn26zi}h#DGJ)I)o~d*fQjE_K_ZGJ7Q?C2YPuy$0a3(b#tJdt!H*haB zpMIJWW5P_1gTotkN#|7VoYxpzzIuW%g^-Afx{1hP#8tr!<*noJG{RKgI$#9-%e*zV$WC=COgF9?-%;Y4@*jV=3-Wsqq|N^o1$z*#lM(JaMZ#s`EzpyD?L8J z`wdpn%B9v~vZNWL_Rg2xCw6v&Y>Yf1Yg?>Zokt^9rL4v%j7|_~pt>DMa zyqBAM7)4T>p09(0axeZ_0`&9QDNiT5ePn>0~Dttw^wxZ2MeD|)g)V9y?<-&<@Z zrlzJwMwBGDW0NK~X0=b6Y!x|xvX?p5E&|5MI!Jyk%*{JCr3UUB*hEKXc;lWXTB%ty z#C{(pgUVq*`I42+`RWSSor4bC$p;v>Y&rG4=VQ+Te_L8o&K|bqK}H4!hF;O7q_1sB zNLROyI@_HHC#D-z{WE~jcB6_N(0VOR@PfYTTpgW2gbo)DR`2ElEzxm4``}nM@bHq; ztxP!>DM>d#-QW2FEL!!`_wL;rCL7$ndMwB6alUUi9*aa#{T(I9;_jiDUsy0&brs#2 z(aUetyWf<)828?gv-s|1s6pg0#y6;M{!xNpAK}}*!_x1_X&m5EvbBA`z>>~U8}HOn zo9^&9rlFBmc>JV~a%B?|d+WG#3t8i%*MKJJk6|L0u_bAISF+57d|#Fj6&R*@zX3GP z>|+kH+mX|7q2AI$fS%Tls8Pk()!S5~m_(Avu}YK_H%0#@M!0dr$MYo@-(3V~v6EMI zKJptyjbR4)9!wiPS+2*u7)&{~CXNDt&R~BYm{XZ+_mGFfuxVzS&_vY8GB5X3KTNnz zvTZxCGhrCpBGUkSFP-x?bpD2i_0!d&K7DeoNn+rXtvkzV%j0v<*xWqn)EmsY-M5%? z5l|s|#VXFvil|mwQpVHuhtMlvdY7%o^Fnb%!gB(I^_%m!XAV@io%VRkUiN!)+6$P( zz19c~QFb!KN^WvUx!~=TkU34p;P&NRpXCb$T}MfF9Hmn`KWH;kr$O2R?f&G+lQ*fa z7@m0K$UOQ1=48cDm3Q{N+%%?TlNzn-lWjQVt|WCQTt!J~!ftzEUoB5ld0;xQe6MS0p$X(lBxrA!7bGedZVW0csR^;O zz9a3G+oPt*UWq-WF$>IAoIN2bYA8pw`q_2b4^)cLf)Bpi)5fkBNj}=(Rx16u@G*}6 z;+HJwS}W6^1eTWh6T^S;Vd*rAx1>@EHwft{1m!8I)VEjE5;_~HmFcDOOV7!q8e-Bcvg0lH2hYM z=Fp>0%A>KBIU@GqiW4lwxoeRBQ3g?k1yjL|++Dp%vSwngVF1kes3fnYvSg~flOG0^ zH;ny$XXffKXFMUQiFH5~s}$G8sl>Zu<5;*gB)<&Pzj5tUBo8}Pp8FWolZ5509)&?} z?w677{U{UE3KJ9AsRt^34EDrRgQ%U;IMuP>9 z!b0Xj4rx(Zm4kzwT`>9E)s5SfE|*CP^066;Y-I?KhQUzlZ|B!n+7o|q9b0Lv+nJ=J;lM9z2M(W{u5Ej4TKJlWW>aoPadGjJc#k*4 zWPzIKk8-9;mCu~~EPgY+5`z7Qr8Q;aopNov$it_U_m>Qwk|JwpYsZcSzz%^yDZO*0 z@FU;K;y27IIfk}kTp+owZ;OxWFnU4u^N11EACQ}k+I3>f@$=tk`vtta0x<6_np?-# zQ&GiNQGJ$74b0b^4PHUF%1>=YUW}8(_j+I;WnO!t3M`>PF4`v?sC17}dgZl+Rb`bE z>*UvY`8@5Nd-=}jCwe6OPe2RduBo)II3>E8e)Grrk<-)+yX2TQj2?wbJFun$$rA*_%|`WM5MT=cGYx zxKrtpXFD4FGD+^xW!{GezDH5eYn$bk0eWsxo-9`LIKHW z&9)p+GV#$3BmBNiRtsM%gR!ZH73Wq+ze~0V$!-0>@b2~N>yi^rBh69+>3u*R48-7F z>G)G-7*-BLL#MwBc-v0_DR^>f{;)B4-WvD5DEP!%`^^n7eSXiu!h*!@?68%$kI(7t zM=*U^$K*y9U!v?)6;}NubJ@LCkim41Dw5QDYNCx%b%Q%mXFP&kgQ%JZV@X>bLboxJI1t{^$ckh_mPUS+u-Z(0Zsjbt#Ez zGj<`X*bPQ&*@EC9yAhxh`pKxXquVCcJs_xe;ljA`aj&J@Rx4}M zMMhRF!bj*m2m|)YL#}j_$xolk#}Qe8h8@bH&gbohHv|3@pu6g#)I>+r{#qUlE>Lcr zW^hj@xsSNb8HJ-g^kQc3yfqGMrmwt86`qr6tvXKX0jPZ}UXTPw>><|6=3gPZ%zHMf zZIRspNbB4nw)C~+7XTLCH!ub?upPAL%0QD>mv%WGgwAM+`iwd13rOB>a}~H#g=3P5 zgG-gWCX$ExdmK-$xdYuRjw9hr3gH~ba}DDC zIPjSWPF+~F2UiQ%o`oj|Vt6e2BiL>v_QW9y4{rI-rBIMMvb$SvuZwz7&p^i7V(Z%* zb}9cQm`=eWA!7>M+}sC!@~r4NWlx-Ou0NwWY7aE`g94?|tV7{Wr!|f~w)Vzy2dy*T zkA@~IJMPs^xP;pKWWF#D&lw=|o(B&dif3Sra49BgE;sLpK^eu z^jBNX=iNG2IrsxP$Q+4{t*A%=L`f`_QPX}hV%JB!47((J8C&sN;E@+OLqf4UpY_#r zHjJ_|F)6)A7?!ns?Ayq=8va(sM=walS$Ie3J>dNk9f}bHXn~AqR%)ln-CYZ+PqrbG zN}5sy#(6sKje;?-lAsW-ba}Tb?I#gcYlRqs+2+GNqPAwFZm9cD@4rz{!QoLaPJ)g3 zW#(HBfoI>ke#cAeUhDw?7d~vX)RpJg1@MHy7CyNiCI3V#1|WmZzUE-1*L_=Y!S(J` zJms?NE!^+Qk$*l)wwAO_JbNA4{ZMH%1>QY#F|CGiE3dt3*gznN^}}Fs#N38Wn#G;2fS3;9hGvh%)E4#!D-;2r+*d z)&|CcT0`{wiwjUgJk$HH&mL!f#gRjN_G$&kVV}9H2X+IyQLK;@O5R!b=8dpk+EwGc zSdleJF)>PTR1a8%_#A&LH+fiu&*#0EPO5g#T$Af>v%&FkVkQQ8!#5rFoG|ToWN}_q z^Ec=hXe8GO8n;JmYSaVS87Tf1cJ@X}*o4_AkS1G8keC7~fgCre6p4lZum9bbxm)d+ zEv!vu5;^IIT%mM-qCe&z=wef-jzqnY3jb0hTLfGyDB){NAHwp-K8z`q(!IP=>6zR=SA?n{kRyQ_Cn?n^pOn-E*p$qN>bW6=iyc2Q54&BC=GJiQN@ivS*jwku8R-st%$hFH* zS>@fz!OE&$1C(XUMme{^K$WMS6g4+ALR6lzTvrp-OIKqI`#Lp+IT$8HmNQX<&z>pJ zW>kJDTy6uYC6lgE@dIK6lIi$HgVtlnfX#-8f@Zk*qunRViAs>>%aH~ zs4mbEUFkmmVsg8IQWWa6?feJCp!i+$6J%*!Ym_AastZranyg=}equ8*ub2Tc$;hVK zcw*8Z%L@6Ek*HmG+`Cc3zpVI*0Crs_8?f>v&vhl-;`vSEi3BMxKA8*T6(l!W%XW2x&B7#ZLC6{ulxJ zHcWO!vgeK+p#_!n92RlvIqIVIHF92b6_rmhi}~w^_RGkpZfCLw{jLx;K$!ZQxDPBf zR`heBM%4D^0}8zNia}5}N7-QD4#i{D>&UW>N6Msw#)GU7xLmY~{8^$~veQhGxIyt5 zgFxizaxP`J>U?oN!EZ$#PN1nZQ{UtLNekmCp~o^bT{k_I2Ohg^GHZ_=VxKvWxb%@) zbSR&Q@g6$V%^lD>8b-;|OvY5%P+bTpT>+qLW5yIMpqpiVO&FxAVJ1hfrFeY6F%@A~ z#e%_VW%4$RXpIhus?cazl(}D%sau$6eK9d9d%f)RV`*+#*`&=;hgr`C@7A-qAPmkG zF&QTCZ6IKE=Ho}j@o)}uoBc06_?sRddx=Z^xrS@!6F@9Odgb~>B*+1HOI9KVviny| zq&+#XkNSkP4%=ap_2o*wh#?P6{+MH`FaHl&Umj0&|81{%R3u7{2xTlKageFx7*Ys@ zNF+lMB{D^2E=MXVB{Gj0(;$?gP~;$E5+aHQPLmA3^`@TZ`QG3C=ib+IbnlH$r;u4{TASxs&UtxahhIQ|$p=Y!@4{J2Pq{%DD<=J_5G|oe zv<}hh)bnnssR?}+04A&|#PUS+&3VHa2TE>9-5#w+TNWB9o==RuXm;bC7>92Tp1b!U zgh7SyAFuX1u5^0rCyK#SZPE`ZX-OvGn~Uy&u&?ibzxD=3P{5RGE=1p&d&P=oM5(lu z4Qo6OS5hTvuD>{+EV<5^XN&!EIYGk}u&~% zHg66#{$%~>LFY^5(Ti0tw(LDz@7Wje;Pb~_8XJFhof?n2MMV#^z{d=;V)pi8iMS_6w7s%tG2re(&>Q0rt&x;dT_)s5KtN zB3H1dQuO4=$jHD3fTfD#VW%lQMrBF+^6uWX-rWQd(FXHJ8YQ00PBc66%=iWhk-cOr z4@E8g?RC-nW)y~FbahoWC;!T`maimZQstGFmdXXjTFG2YdXv6^Fsvw-ib` z0A_8x^I+w|7}&W4oxC`A)EIo|h11EjIjY{O&IyWA9UbWZiMywWn_N6YRGE=C+mKuo z_e?nMp2N$_{BVvK^|km8DyjiQQ* z(Y%bLOlZ84Byx4^UQ7twu7NYd%M*iI=e1hX#>d%v?EcMh<9h!H<;{F7d!ei$acbzXiO6(bFi>JglBbiJEElj=J{Ci>`6 zYHf8jWI?<|X2)8xZx9$ zHrp&2x2*fTx@N1C)TM2^J!fp+?1Fk(#^?Lj>#cWWA)H|04Btz!+oSxFec#4{JiaEC zf>Xz~F34zYyYDR6ZXFKuDU%NaB+6`Jqwy~~I`k1~ELC`B=_M-)_L#vi#U$c0Z*^qE zMr#{w{Mkje?)it#L>DIycYJs}vvLC+N4+(~9{>&b?M}Z?HvWcxQIOz8f7uO{ERm|} z&KLn+!OdOuJ)URt;l&EpoZYCERhp*0%)x%p$mZ+2Wi+UxEYbz*{a#&VY$w|4%2oK5 z+mJ3W)kbow`L1mq35$oWcGs<~*E3J@ArP%xiT#jyy1uxdqQ_jJk0pr0Mc?%aWCWFDf z&9r~79?}e^8dUrSAA)_eUx?9(;U7X~gV#|92jptg{rktB-r)b|i{B0m1n;I_dF<5< zPX-564#`!}=k{ZC+vCkbX9%hIkC3|xWM3Y>?Amt9ID!*wE>E`*@~s{YIcJ^d&xK|t8>tz?C_ z0NVVrmTdq2S<5CGx+_;b1o$OVyJ33gA8mUdK@C(^G^6J~m*3T;y4wXq28|pu4hHkl zP!z#VJvS%k_HWh&3$P+W+FJVG4~67#Ah^kgvGJm4cuzl`0+?`@FpM>$sgblaXzFJe zxw?Lba4f%58>%Vzf=R;mWq3~+KQ4XS_U+Cu!j}bc(I8~R>zk>cxUj%kxw{`7D2TOOXqf&#;PrLK zjvF-m0X`xj7uX+vmNVA+@T9ogFSzUsTmo7{)#!#!=27Fe{(jr&bGGGr*qwwo`uOqT zumFvnI}>DiN+-+Z`RU8g@=C;9xgzvUO=X`zIY2{3Uf$*oZL~en(W^9@08oHG@HrW8 zKY0;l4hoA)X2Hc`3#Hp0wG|1ytPhYtYe+$u)P?v>n<^MW5TfK7h@ z*5F|nJw5K`#s>$%QJZ}NDiqP#!o*MvcX=@&CL|u*P zHq_Lh?l*gUq(y&&xUw!lT0mgt^fiDZ!Mz37WhO|+4%0f^W^4oaZtdl@)$9o{=X?Ia zrNb0cQ`7HZ69Hd-{K(pyg*{hW+}fd@GQ1GT;+(LO+1Idek(7w9lQR=VMINT#Jv~o6 zUk!n6bcZ13(P7g#_@=0E;zluQf{#ZNF(~lyW*I0fCD?!A7*1k&%0rKDVs3JCh!XwH z-n>&^yRj|l4Ihyeo=@hbeSk||%N;i`hz;)kpfKy*FoOXqpdMg`@h$XL6>*(}Yzs#n zQLGUUZBBe(JzuxRnijq>oS+I00lIAiX;4S5#~a}DeUQ7*>&3NAgQXC}!RCZ9^!@8VUW0W3W2-)2{R&9>A&ikU-XbTyr*#?c0XK#JV0NTJUwkjz64Db6Nu!ME) ztgft#HMo6@n5r=q^!Msf^v*JR(K#M`j>Gs}d>m?|x~m2|je0+R*m~-ViuW!2W($#M z=LBeb)cr$`2xB zu!sEA3u9j&l_OWK1qYL5Rwy%G*48G!VFUfg0MgFBt)bL7sfCLcS!Ly+Ppvo-2*NU2 zcEtvI%Rr=2mU+$Z$`81e?5kFVAaZc@^rJ#04Lo)VFYn$=lO65bqxZr~VaY3+x%sWH z9GSjK&iO!2zmLBbzJ0|GWlJ%nNFRq4KVdve?HpB3E~nxtzE;HttqA=ClmP(Ewsg4i zX_Icn(2YeaXlPa@gd_9SPJc5a!+0}!i0aSSPu8KjYCY}*h(F$3GGSO1VJH?68d?KS zWSwiNj>j$t8mYTPx3siqJ%yi{!UPcxSondhhQp4tO*t0T!i*X6ni}89Eoc_tF;9Kr zQTLpL1fe%Hqd=gtLF`nGo1h~4#c96}h!;WX1; z=kf>!x>${pC0@UBpN4HcRLSf9RK2tTs4IC1-WKt>xgBC197*l(@7LOOZ68(?_fu46BN8MDgHLYI-#vTh@KcT(tJrC#1xIgZvUa4w ze#K+;yUGF{=7I6gJ9bBu>+P_{d%yOf6G+kzD{?p2LBSsL1Ik5TDMCy~hb~`UT3hRg z()+a}ta^?f9Rw)~F(vUw`Zmb&wgeJSi0>=8s>|F!B`PE_w~gZDZCxbmrjQBlh6zHiJ}se{=r<##`r|-?zQ|oh@&eWgh)0 z1)3Ec*qs_0r_65c0Eu(QD09`1!!O`i*>C4t*ODWIwq~SG3--9U*DKcLphBS^Pk?*TqkEzyVsZ7~V+1 z$dx`Yksk~_lbJb$gi@|~2|d+TEGSTLqfZdOw});#JUkvfE@+>!f8tduQ*x#re-{Kq z^9CD|ueNas`;l%bvb!?7}yzyJ6MX= z;x=YD=x7O({Gd^Kv~jRrX9ce0rMq>22rwhK7nU z_I>_2QgOPL3e9Ky55`DYSX(_GK`#qKYH2^hYfo;`Jb17Rwc%5lJ&jVF(S8Ak9!|y8*Dd~>pE~Q?iYbaC&OE@ZDzEpM{Y`H0SBIQY{BeRn^ zXYO2+2+0JMlIs}*Ot}RTfR_*4Va0#WFRrs8E zCHuPA8HyFlMOSsk_3z6)w7_)sje@-xw~IIw;IR!D+LyvGmGBt%fI(#9tstfZKbcj@ zsBWGInMS0?2L3BZnSykQc9JsJmKW$ME}LXQTDm|_%@ZPPa>yvXzStE zQnbkOncX5nLVl4~>rF@NJ3A*4VfAvSf$HU^mbuz|g&6<^XYySN6L#F;6A0*l# z2kAFp=`OaxGu3p(uEc6gMiaz<>{$JHhv9EAj?cDbia`Q>oSw6=lLSuFv2x zFi3XvxmSPPn$%<~Ap+DZYeC+@?G0Wq8TzR((BG4qEV${xGsc0*(Y;ZVofF?>$v0Xa zy8-hofp2QQJ}+aGCovRf%w>ppHgRMkz20yN&>m-gcqQ&Jj;7MBMvm~+sO+9Rdp0kI zVl3pb3$59KDpcK1aYKKQ*6|ejlqzSRhgz(zz`-=b$mSpl34mN{>^y#X6w}yzTTiL1 z5Fh}(JdoN=pnNv}IsXS9-|PuLB8NBN1EdvZMn+FJOqF9nOP$MADQ6@wa@})T@VQDC z@#6$gDX{;66w_ETRW-5kbqB@s(xA>Lc8o<1ec#)wDdhve2Fs>84sT|^9)9h*2X3Fu z5!(7w^RboyJSUc(JmnO+5RbW2JV@?`-+(`(Mt7ef1&rJ_it|1S7{;}7a_%?*DB*LY zj4*A6a%y)^klfUVdw6tT7JTp<)?R2XtG~1Y5P(NV?1+5#GctQ_WoZeP8+%gL$KN{* zIU{DM8}Pr_P>k}^f&_Pg8BtsgLTFXiw`kU`p`px)VoTsx1mNx$e9j4MgDW_nQK}4F zU1b#PU$nJ(;C=S@_vbX*^NtF4By28S(eBjR)&^fFD8Ni6b@h=XlNeWoPVIF#BY=#>BF`I$ak6TZ*;BOm*>IaIROkKz9e#ieiwuk~nXr*F z=-tb;nT}b-eGD>WQf~g})WO$OgiCB!#*F@q-(HuG;??XVRx|S#I+qW??MQJs=TNt$ zA8ygol^cDZ4idR)bfPl8K=)Athd1aGJchZaMwz1RH5V?dR+Lv(o(z>`Ur&cA8kS)} zf(d@HrY9S-=|9Qv@1c3l94jM)wVm%iuT-ZI{;4pPp*}H=TU%S&Ws--0e zN1iWAx$^jVP)%FilQ*XNOl%ya0`X&5k~Jj%3KQDTwO)?)`h(kK>STzoDs5MUORfYH5X@ zi;HyVBlw9=eir9Lw?sApBEvM)(Bo{s9SXNJ(fD8v+F-M0Py;Ker&Z{< zgzr3CCSulM&9)zdtc`N!0*72paN{t{$k=u7UJLd$?!o5SEC(aWGZ_#JL z>DWo0L3cmGm2?J%W=hKJ@d0WY8uOLpq+|i+vh?ETDoSFxiD7y?e5(g(u$cS{?Xm0d8WDU>=UpX0%FOU7L~P6@4adW`$P- z$CIkp_#Igp%F9iH({Vp)ceYi^OGtd0eM@kcz;xKYKm|LpPfc|bZ1}frp zJFh)1SP5u@y`#P38`RDV=HgMiCbdB&jxLU}`*8Vv=hYbK2DxY zO0ItzKJYgbFCnz(`bsA(fk+jOTaFOPx@1Yxv)6Cl+?tT=8TV4*B-xwgZ!XH-P0u!s zrh;7YI-mNhk4QfGq~Q0i;8MqGM?f!|7J;Ome^>IL{RL8hB{z?57~0& z%$d+LYDEx-4IIy0!&H!HH)3KWUopuKW9i;baVAXdzKds8V9Oe!Du^&DZhMsUSG#d_NyDLb*=nP672WH-;jGdCK)eqbR02C%Og)cZT7dL^X1ZEuEqh zf8scS-9?(F(&eNWps8B)c1VPy0p9rFgbfw|V7zed+_|S=>aV~1i}L^cD&{9%=R=lkDlj8e>LPB6O zpNhiLH1G*b-cbucO%&;2Ke_fe1p&trn;Vf(l8%jn;lSzYfIGhUR2F0H4%&V!^!|z; zpxsWRszQn?1i9m|9eu_Du+D^x@wrk^P;hJG!I_Gc0#k4V$B2gMkd?5!2 zM8w)}7ogki=?A}x$%l+1m|bYjGzH&iS(g+BW3VjLG+=h&J=a@6q-&f9CvY~sR4LAw z7t>r?fl-LhuOC>A+M;2B8;_-eb3^5zT5Oo-C5Y6D3Dnw!g|EJb%uGm0(fD9&yKFrX zG2@x<_4rOpt%G(s0JWND4^He-rk^miee;;56lju zLRun5NRhY-#N2uHLcev}=Rl$etr!XZyznjx*kk&q zM0^?P_`4nY2NdW+{KA=PAZBXXjxE2ZX-&RV{UGep3%u2j>R(Hdoh_2;ERCUphb!7V zz9>oklaN1crGcuG9!7=ohZh4N-WJ3`!?&~bv&sDP4f`oMiXSnd^Orq~8Ub+VMv^9u zJUGm3;`Rhr|4Gy$Vqzicq!{{>sK+})=5Sw8xk3(zIT0-bN(ys4^6O-S(T4BrBpFb>ivYM;h}^5A&5;nWqub~EHh zIBB>Lj7w*8HZU<+1$AcGuIp0%f6yCgZ-BW+QW)I->QPK4=ik))Wi<^zY;Z6HE>Q_S zBU_H{AUXN&k-ZYVnX}`fXMcw=T(W|GssZ*yY+W)1{RvQSxlI8+$NspdV0=*6>2QY% zh$G?@_1*^qec*D&Y$Om2(X-RE_)RF`nBegH^t`Fb6`V-YAj{5sI@;Q%g{}@TSUi7z zQRoB6qgKDiR0cnK9?6I-Mh{&!`vGz6;F`l;=zw=+vy@c%R8rdKGKs8;y3ud#VM;je zgdCMxr`XogLabsRJYe)`PwE_iTZfCIGFY3nwPQ#kr_KO^bbLUAG!L&FsA|2wey_~t zc}0b>ff(Vu4kjS3W(xk_ET#5Ub_Y{vLg1f7L{bQ@JT|J~|`fPZtPEMq53T{|_$VPyB%2Z4=r!eAsTPC1c4{wR9~cw?`CV0r}u zZPSF)m>zsvGMyM^;<>TW;to0sRSo|XH_9N{{TkU{h0qxNnW2(?JhY7HHXEM9vQ zJtX%!oj4IMOq{{Uqw%LMVbQTTzeFG|V|4 zGeO7#oGVs*boa*T_kDn1MG`3zIS|_n{CKe9z}xVX$5Zqi0}f@Jf58fTMUTMd(n<7 z*;EdZCTVD2y(&C01^50i&-xW;cD15hx+-D3jE5(EGU*s}IoS3aTl{v(Jv{>oqk+iz z&XFzB#a@uhp($j|ZfZCTg%R|L%nl&XBd-ZquyACfVQ|sHLj?Ov6yT`?*9&ouy5|B# z=di)BX)=3%18R#n@bE!`YuS|P#6+URXA&UdTJE=W6#0c1WWQQ6MjC)d!(oCRW4oi} z?;@;{4Vy?M26TB^?7BGil(3eOlknLYJr1JrH7D7so+y5n9LzpM_}lYT$Jf4j{yZJS zlm70T$3|hhrfLi{y?HciG{6NITHr;e8fOKm;|H8|C!PB@F^c8PYVZ~mdt&j+r&1(> z!-o!K``B1pgYoTXYcu)XORyceGn9gR_4l27;93OYMu#$l90$cl>39`FxQo zt0s2`=Zteaf=43QmPE;F5Oq#Y`=Aj792AX5MM#|EG4zJcX3{1!3!gP__1~j6RaI^{ zRu}sOpDMaK)e#-(yIBR8aCvBgu(P8FjLxMy8xNMw9y<2-c$EDGJLcprUysXo;s@awmLk>VZ&) zE)4u^m!jTNg#o6R5|E3e91D$kB5|;8}UKaSw@Uaw4-i?5N2mf zimn;Z)>{2(;o!bZfJOVT)ZPF6K&oZn#|;OHZ%$X?XQOw`aX$35JXkP7%i79Hu>5@+ z7aOf{%h9YyxWeCXD^NzQ0e0ze@ePcrU;Q~c%EifP_u2_iY;ce8L-$T)Ebclh_2xK7AN-YvGOL0^m=4sFDMr1Q8Z5Dnp5NQomu4V#I-B_k1$RkGYO&hS1HY&`O9fr>xJnljxHNM$0|by@aWOjoYU)I#f%CM z4j7e1S8#6OMRgcKk{R{QZF}9f__#O>+>>Ffo)m*Ltmjg{`AZNn2Z`@9TT_Yc9zCspMn(!9_h7kpmnsq zZRwmTNz0P<%nj?-9fr~#?JSwEk|zASF<)S8 z{|j_Fyfc@(lbY-C*~JUjoTh9dJ}r>>t2H!_%F=JMeJOPU&g=g4>b-% zD_$Nxlb^mb*@hC1^z?q2sF+xqmm_&~H%&vNIl$o_aj3C3kWwW`uwj6Y*0=aSpMnK* z|B80j(0AH=bv7_EDga=`J!4(D5kg13_cc@3{s+HcGZIq`9FP$aHL=l>@&>75qN3dL zE-9m3+Su7sKjBXh(aPD=hzJ3t>)P~hebIvz37(MM*^RG*LYKJ*+7Hv7K1{F-_IhHn z3uP80;K`CO?w7s;ofXT*`F~iD);wY8t?*@=N2svLdF7*5NDkw80p@lOUWEijcKx&B z1g-k}D@aUNoyL7Z~u#}bdf_X#ZDy#_FvvUX)Am+^!mXbg;?OO{CkVZ?K<}d zbU`Lk|Bq~D8|C^gE>dnis?JIpxi;0P@y8-m1I3aEr0?L`X34)_7fSAdriMlfrrwpd zV>3Y)=#*nykNLBaSM~k-KvFi9QmbFQcnvZVuzTW$(!=8}E=;7&2e{s2hqNf+Ktd0_ zKvtr90U3pQj@gTPZp%NnqO7|g9qZ*(s1~8wdpO~F?g+M~z=9e=J}Yb{pDOIta3eFF zUWCsK;-jHo%2c`;t?}Fzr4aP$qX@$47gnC3rriv7b^((Pz3aDmotOs|)9kVTdmoU8 z_xePjg&98yU!I-PYG}BOkBx9}6Y&%HLzePO6e@p=68D=II@Pd6_1lKZi%y}eyw zvfGyO-AocJvJG;Qo=zBd>dE({%PyhB+toNKZOL(E0%I- z`mqV*hF7eT)eu;t#^`PuPFU~JuAiBhR@Asmxv~De$@c9DI2@xZsPs7y# zZ1mYgUUbt)c&2r!9;($8&a&Sensd5R_8<0-$0EHWdlIvs%woqEiIGc_q}1F}ydj2m zi-vhC8!oKHcQKkv_HMJ^CGMoA;dq;8`TQDAd>6rv0kJ1ee4?(hw@Vvlr}f7NPZQpa@`4NRM?&j2VUB%P8Hibr;GvR6&Rhe9}=;jd_h0A zopma&T24p71%tTMVPxNg%%ijMbD26zyEJB3`^A%i5OM772%ow)Vk>dYDw=o_JG(^W zt`@tKs->+RZ-hMY9Z1MdT#yp6h#a$pYbegg{o>J;07Tu> z`r^gT1IZ(QCRpH z-cI|c&z@=PXCK9bx0odbIF#DKe(|bh*OUVYo!;1l|!Pj0((SxUou$s6++ZyKeP z-S#YKowp@%56~Hpc|O11b+G#;I;k9cxen$k_CRLo*0nDH0qtU4zNHQaj&Bu z%NXoGVbHsVVr+}D;Nc{h5;Ex!(edz*|BRqF-uP(=k&62X*7>0c$l%fG%xL^Hzj6`2 z2feUGyZI2}4byoN9}rvI)wLP3SXjhyE~1FK`2d|<>upTweEjASsf3Pmnvtv)L@nB$ zo}Nub;JQ;fctPx1k^2Pd`hK6rY}_i0QCJQ1pr99Xjh8lhqlo?pWho8Z>FrsgG(PY0 zC8oRjvh(teAnigw2eb?>IR4NiJAL+<8QimPYf<5-r2~7;pdip*q^(=w=6&>-F+vw6 zY;z|qa|4{15YR=8S{ylIs;e7oDXY~0X5_=_%nzld+wT6J$-*Cp25H-MED415(zY9#QN2hJx%j=Fe1I19w}{&etadDGSaP z-|tqU3FuNOcoiRj(#DyuUe4M1ZD1k;bt)JKT}4Vx(bNw1!R_2Is!{7$v~Zz*As(-Q zn23nQvy3($E;cqNG<2hGzeSfKJk28kS2%(QIIwl2+esOfPnM8bt`6C|D;3Ir@GJ| z*dKw60VMmSggz9$Eyup1jeaq~qBonpi&zs`@>O5FaU(siI-JSEqNeLgCHmD8N6fD& zkF*`hVr&MLweZ`^g_WE5%YbNzucfu9h~{+qB*>hE9?Py2xEVIq!2(vm!T8AizMv*^ zGHb@cVThX#nX*ng+O9f7c-e!3`~~IPCQM#P&xHKdS2-z*i83DDjlttWs+H0vX4oed zfrejXL53)B1FVsepV^V05qNiGp7QX30iRl1brTz118Qy_RdePAvN3UrR=;Dn9NL(Qn1!jLIwbaRw$jy;DmBB;*vC% zaJLp;KoeBIkln`w9lo?;?hsz)?Gh9S+i1(r)9D+&o^{kxhX^e}&fCi?LH^f%@ppwU z8BoWj41;)En>Cg0@x$X~Nhgma;kE8m7`81R`VKHoSgoZX@8E@RE|at$>-35(vB?L6 zew5!I!O8W11=-UY12TF9CN(e#%hiku{6I|DCQqWt2rv*AHJAlFYjFL$w)6wczDEI` zU1$2P)8-lK0s*xsK?dGGANJY#BpTe`ZHNOW*^`Cm*H*;fWbus<{~aY5AS@`qAM+H3 zImN~cew9K6o0lTxz3JkkT*PkGy5wtb`jKA=NV_CMK{VB{*#dcsuB9-RO;yRc#vo2b!`V_md}EbKwB z*tZ%}H{9qABEcX{Lr*tv@ya?7lIXjEt;PK7ty{@9A`Ez=FfOB;E{!)Z+O8HocaR5S zuV7Hb#}|Wlf~KZcCp2!Fyo)z&@bl;0m!-peHhFOBBqb$9wp5}2AZ-*0pT^9~s!jHl z7uNt7UOI}HXyNBChz@F?&vzioy%kj=c%AodwJbxMv^8dUc)0P?-MDv)Xv~UpVli?j z)Tc3->H(dT2%;?2`%g&YJ+-%nnck(n1(%NCM6+nAtw|}58N@)a7Xlj8Vv#?a$=Ga% zb8qExI(@aGy_MCTEOW&NSr2zmm(Aum1^idNXB`D&B>Lj{dYb0kKshWK`w>(@YTuYN z#`TrJb7k5y@a5u=0ym23bV*0^H7xd65F3h?MO_Q1@cmq0euM1}9If1R}%`cgW|_ zos!1ZJ#VWvH6l;4i1?bFaDmXw^~83WZ1=VBM67xCEE!!_m_TTK=ld6vmv;3Ve!cz} z)gw|!5h|Pk9}dGhjHyAZaS{c#%#zi+((#FPDa+;^I(0CKq_NR3MB0!y^1$dUy=CE0 zR|uT=aa!H1!WYdFmZ){W{#VZ6Ph3AiEfYn8aVig8B9K73SUZxC@d&AzyO5B{@5K|W z^Q)}X@DpSw5Q@b&gaDFhMC?oc&0`|3hrYyI-?lL&95)5l&MR%p_RR;` zNE+jWwRqn~lm;X1UN9+Fw;na>WbL z&uwIbLq}%Ly!io{fkxg+zRt5*de;*JB{J79Umk%U68MlCvTA4OO-nz|jfng9(tMXr z#{LJF+*)i8+R1zn^65gYnzHrC7$+zoEOK+J?2}rd)&5!BjI7&que>MaVQS zijtJfbn+c-=Se$^?SIpF32=}!y4~u zmzT`lBIIyA-+OpPeQCYXq5IiM?K0k*`Z}&*(g2CUU3RhZ9_h`KJ9GRKONQkJE^B1< zUN*QaHlk_Iux*za-O1olJH`8E;I<1uW9$JEWvJwCeOlJKp-JcjsR! z?eS_)awv0ZPvWdt(bRu#r!6awLp$@+i01d}anBIQnd%J%4x`sC+hu<9UoFU%{uSmk zc8;ql+iXJhaC^bQLR`2_NXhN|?Xev*J$K%?JWyJnd_h*NUFO^6-MKH9^L)7+>93^9 zyxz^qSPXQ$-udOtt5<(WT1Qizw^a+uLJk|isxhZ81nJbRqkhH8yM+r_`0pTnUcG$)*s~<3r+Xs!R2c9ldhZGre+iH`T6M*6Ljkd&+(ciz(dENpw4wTbPJa@GZ z2&EXHg$)o;-97|?=D|XrU(XLfmzFwd0Q%>WdBv*4T#QIR>bO+_fe~OKIrC;xNAKZ( z2Mp=!q`k(TgC<9uTDwbFMG(KxT0(*|A$Pd}PUB_isV+~F+FzAd8LAhkTNKI_ z`Jv&4w)VmM*=W_}FKf|qG{10HJN&$P{vK^S*H8! zuYRa|Zl~EQ^Rz8dHhP`iEQ_J~B#p~p{g~?5*7Vgib(Lb@B5l^M{?_8x+Y&R+wpq8! z+&yyNVNYQI`9B^ytH%2$8nwtgZC?*R=h2;dbBY~CUS468*4v!9`@~rue zW7iKBs>g_WWa+e9$A~sZeqgPOlXUuR^kN#)`=4*GI8Zul zu%lGZ&Z&mocBXf4QRYhw6W(G!7qsA<^L(&fu1@J=|SEOGW|o5*)8h zR7q114D)WxkRL>^r}@j;`7|JHOyRBv(q^LzF%mQ{Bm1Pb&%<`5{rH!+q>JqDf+1Ii zC_Xq*YcY*Pc1af}r~6oQqeu#n92?-n1Dt`Y;OYAP1u}C>I{pm&OqNk*)Il4pdw(%$ zqE4z7i;)|*Ml6=oF?6Q==;C+S^1YK(rGhA&y@&?zN6@&x)1e1w*L)~Lx8Gn8H&G)l z--!tvh*x)X4^(__PW7;Qs9YXQx`;@)wokCZN$p@c5cJch(3KZI@cu2Mh&|BE zM31iQg^5GU>9(n@H%mC<1IPj+aE~v)@ZDmg6ABOpar;jQ&GZv|4)}9%ROQ(!&fh=_ zmqgZ6at?Tja4UNKtosrb59|7Ci9FTp>XMsSX$VV6Hi!}}*IQh^L=&ypW*~R1wOJt? ztmiFowrmE1@n<-0$u|AHW7k1rfzkvuon2L5s)w<2fLG&p15KGX7zeZ$(=hBDFd|`` zTmEJ5db={RN&!6}+qEtbu%DHKY8HxAq0w#=M9+a+21@|WfXw(C7t-JxfURbVL)A*i zcmQU$eNYf$Ro=w!&dI|kGuBWtQP7N%frNj4k{&(wy{PXaDejB6aZzh;9kp6cM-||B z>hP)xzO=`}>4xd9=r^DO{Az>X_{;8?dKZz{phU;?fR%8cGX_R*y9b@^y08K#_?Ayk zsH{j`YfH;YLlIeuUV-J0qu?hucs?DT;x*GYp8A4n!XlnyjyTwnF2J2)=$8|JTWtWI z&fYEWz3UaI80}w9`3O6jjyW_%8%!HEE}=mdn4|>Ci&mios3vrwlz|)qAqY_?qkU3j zzI!RA_CJ1|J=RG`3}Qp)`y#1@G^UA}X36iBV(Te-)*ecVir(n-Gc7PN72vaeDF2~J zg$O-IQ&KVuJW!sy#8Mh!%e(YO2*U7k=F6az`W`9=IJE^mPZQxmw@o3Dz}a_PuD&=s z#TC3OOHeUPJVbqXdR$&%Lw3g&UQ4WfEdgxH`+l042ECl{#*G*0d%O-r-MpEhGO1nm zv`82$*L8C+ePmEk|^Ar(4MhK@mYXn8VkgMS~s7zZb+{$3-{i1`W?35aVmS(3_F!C6WDP9 zJ@b{WNykO3a`UuY=I*tqT1Xh?gN&}Lf-^+8=(fr`f9n_Qj(nH26w?rJ6FcbH2&8Ik zo?YLPnS^yVl8oh|*ebhKJ`7LS+k8wz}_8EOULc}*u!z95~7^LGi@(0)7kwd4S zO|b-)YI}W(`}%Pb1`pl99pWxhD|S)~uW4_;aSLPr??&ow-;{4f$t7dAif%g>#{Xtt zzp#gfyvj7)yVq-+JtkRTmXz+c1Yvxa<9*#GE_x;AF-}$pi)c;r>?q9n(Xl{0d)Uq4 zOUzgutY4Kea7Cx6Vm}<=U18CywTK3sR4^r~e*x8d7n#gOpH=&OUHv%5$->};9PaBHtt4T2Kzm8i z5!4qK3v$`(fYbMv~)w?vyT}~U9v3qyf>{Y>F3>N>%$;d>m#$@ep3^g9!d@VrG)iWLw zkvv9+n`e&jtd3Yo7g5v^RcFo7`whVuTrljWE=~i4Y|1chgwQmHGZ?t{fvm7@^t&!1 zQ$Z5#M7+}1ua_(t+ZJYbkDHAxR=oJ&>#vr}AA+0k9BkS2eeXgYWSPO;`FV+gJJi+Z z#w5ragdUF96uTA`r8RGrjOJwhnqVCST~_Z3Dqf9==KhHb ztB74nmPJihP(>s#uBLfwJ|~F#mkkXZC6r*5HYYXrK-U5JkGgFulLzOrTJ-0=RJweK z)Wi>S%h=0`7cb?NXW|xHzc{>#jvZ6#jA<&9P2FMSo6{dbLF7NiCF_?8?01GJ~dVoK=a<$h`3@`5N3VO4y$ov2Zum>gY(7iir<`n2; zKzJaqnJ;QpE|SLEj=G!)#3lP5Gg(w-Aso4|9m&Bh>kkPG%X?&iUl}p{ZFDpT z)qp`I|g-(bsCfeuNT?Wb@9V=@Elt>UWC0$`Ww>lng zRPIw<;lXv;2#W~;m>;i=QIGKT#MGCD zeuyqH>c95w5)RU+fQ{pJFz zCIO8f`z}7KD?wpjsD}G1T4;R>yAQB&4g?@tg5t#=F=~gklXoG>z2I1giBM&cy2<)| z?$jutnUj(b$`MMP<%(~#5!us+*VjKVFz@q%h^Aa?Z7uP{j*q%a@GY2401PG#LOB>o zLAr*py1w-r?orXVCR0QQ5VIGTB)^Iv_Q^*>(G>-_S+>&b1Pq7n*)oWenuIJ3cJ75XzVqg%-`AaK#7GFZ?n*949L;EKD1s`#xf$r)`xV zt{01vvYtADzLTONBH1}P#AvdOSC5Z$F4NP+ix`lJOcxoX>xXVA>CZLPJ^&SL@22KW zinAFrW}s^c=Ff2T-##^A`~J|HWEdb%E)x6(N1IU&nlYlL4@amlSk8 z_GMcm1@> zqeA>&OZ8g+UUxHQ;>xx^N=~*xXoog#^5Bw%Mu_>24N0Mynd>>fe)-Y@OS9<)IJ9Fp zy$e*ToLnHbZ)*nd*FBF0JLL8d3fO&BkR5Q-9E)Z^xY$3hsv3Y6xFEX%T3BRN z5~jgl-j2d(PsZD#>np&Jp4%zWf{XK2{+iDgb-Uk0-FxCw9k_#3HEJ6N5I*DSAD~&Gkkyfc!zRvHYN!(Mu!ta`x5`3KDS#TI%Z| zHNZ=$2d>KyPKEd1MX(YYAI@@tHy$s98bsRSF^j!5pw55)!6F*zoK@o`x;)+d$v(*W zSMX5nCy0XGz85sW|NavQFk`FHwhEvACoUfv7ffS71Eew3PxEG0LHGgN0xk$HhW!O) zlE{|SKlz`u{2?p>U4C6gm>j!0(C537ND03P3^+nLj+>@w>wFT*?;Q&xCwM0{YOA}{ zT`4qS|MSUG{(_OlU^x=jo0@FL`*;gFrXt3_${94h*zW2B6@q-#nC_(H6TOWfMQ;mm zhJr#NY=?EGpQj&&zfa38J7Xz(CNwN8IUryOub6}J-^;7T1_h%E$k-&!9v*VPi&;#x zVTog%{jMnKs^PP}N<$Q2SIB3LqW$g>f>08?`^ z+`0*piOks@KEHnc^nx=ns2H>*2X~x+I!%84dI#V|(4QHNs946KmYp-zvshfoKfj`s zy{fkMJCm8vM>at7@s(IsEY#{eJT!_IoQ4U;RV!BPMGw1JQfJpLqu5Dg7kyP~cT1)T z*4XQ~wq5GQpx6UA@l;HGB-Xa(KNQQz7F`oyeo=4{dND*o4~BDLK2FAwG_?yq@P9}7m1h_SM$1}NZ`kR zQ$Z{xN|Tw8nam~v5nlvKD(S!gM+#7aS^oP+X9EN06){i@8Ic4<9w!Upr!$G;5|rL| z@6c<$NaRNUeh9pX-sa0s_i;p)PJcbg@G9gT(?!T9(U=V)G$%ss{O9w?IEOl1G1LC< zN6to(=Mj$;J=4;V|NI1GM*boOTHug0{O9VDx>90e-v(TVnW*u;eJNTbmNPJ!N7$s3 zWeh;3nvUPd;V-jil1sN`FJ*sL#=sIzIFqsQ@@A$l$2*v~Zr9D9&m~JlT1nt8FE8gu zA@FO8D8t!Z!Y$?3p`Csl7M4ZeF*jja`0xFOSxcfJSv?YO><+^I35uL z1U3NN^@kGY?J1Q4g$_wDM3KKvXF&v$pJ?dS{A`1N59xb$cb_Hguc&iQHj+L`=*Z#5G2*hjxg6znR&8RT=C5A4 zEvf-jvk~bh2I>=Gg$HkoA`G1zbwZW324)v)1+_ zgiAC>GqO427pRE2^X5Iz`3O$;Z@}K}4zbPUY7ck!KcYBGD=YRXQCwU0!C7}v7Ht#N zY^(k!!h8LYBRX}%Vz>iLhid^5k@e40V+mCISnI!I}%>l!tF!(7KC1@u6~T9B^v4P z*|Rq50(s~rKtRwI;t8u-D{kGIu>J_4eL!y|$RfHyN8o~R_s2i00x`VVVt(583hL-d&8a2s4+<> zhJIgUq0V)wwJ2Q;1@i?B{|&?s)@~Ir-j&r1#iz(fM;~Wt_(l{h=MYiWma+c=3(%Uh zb^+28&UwQ*ko@8eJ_t0L5YdiB5vu^$q808NvV#d&D%JMsMe<1`64$j?3HREqWe_ZSOxu=GnMaEB@d*+*i2y1gq~)JkI$v3$}f# z0RX5P9T=>f3hsaZJHm>yNwnxxEN27ghXQ)>HpE{x`py}pjKXtD{<*nQiDzK-2zhOZ z+vn8|>YRZ%AA4KQj_yb)Ko91U3dRa4XH->G=W?8JV$$JI@(6zpdA+QkB?DauUS8hT zqA!C(Lo3#s80hP7i?=-m^KN+8R_lymei9;(#e7Qp$MrFz0MaHj2ty3eD=sNzUpX6! z3_rm=zD~>yr$+ihA$ut-Sug8^xe*C2cnd&%{Thx&t5HJ5s1qYK=Cy)#`(JR&99_wJ zedEEiqjMNW*VgTkpFf8f6--~HMakZH{&S4&RF4&YC@Lw9=t(&8D|i&Tj~oVm7-(ST zlf}lvI%13VfYwPO@`}V~2ob=hh9v-+EWU2Yu~WART~+6ImCMnS`UEV0`0!yU`k32L z$lvb*rcYV}Hw1Vh%$YL>Xx4f`P-x<$4lMZr=Q1)fh#6%HlmUhhB*6D>f&A+oN?nEo z?%cMbjSy=hp_4BmV+y6=cs`e!01#UAObYA`7ZYFg&;$rW#gDpEQd{V{(m|G8ilYCTO;GdL?N%YQD|K@hRYZKbe!Po3oRW-^fau9T96 z6NY7a@U@mzJ$P`9>=@b*Q7$dm8?_EUeHR8zLPM?RU4l9(d6KLRGXZ#V>PkUnrl8;n z!TQk#t`t^K_7MG(g+K+#0zde=D%D`PH>ay|xyCY5VqA}mBol;iTtHX+Nb!e{AgR8& zqBMshfsCMBl_#^`F`18#7$$-uN$ax3{XO~}(t{ksBcS(W9onvhhwEWqC-{d|^_ix! zCa++d>;UBjGaoP4YtHS6ns2A*8|eWjuoRqGasXT_6j@nWd%r5;vULzIT869uyin1k zj;6;hu|0NXTcj;?!BXr*B%liGDBTZq-)yxd!~_`2_aNeYYd!S7udgXVljC}0S7m#< z>AhA|Hs-C`R*4_5ZsGQ+T2oN7c5exN-T?mic*DFobL?`}63;HipoY}1?;7_e&^xWg zoA_F^6K26zjzBY)gB}|=;qj!<#>PejTF5(dcxP#AKL_2B^ZUSjR@^_TfcC5-ERj6t}~%@X0j{*41R=hGe54t#}x0-K3FJ+#E#D+bC+OAj48qEY_{#})%sJ}?Ht;QQ{fd)UU+yNom< zSCS2Z>brrzQ%xtq)!Co`!`W#tRc&`O4@u>Me?N>Pq-db@UzI!&TSK`%-&N}+N;2H8Ikti46`?1RVh{zUewHC)T<&lI`^zGVDyVsYlRO55<>A%BA2{QxX{~;rX zL=keqrms4cIpx!taIzmXN7MG%;#RyVA?il7ax27r)x~|4V>W}2RSuQ|Fr&}fg?w%~ zn(aOnR;>`g8VXas)iJEmFD`l$HR4KgKHNYZHKD;0l9C0c@-LyF%FVS!eBcaG)PE#q z2Rz&bFRD%zv-2Cq;B$h!#n6-=IdcBLzigdOj_I~JK$@u!x%@udTQR84tM><-5Y8GQTp?bX0-9Go&jf`Vy;3BtPdK^-1WX(0Sk z?*R^hSF_J=syN4-N3c*^67CYoX-thYh;avkqo}$SCbNnQ4rd?0FQuSo&vk_eI@y6Z z<*lKx)}2)A0j8P|;y6LpN(Ed-h^EdpMOa?9A5uQ8bqf;3wld6-N#XY44t1pP9y}PO z|EbJHWI{+l4$D6`TL($k|9)C`aSSNofeQ1}1TwqeiCbLbn~G>)>-k`j%Fh+%sK)o+ z)M1f7wmx;=Npb;%u1}sYnDDLsbim)AhEKxg>>C{};wR6^-mwY2okXTIw6{mbJa7%! ze126T&wy>phgPZR#={uwjBXBkj5Xnb!<{+Xu;nrABZaUZ@awC&{wszshP)ttm}-*} zOf?rf_2%t#0TKc&0lfbHER-)QDR)D56Giqe6#g`fq3~Vw2uUFkveC2<<_R&zu^(Zj z-x`K-yMSy#dJ4X*#?64TT>)zw+kUIT6kdX#ofp6XNtvgk(E@53pXFN@- z{W`UIUslxg_dv}~c8@_`zG`Y%zkw3)<@4vG4t1y=ls?QLoflgB?;ktROoQ%#kiHO% zESOx*KYHwVe62`A!^yT&pS&9j?&7Zl(2&iMg{Ko}FC~0rynt85p$pBgysBLY~5kBasS$xx$a$uSBzhH3cAoQOwQi1P!p( zF9b?k$6UX@25v#SZA5BLud21U8g!i)cyOm5vw~;w#K#g+F}@V~MUNVvjYSSS+jUUv zLjikqJbtd@=S=Qv8~!TE>jb%m7_; zaF+UymMrb`PMvxoZZb^G-YtF_Y@F^&c6)n!j;953v-l#D3o<+ZG{%BC=g$alq~B)U zDd=mNARsJ!GUpj!k=0|2tJJFx^Jn{pbGv^*B?ZkGNt9O|EKy^+Tj$WIP&zw6)`KF(^7~YhX{wEAods^6~U$F;UGc zn4lFMKfeIluEHv)8L>)0tHQdN2wrP51VPnq{TPq3%@Bh7cE<&*Dv(Ln8*tkRYzMXR zDqIVHJA8teAYoyK_VmVWp@gy_0j-=Y2i32Ra2yL0$;;E|pIQB}zj*OglB9$L#$%ni z%1XQgz+0=J1~N5sIFL31HEbx`!FGSc^WszHWPU+GLp0P*d>I_PI@rV_euB?875&3J zjMXX7I0Tgq7%`SmRTY)2X|7}eX$S!b^(i_~vCXuZoYJmOAfB-8K*b#OEn9T;oIk{i z-oOaNv#{&xohFG>0>r-Hz~LWD(jp@pZWN+VSuY#JBH~5L_~n0>6Oj9*zvD~TmTtY+ zB&J;4vhl$&AR9|dUfOkpvGfu3EG5AC3se;k&mK&{aY~dpjwb1j%xI}<@IA{sslw=| zcpM$5P`IJ7W!ZOTM<>XxU~=$54{)l;1ceNhh5jArUcvK~gy@+dKdm5*OeLpUlOJ?x zA;6_>+wiw<&d7hYzu8oSqzm@DvMAk({Z&^bFk&n~5g$jqu@CN){TIXPHgPD0-sY?c zmnGU)b47fN!|!h;?#k6Jyb@l0b5#Sx;UAs}5bo{@a* zfhKUD?vB~T`ODio;Z|yrRNqSQ;tzYUg<#5z$Na}`B5fuK(h^KU>{DHvX1IscTJi%$ zQ^pPO!oZ4s7&VGWGpWt>_fpU5ksrLPXC(%7^s1ygzNEXjG~vDSVQ-0X?fRy;gJvk? zYzZ3YKgGbh4{FEAVp#SI-On!X`*1vOa!tryep)5w4X{XK55fxtoP{pJre>ue3*Y@L zeyMnG0DRog?t~m+=%E?K5?cb!Mm)(go=WEN{AXhh;!PXL#}L7uJn$s^rjldGwb?Zz4BYgJ@~Z7 zF|G7a88l2=?u#7p2tuL#AFMyzUsbi4nq3`^EZOdp;zX8&y^uC*C~Wfedv*YxK>#pA zkPwVC0ee2gt3SK}sE}R{ zZH{e1TwI*fyoA#j-=5g%l19y`-%(m*lePZeS%7Em;l%vWl)9<56Rs%bQykkgL9&{IM_y&j|5Zp^*4j)6c?V=*Khj*a^YPj+8;?F)=2V(POaPJ zy1Ul$ArcXQ2Dz9YHSg#en@E%6Xvbw{p#pRAb^Hp~fd*dgmLxwvzqZr5L*?Ep%ZBvP zHH;o1+5Rt>b=X3QsPlE_5J=aSzfX~mgwDjfc5on?M{qOMvVvwKrG^;(*ZX?3OmXH| zMq1k6@-sXM=b_aGqoJPYnpR?ZS38r7VzL$)>~5sG(TUJ=RfRxYzyqlFA#2mW(fl=? zzB?0`yMW;e4i&uDjbEc1^@Pg6vJ!+q7`z?ss+Z&+Iv!`*7K+{*rsteRMp~Vt!p}BS zv~7OW@%wT+S2H&2MQ4eU@{{ceWNRFX&V<@ynsc^VE!67<&eIg~8Q+Y~y88N+T?jyvlL+v8$I-l( zsNbjlt@(b>&o=`5Qtc#-SmB=H{qJ;}YkDk^Amedp3q~rQoUh80c%TtKmcw3*R|DawkG6-te|%0LGS=NxLr-7g+M?_H2W%6X`%%ssl1;iu;79_ArIv5< zMb+ie2@Q>mZLZM&*46R(agRP7HQtHz2;_lWYS-2aqX2}8xt*h(WBy`td)HBv zHKKM{cIglG2+$Ik4gga0zyWASC$9Y&Kg=2mKv0*xAI%=Zv1rALol2|&M`(9D+}1Xb zJC-ALyypY0FHPMh=lK>q5{mlRQmb>=I2)Lt+gt9dzpM;{war*Qss?cbj1|t8FaJbnkc@lq3Hf^z{l{V? zG^wKe@D4u7_;+g$H6cGZ!DOe3*gLDs3=c!FqM&!%4V(WElZvD~z$U1}RQd%-m9$)t z-K9e27ZOVEJJ)LmH8?B-Vc&8pZ~U`C%VC?aVZ*)Pbi7lK4YN9fqP7)ToYXt|hKkrM zf9hsFYhfH8KP~$pKgzPZnvwM=fC)}4`pyx&j=`IGFeeY9>pq*SO;LP_G;g?7<*$wXLTfrre0ybl|{&k!lni!>MT-aKRn>H(eq2 z?6Tazi)St;a}%|7{9=@}K5JJairULtd3{qcv-#NE#BO?@zSDt5w1*Y&_&K(o(l<+{ z@fz&5A?SgvFnX-gO@bsyBJf0IKfXH21?A)5+oOd0HIN{Pb~%wvbfRFl%UH?2<6MqD z<3|Kl#7mP})h?91pGd&#oZ}|n0c))r3w01Zisf8B)jdSiMsE3CEwcXeD||UGUd;O3 z36j6HA7v^EWc<(5mj-ZHGPAO_Ic52)7JnYL zd)?fe+997BmHpxpTsLRUQYQ-_+)nt73Vy}~ekJtm#pdmzwey0swkW;E330MnYuxOa zD*1s(_UOT{`J`Xke8ij)Oqg~|R>n@R^+Ms_J*N8*(ym)K&~s{+U)MfdbQLn=3x}Vi z^ldhB&jic~Oz}Fy{#Zh6?E_zKeLZojHlLWmk&#CGUD+8ZxlWNT968MQ=|}w&OQulX z?eB;=TblhiGnuv@-}Qs8P!)|a{&KpU0>?B(-}LevprLG{^dU}28qKg>GBSN1KQ0nO zaxr)1&Km?7G$;@x7lRN9lF(q*4v@?!geJRlzf!Z$q~Wzg`p6;Mv(ZDPK_n%J1v>{l z6eifzIW)gw3t4>^`ZnXl-G`K!-1R&0(E%HY zk0w+HCB?;$pz&VL7|rqrNrC-iF|>AhpZCXT(KKhPJ?t0yzol~25?11EB!jLbP^l0jqn35#MNCeUn=Mja+gK@JafzT?9h2yu@}G8~Pgctu@H4LbGKU4R@ozWFuJfRqUA2 zdE?jxmx~IxdvKwHlO&E+#-g3KqN7*eKCa!KNI*1I27=6ihfrC2GRg z?W)m49%R`@Wd6Fi**=v+mO(CB9Th%E<=a>x7H_UZyt71s0@B%s#mf8Y&fw~AnkIg>17XG9mU?? z-wW9pY^_Gob==iq?V1-)2&&Txv374z^qbAgX>`2Bm#XvL)u8l>7_>)x`fEpprluye zceP>TTsq^07wE3!Rcq7>X4CDX!+QuNWxwDA&{~}v@tN20naqF=C4=ayqq&RFq=h4P zkSO~5_#lN9#tY?|uA81_FT2H+s+Q*Nu5NXt(73j?Ht^0kqLDEMnYi*#oqobxTmpnV zc#To3{2N9T80E0=i2(PDW7d-RRy1b==09OJ%P}+;Xk3!HRUGFnF_=f|7xiyuHa6bc zaPF-;RmcOaD=O#0jB&$>j+`~2^u=aNR%8urTDf-zgboWky997MRPnGvkX%F!0q{Wt zQPU{7C&0iAK094sa?KhxUQl9FzNt>Wo?pSMdE*UfT-JqN09?T6ryIgV?^RwJ3KHUq zc9BL1B|6*@T0)QkY-UEUnYWg)2h~y8LU(obs--FgTFjX)Oo%{ZK(R;`=7k*Fn zu*>*x%Sa&aLpEad>josjzxcI*5hrqQA7QcR=nId__e(`bxbPoEB9>z&&iOf5Y$Xj0 z=t}#s!;;WV%a&ma(jll6hR_+vTa@n}LdKm+3{+w3`C&U0uAnbvyt<;N3^Cz@RF- zS?u?W~S0QaG|1O1@V$JnRB z5n9?FcxfF!I`VbR&B1oZ;6L4S^*qnTC`tfOYX0}z*#0UiDy;4VCqdEl-8HC`b?ivqp}QtF2Z!6$&nbp z)uaH4)Jz)^3&HV^AcsGcOH&m-nlOAzaonYa-@}o_W}8B^CNrcl^f*iEfs|FPCWwR& zMPQIs|9&(9H9p)#2+E1kO6AuLHLdW%{KR0X!Y>LqvhHsfqlWLT{d~+FNAI)pFNJqw zNq%`XhY_$}(tiZ}fKeQf%lz(Byj{v|Xg!FYBFN$SOxyv?gy`@JkK4b>pcFRGAz=k7 zSvh$S5M@`tUo^x4Fq!wgbr3*=<)_lOtxtt56z?yt zRF8pN!@u1y)fntTtcXABbc*p(;v%E8kGF@ zptnwGC?)c@We4B|RqbP}Q054Qeq2TE*W@ITY`faz{sDBDUtCPU){Y(FN!DaZNS!FP z{s)S}6B7}mZk}bd&O8}e5{0L~$s$Y#Z(~j}=X?W0e*36nGPh~5$1nSZZ839CG%=F~ zZHgjnVED*qDJj=&6nfi7F^pD2?bqB~j2ZC?ze9h9m}!1hB(1)N5OLrwAllT89g&S{&MOcv>Awl~G&>7Zx;gpL+hK_6U=xqolZyJUl_7`)B^Qgu2ugf( znDLLy1%we`0(U;hQKIn$TB0qXy|*`E85e&3>LrBMERXc#iKmm%eWMU+{GV<1g_0f*xbA7ckJq%M%uSO|SUEZD;92(lUcQ3j^f5hC@j<3?w zSw2r6<#LwA$M=An>o0dpmFI3yTAzl!nnvlBj|pZJ+M+L~_wYhvz`IR81rcPaS76^v z)VL&^z7jgipK?h1v9u|;P<<26Eq}eh;@~5YaoPTfCzEC0yu#$ZJ0C-`t$Y_D4pT7E}b7OlA2B_!{% zZ+R}fVaNm7|u~wzFX$>QYW@`7pf~t0zSYPDC(Z#*hO3SW9SSN z;HBOwtPJRsaos=6Kt$$H!=n7cX0;9WT zjSrDwvcRz;j*Nm7R7iT>rt$3~mmjTNGkkq68Pc5aq@>3)E4(KOe)b9`J5wXMF0wyH zoUg$Vfwo8yz_}8nW~&UqZ}k}KDnjNQ6dWOgbfuOjOC7Zh4b{VAwp;AuaD>?Ma)*s4Hj~~DQ)He{;B4vN{9pu(>zUNt*l!^j}@^fBD z%|%{C^bXF|J`Bx_m955$2j=UHuBCpWBiPQp`l{$#xjH;+a#U!&zGM9omQPO@XvGf= znr44s#o_z>?gf92y{>YpqeAjM^=l4#57>xtmO7CRxbx50M;?9lYqxm~_n4C&@x^y0 zg5Cx9^<+yso{;uF1|aI(4taZfVUb66XkFiX_X+Lgjm>_yw9zl!TPv=#_h_QyL0G7E z(Q!oEFoe>P$S-so0E3~T8r(F0GdecBaiY5ZTyR=+Q%x%ng$zoRY&ikDPE)tRY1&}) zYHT_(j~Q%&OQX&)DmgiCdnggrU)RIOSV-A!r0h#}`BD~&)-lUUMCGCc zDmv$GPT}3T^Qd@D<^w8bP}>I`jEp!rq!9BFbP`$5Gvr;nD99-qbTEC0oM^)QcZU<_ zj6?jQs<3;!A#Jkf^hO&B2MNI9<97Gz8T(Tk__A6%= zA|v!(GX36wPIX1_#rV32Pi@*OCwTnl-4~Bxj(nJ=&WKi4K`nT$9^M!-?`g}j8F1); zo0h*pGyLq(D!(gafoU!*d->1@$8LlL?4H8ri%`N{k>`D1CK;8xhDTfP1BS$aAFJrR zbwmx~vsZ;|vTYO%h%+TUd_BZ6>XWkiJ3HcAvCD-E-Hp;8KXJ1O5Y?5CBW3`8GI%dd z0^fD}AxYX*RI1@*De)yts%WgruFO(~Az$u2bXHhIOG)5oN=nKF30=N}qN0u<#vwGZ zs1Ib$&^USW;SFPBinUUOfCDYTn085Q!dr~_8k2ahZa zumY*?Ho?J0avm{+>0=ceZwCSMYfA|n1yFS-IDvh@Nt&?% zEQld{3HpRhU%Sp&m!U3PzI)NO^?`4jqgPEhQ@TxP_f@TrC;mBK;a>MxG-1E#dKm;{ zUlf5B@GDMf92xCA6042TcQ5Icin?$!b#rUWL8yMS7NH66eZhdCe#}at>Nm)%M$;I= zkJcAdlclLgMq`R=3zy^-OC(-^`Nnknn#Ud}E=9cWk_lUcDR!8iOsqKTvMWJniULq% z2yEFVOv;yH5650hO-1$Oiz@mk5ZB6{KmlZDW#xuo-$PSaHI}&u<62NtLPvu7s&@`h z&2MA83$8p)WSr6wAt52~atu`L5TAnB*LljU)Cz3LDj@9==I z5ax+i{u0!q%x@r0PDoB}33=1hgvcaA_-m0zw=fuc-f83(@s-hY=zPAVXdc#a6Bs|+ z+&WnNQuyz@hx17DOd-puFS|YWpgDdebEDEnk!x5gh&PIp^>T+R$2{_U@g>yOz3fm( znnDF{V+WcLx|Z$E8d-De&P(xMP)8JB0K^NZVh5+CiRbS8pbJ4E(U@c&6_wVReFxy^ z3-s2Qrml>0zORY|EDI3vk zT!=tcAy6elOdO&fuWd~RMhaqkg;-kXjT9>vSBtd>4v6)|+m437_|@R}wSg*NYbepJ zbG@;Gas)dRtVQ0{!b?4~qB*?9|mppyyeCZP7gG&TOu;v!_n*nPPvZN%;I%i)WzjZ^7 zAU5Sz3EtymGMT28yehFfwpD_JfdACFb;H+L)}lT@CxM4*^)t2vt}=k9Jl0-8EQtY0 zf+$JlRz-O4!YnD4U<=2D{WB}pND^6OtNJgdRb}&aKS&ku&liY;2^>1jzZW4%GB-0L z=Ee5XBHN$QlJCL{WW9g8!VCnISaXoakjyVEoP>e!ITfN9W7T1_#tRtt-8McMnR9<@ zNzgrb|5A3{s3DL_+5QtKhv4G*ZAVfMgIUb$9xxigyC8q?JkTw4?jx8$G>gFM;3U;P z+??w!2@I5goC^5hKY);&>=uj5CHmHfiGh2o6qgV8HO=jqsu()<2rr%5-~VWP6-7CJ zP7aD(yxiv@*dgc{RQ~56LOCmjQIIz}yih~Ac1^JnswTa0U&yNO&#jw;LeB_H#u`e1 z3t<4I1ZYM4=O>v$K?!3^d1Ec&i3SZwy1^WeHMe1D@+t#9NdJ2oF39NOhE8#N-twmnGdNtjBc+E*7j#bK!J3`aEM4lm+q%Kulak1RMx1SKD~!cu4(95QKsAAxYvgW{3LZ#WXiH zfzyH(Or`H6%F*WLFFEGZz%v*Zw@*?MnuK|1L!_TB0-V6ih=e02JX!#c4HcUz^&kG4 z!|{xhYZ*KkW~2}rkU>GyV7 zBh+rNgbl6L#Q_H+9_YZ-`P*8rP;!GOM|FOD;u9*&dFyzL=M;s>snTO6mh)%ZUSy{^ zoIJmI;fH2R)=IWsVCB!`?}U(JY_jC7P$n_#%vzd>V z61M03wcsQXlp1n&oZ9MeO3@XdLEXrhFTmG-BHfqY5<*yDpu#UEBy`VkX!lonpHGen z4hoW;|GE{`V_nS1o+A6c1Sfx4p-41is*zba2S{gx$N0W*5HAAYwZ5Jn-%~^I+JLs0 z?K#{oR?!JmnEp#Q-&{>itw|Q=sr5xfLZhF+d+ME$J9N0r1`o*-7y|fAK3O&rb>g3e zg#xv4G9qn&!@GX&c3J59j+eaR9}LVfFaYQzk_wY!_?k{&^!rGeQt}A6yqP@xn7FuM z3g!Mx-(?q<42t{H+zw;E_1ETlEDQ@$e;JN6Z*(VOdy}|GSZ&mAYN6viRO|$WMm~e$3U0Yi08r7}G64s{E zwo$Y;kGC;WQD+%RYDFj?wZ7mL-(UFcTw?#!>(}uzjcE)?EnK;cNiK6emoLiA9T<8o zdwt_1mOj(F14dV__{K9yA~W!XuHR<%HtYh#Fw=h&P2pDr^%ZtK$r!Zs^v8S$%%JH8 z)FK#HI#$!lNr}5ikz<$6xbklV;wcbiIPrVl^26Qv;}x* zMqW?a&$(Gk(rvuBi@_fia8S7E;`Aqh(+^*x#!mYUvvMJmHRNT(LJ0q^mWks%Unwn# zZn{R$+sKmxM~?2?y<3$d(Ki*$xQVQ-3bl>MPT8G*F7O11qQde^ys38yZCvw2K!PhJ zm*e-YeGo%QJoq{YbJcbi4xj?5=}U6Jlu&USU1?U2?rV?9uz19;F+DVF--7)2@Gsn0 zOHa?IT)=M26wZ)91Y+9{Vb_gL3~(F4f}eH45IGgP{uf9V`8YlgVYFD?@hBqocKI=~ z2&lArI?)Th`rA=Ohpws#Z1P5l&`G$2*u|z-mikwSJhHg;UyP_^TcI+6p zZ&{!B*(DTR1?BKsM`HZf{81_K+=@vcSz{jb39cOCa|e%#M|0{l6ig-VmDiTs@j&)V ziAyjSU>?J3o&)AhUw_0aLyY<=3{iW7W8DmUD~4I`nc7e2wqRK)w7_=J}iOJ^W0gdsd|+jdPxbnJw*xDLF&C)H&lWiSS14jt{}fSO(XG>}NfZ zBL;ux*f{D6sT_8M4KLC?zv{}Er4Y)FMxJEOv0PfbQ&RnXrbe&|DzqRx=gQn@>Jo$( z3McT5KaR3F4s&pZ%$C6U4vc>{D-I802xgC+(({Z}FOq@EG2~+DSLspz%8%{!95LxK@_O zpCHNRK0GKV=Uh}i7Sb!xOgr)4oA;PS;T`dFpyN#KQMTB{0*e;T$O4oz&4;THJ(<~k z07t=gdMiwTR@q_{juY>JEf&tGjM_D;?kD%%dZPGFpPIu7nUgFQr0mz@bZd zJR%(ebGP?5>?hxDGAn)#1%-r$SoryCW`FO*425?#w<0C{^c`(kftW9JaV7Za?wj)* zT7GWV@_XMZL|lHJxI^~IKL3zPga-cr1?(S{w<#dNVb z25_-6meI9q*Cjm?3H?(lee%Z3q3_@C?l)EZleTsH_EAw%qe~u;#hBCP23q?a(h;?a zU|=2fomMyGH5CBf^El83E@~nAQlmKMFp)f_ODURIPel;ckOHlb^<$9m$b(@ z1arX52Yohf{V3_`vp{u{Fzhocb2@ zUHsRama)?jMg?kWzH3aCD7=I1DC?q4UWaLxL!uXL5_UVQ;K|d(DDwh_8#g>zsQ14& z-xCqw`9&q)LLy2OWNNgDUfyry)biJ1znPkz1`ivnn(tXZgs>Lp(&K-q*}-|edps?} zY^D?ak8c97@q=sg(}ThSXcl1jhck1F_lH7@JBf+k3YZ)&mZwj>(lz8^;I4lBHk{jE zm5>C9-X;cGq_g3p{TKSZSNhENSbz07T9>qceTS*E?wKwLBJJphkXBlNNekV65~Moe z2be<5_(Wg-P_N7g+YKGits8ug=>>n9)oIk258fb>T$wfQ9@;`E&~^E63W)`=6utls zbGT&jXpKZ$T7+tntXBdm$=5k7Eg?}3Kf1k}KkbTNdooV+So&GI(auaw`Ef8t#p zD{R?3?lftShNho07e{mw5s13{{OtMHZ7kc2BF8RXTbHD$6*zEAMkP3?xo`!7!@_Re z(NlaeUB~y!!d?cMj}&Gj^K;3jylQEo-+$tgN*M5;DGv_AC;Wy~hcI99QEllQ8?ntb zu#d}0;FMCED&960&3AgeDM<2rs#8)E<+lcK+-5Vw>bs>5$DAe~@Zqz%rx!|9^GT8X zdIf2!m!QcA3T6>_sAcPQiG{6>y38!lsm_W=>lR5~n=j@-E^=jawFJ99+P=??SDgYZ z)BK2TnY;%OUMoUgu6cdI-Y)pmH%rI;)$KkLg8+?mwzeK#Ry1eW@4rgf zRPz%xGSA*#GR=Rq^S+iR>hNGY9r>oJVWYY#NV~5SKsIX7)G3A7?C!B_LIRqES1>{WHDFs+VI>{b{Hp0 zX}C^Nrmn>c!Ki1~WmoOlVq)NN%;3juigJ1!rD5khevoAH|N6mJ0pUZ3o)SZhB10M} z*d1nVL_|qh%m+y9Jl^Dp#zPu82kITuZc?xohjq{(aDKMe3L+f5{Z^&1e?Rv+tr(jq ziCvVm=lBpFY80Da@ix-iO!iRiAokGB5l2Xk;?{2Z_aywdSyrEV-p)Vx0TZ$o-^IUI z>2T4%OTvWQhF^Sv*qz-w$%Y=)yZpGLP5iiTMd>%)r8JOg!#DZC;`88)e`7B#Ui;PW zI4PyHelziZDySx4oaKcN_{obg%*e13~p6$P1!kDgQg7oa=zt_V1rai9$gL%K8 z-^wc+_TL-CI4#=?ThK!XGqTu)fA4$L(|b5>AC-J6DAoU;ul1E)3HzG7)ZC-W@c+5- zn%n*2Gk`}R?Muk3_{>lnuDZWA{MfXADh{q`DJ5Zi@zrZojhwL&*~~8Xau8ROK1~Qw z?@%6{uQEz!E8M}1U@xuovs$l^d!ZAA`ZmO*E^8VJF2O_mbt}*?cLpB|GR5$NV3yi& zW`WAB7)`ZnpJO&NK*07ozyfVPPDGLi3Fu^-SsvC4+XZqw-6Cfk>1c796=#>O^63}x z24TjFPEa#;*#WTBIm~K~gmhCi-^{MgSS>k|7O77GjPA-@0^?ex%nec!3`KGcs{%y7jSE&UGtx&&v2{GSb6o)@@6Utr?LhYx>H zD++roBT)FAJaC1MpRrfIJnHm^M&&p>qfL4|0NS)C+^{P7>!3qE?JXswSD;Yw8!Wk& zEtA2)%M64mzf}2DD>wVJ#K$RR6t#R+Js0<&%?+vC+tCjpG?=2>*h2 zj9&(h?yWM2;K1$=LD1;w^So!D>>S1887L?~w=#-%#vv0{)8{Aj}pP~>-)t*|BvZ|`8yu1mNMmp|(pb7_$Y=~ZA z#pgfZEK31g&OGAz`$@BEM+(zykc{c(%zD?{$2H+;RWPUu%#AS+JA`J!4Q6f|5h^OG z(z3am@>$ZPd$L~JYimzC>pil+>u*N;Jx_fZZ}Fi*u5^b z`5yuM_$+USHnPj|TiEdYv)I?67+jq(z#PQepS#hd;+@89)LMPK)`QTg!bUkv{s$_ZN>9=ZM% zG4rMl5F+>;s>jZrJqsnL<3Fd*tH0iSAQ3JKFeLm*%|0f!YJ*#^=Ge{W;_Ud~ZFncE zRB&<+=u&Xuu2TcYqh%wt(dCBHTqw{0o7+ahZd;M4-?*&}Qtu6s2`C2}6G!nbkIFRP zu7J+tn>pIXytM~)DlE!5_UEC{{)wVfy$Qw!+kCIfN);Ai{aFyr#CJ4o``nZ-xtX#T!>$Kr+8FKay3kwYUFF_#-gAnYlgOO>S-`YMwZVAjY!Wy#C26Xvu5aex zo#gZ&WbN>Hb(h`2YH^#7m%SF@R*F7cwN`QWlmrA%r8ictj@vK{i(Py@{QbKJ!pUZn z#v2EiAWsV%dCC4vKfloN3OfI3-IfFCPc_yB2DI_pjie16LP@8VEL-7ewlCx57m<7L znb=?0v@d$$z#>E(zA@mk%04nIGf_ky&ZLMvK^II)`uGs`uiop$ttP?=*Y_02K#9l? z{KTdL7|MKxo0PKPJeqNa70l_skui4ojO&G?si0&qG>N;BXWT! z5NGLquxR(eE;?kGq^m|ZC5Q_wJKyby5;8c%%gf8d^Rn-4csq<%4o0`uQJJ}TAOoJ) z&&Q=q=pmf-_1%)$iw+%3N8WZ@aS57K%+ z6L4mIDQTgcn^vKo%U(M7`w=}I-JywRXY)KE2j1}RsC!%;q-&$RLzb%$B=}IoBER|aVSZmN6^7XdU>Firsd8I zNWBCgZ0^_J_7EMn0@qwP;d2AbX_Rn_Ba1W4ihZ zv!-tcv^@nQqW95oO^A`=9Ay|~{_pk+zdLBWrNSyb*(1E0Jm=5T)mW;l`}aX7HR zoXgT^sDWDn^d^%Wg+I?qK zk$O!lPzNmwzmLb1cR_8v(K9FD?hPZ2p5qB;L`fBP22n;!Chb753MAo%lp#^l4MUdW zX|`l{5F|&B9tGl^NjZ80&-e48hl-y0ZJg@n zgQhW2oHTocA{`GRv2Q+=ftA&Hc2wujO)SlUPq?#p9R_r0m0T--pAOC_2RMr2q-AW) z5w51tBpkGgkfvC|;1+iw?`(dJ^)|vGvL_lGeq;Y>$%q?F-yo;b%tJy>gO$x^stp_JU%eWg zsPkS2rj%XG?49s>%Ih4~Q0(a3Qg%R7d@`(n+viMbG_PDKTJ-*e*tBT7J!F#3BYmAi zW}WA#SiNsTsQI;x|C!TZMDPoYW^1kh`qu5)CGq7(rVb%^A%e*5x>~;GRMk$X^GZ39`pL z&q-ne6oMm>H9(oeH);)ZRy=>q-^&h5jnGfWtw^Z3mD~(7@OxsUiXYS z$_T?~&4d(oyN_fV4k<5lC6O;Zct4Fd7P${Fo#c=VJUeSz(P$l9d4)W7;sG-5(Ty2hGs7nJHB6RJ!zHYO_wouIMNT{MU8$qS_57K-vi759cy2%c82Ub|KEW{-a= z0xn3H*K?kGL*PumOrqpfetn#mX$662gh+4>H?D*n4`Nms$!7q3dvGDt=g9Ey!ce|; zm|NuJ8&gY?6f~W1cvls(LmlC+PXPvJVfCD5+D9ngwAH1e+0L5Dx8NiQ&Zt*eFBJZjwp_u zjC(fKYHIy0^m0gV6Htn{;O&JcglEMypy3u|@cIZEU(OtcK z{O271`HR5c>tGDu^7k)%0Ib^RwOM!1|EgM%K}-uOh_bO=7;B6X9GS5j!1Vtn*ut}J zK(Zjgzn2i~@@syy^0FO>MIQv0v_Y|Tx&zL+BSa0G=77lKE&Suj+F;O&w%g9YXh!E> z1ZH3Qgu4BkWbQ*y&hE3g$190(hzOg+uyk9tdW>=X=~BzX z?g-TUesl=c7SfUaY%$(Cyoegu&-wNfbj|`j3$ix+doc?b*C6x|DAaZM-7X7yjFY-} z^yqcUu1}vnL8yvWL;QWVkr!QEU7nXur71HqCzCG`3sN|^JzdQOG}w|ZK;RJI3a@-K zhDVImi~JhUi<{UpM%8GZUDg?nbLv4PB%|@MZ<3F2nWI%xXUB8mT*9d{m5DPjbw3D0ONb2@(84x%ZGb*Gt6vq>u_j@G^h$NM9mLmO zn1gO8m5ouppRhc7twhX*Wit?RN56H8QmDBa^^mf3@V3%Axj?2p>duVcI<<7+L@oK1 zM<AMmu>x0yABtqrM?^f%0ESqBvg<0$mNk0ZJETF62Ph_1!Cw|`x#R47I)n7l(y`I#(u-wT)@0SW0w*W9R?{B1o zl2`$HAYK;$1V;?8P421tH_fOb3;Vu)Jaoq=6$;iiO7-@Ta?}5YY4P zFu_SDF*`9B2|ilF0=5(cq;_A)BK`YaoRAK2auF$wH(vqE#m`9NNp&E(^+VdLTNLHc zJ4LoVZE5~!{@=fd)G_^F*3iUzod+*VpgE$X8HF1y$S1^qB@hgrwJT zQ-<-7Dl03|pIC6X(V2C6gG?&p%IFz~7gB45u)VoV1g4w(`_vlD$r17!fmAvVWA4t{ z5Ohusj?9D5M-M*^H2Xq_UdV=8O=V@8A4^bwsk?`5{R#>^qga_7@!Rxl_c3*-E?gS7 z(*)8h0XO+Q#%^oty>0HFAK6;zLGK5`9jVZ7Q#{hke^YrA&h_Zy{O=%DPPT~#lvixV zvh5o^BL0_;-F^HUWf+$LC-1RpB<0SGH>g@FEbgCJyhSg2v@G<~6X5 zEDWdK`6`$P=MH=qr&ls@Jnu9w9rMIWa{Z0bTVHZtoTk7ouk*Go|Ml9$Kp%^4FliH^Sb2k9N^EQwbRIL~;Ioto2jl|nE{V-bY}qIXD6>Jg zZ)0*reqo4xZ_&mjPbgidxs4bXyE8PnHlry)P~rvhll-X_kaq-nC;WFiMCF)MrwxRN z)nV-#WkSYCR0StL?nqy{gMjF;18Txv_3>OcRvXFyH+ z^O_^~wKRBJcR$&+-TZyXhk}Blz2m-w7lHB$aW;86P;Br3B4B@paQc@mc>!kCRs(2P zW)C<&``Qjy!ff>0P?C8=SoNek(MNN<(vqT`r8PW?y0!EkAP}Lec{*EXbMVrD!uMcN zGBDB|={MSc;Xwd%y!X&B`sM{@zuMB(X2-3@iX?qf0lkc!U`@CRO68dvU24bUa~vfy z9HB>rEjd_N_8fuMwZo)j)VK3;zr;;o`TIEy4Go}HD2fn-7p)-D@OM7`@%wG(pTHP( zMJ`Fp8v1Eh9DsJ+$vZ}$jKJ}=Q0DqM$k z2+PaBV@#QDF89ZY(V*HJH>QG@+!4jXa%srXeJ)r>&C>&NCpqbrP^Bsov4yc*0E$yz zz)5Z~=iICWf=&JmGKTcvK(a4#=r`&`99}X4m7x#@lZvoJELAF*`yIg)-f%*}kCSgq zaY<{Ej#!D!%{gee2@3=gUwB_Eg)qETy5S7}@bpn#qkBgJP6N||d*B|%MMGpXe7Svg zG}>Te%u%5yl;EKeG+xBO%_!%m98O&8lVv=d*~<}diY*46`zUl&6(WEhci!E3zdL!` zP_?6vh0o(Bv1n!8=IHnObs*!iQw`JC`Zk_hj$}ieZ%pEl+C)^sVg0TZo?ejL` zS@-T+<9eq@!!f(H{HdQFj>0Qn6cO@+SeARF7>e zqZxxHtCem0cI&o6!|2XMrw5$nZ^I2Qvu3?X^T#i`e-Q>U{a{&AHPVs%xx(*bLTfMw zj-V*#JO133Jq78BmwE4bBk*@l9Jdir$C~Q^Fz>5(c`Y)q8Cn>e23|m2&g;g(?#yr>GE~KhwcKLUJ7R$JNA4V>;Yr0S^2JC!@pZ|MYDY7#OY=#1_8*dy zoxi-=qUSl0lULbR!coKcEd)idSbm51Ek3>)#)qMhoJ81J4Nb7S9wHq6N{6~27xwyt z-d_5SmL;(J8;2#t)l4ZgB4LT^Hxg#5SD15icG>MlHVTci7%0w8f6O(%n>D?pl6-AD zE9*BWgK3>o91>PS3t*lUodQ%phX~!3&-YI{h;Oec7VhYy*FNuEbHxTY3@;zbDNLn< z_$Xk>6m(`#{kP^=2lPK6uZ-D%ZCmA`I;z1-&rEJv)G{7~rd=HI6X%Pqqvs4K!$6BF z^NM;V*wHBI_EDn2(dpZ$BQ6FfR)if2k!Ir?Xg;Ffjb7rV)siQ$aOH=O$(138M{IsxsA=fnF)u8rO% zEp~dJSD{sc%NM14k%r;`gokzKey>!^Yb0PR;EgAa8P?Ody1PfbD{JdzJ?7Nkrxu#0eUP!XJU+Fa1REpWoYWmGkS*9=Jg4(u& zMGcGrn8>_+x({%`%z*IW)~<8Ax;f+duEz;gW~}=p=&DcsD8BPtEcWr4yrhgI{n7B4 z8e&?`B+h%$>7Wi}5@xC?Y}uURP|>2;Y5tERu0& zl7VfqCeOe#COoY4>)10vA8=IPuQ{TVdo~~5J7Vi`dsSWJ2xS{o=|?N187^%!Zj$vQ zV*{-OXyzAQB-HT8B)FUFweQpuEKnl$swEv%+^Xj41$=y4IHk|_-=x})8Nl-eenij| z_zo$QqfO*K@cqnoQ;r@8vA|wd9=~0mTtJ~I+s^k-Jh2_Xh8idl=L8z&nfnAt0kgj2UgR2w zoL!{B#>gR@#D2#%MZam>zC5hk&r4Cpgf$@x`ua~tZ_FJhif?R+L@wu*(+o^Z50Y~b zEW%#>1lz1D(R~~2J}>5<3THLS8bUgDPx7kVsV&H5yYoajd^dl~cbwQI{i^s<>06l* z(cffzZ*p=HX>)M1Z#%Mn!v-8X{nmdDVFvc>JFQ}CopFlOeKQMxy79*2VfjK{sZX6Z z%w;1q)c(R5DbG^RWIFCkHX6heVhqPY|7&xO*1n@qAgUtX1Zfs}dXnrlnrLi64{$zP zI}1FS6i0MyY;4nK&5K+DW{<9V;B35$3eNsEr;NS;_<{V~eQuN*>KwZ_TQ&wdaay6! z+1$lnP(rA7uAx)4p!kf9b>~03ukGfdG$Qit#Wnv6B^hwEvaZ=j+?)qgQh@uLs3*#Q zhQ{pO#KI5vc?C?N%NPVwwX$61{P^N@vMw<-9j68Y(lBF}M^G>qs7pVL?|fx>(@#iv ze3o(K% zUBzQf3A#D_$x^VUQR4K^^c8{- z&PE8|@o=I&vefp)UWmC(WcECA1<17=`BYIhuZzx{PXX?tr4V++g2l@{uZWunq-ZG( zJ9!a8k-33fQKayH*I8W#nguG66dD97%{_hW}D00%RKg>?s7Uh^WWmGSvay z92Xp!T7VZPkqZm-j?`9xta`=O3$!;TtERz=Q`gDz*zg$>@-qjZZ`#Yg{(t>zv}(Fg zxj>mEET92XA!ugoJ$q_1v2Q{HbngGdzT8E>dOa1DwD+>~$e|l=0gSZ4hNgX>TK#gG zyHT@T#zv%45sh_GTKn)qa4<5qQrq#bnON%I4*d1@zGALmZt7rW zE~0L3Z*j$PCuCUrWo5T)`pc-*gtr?C*^GMXnpai1DNqd=bb- zb%U~&yS-oo^Nx7-uj8lt^(*MT*T?KQ|6pz7XrG76Rlx4-POq71|NhFAC5Q9gv&(Z! z6IF|pKfm&-7W(e?f4nw3-C1^IabYlLWx4ilY?bHI>_nB@((4$0I-mjAHHwh{g%``~_`!OV%D zMy`ny9DK}~5f9X^eA;hv*P6Gac*5Mz{n@AQ<&RiRExbxEF1+=c*t%m>=FNxAd*apf zem%3l+(+A=we)F*VtKaM`l3~zZe^a>!Koc>r4y|V={6@V!+!|Wzx;DSTrQ(=W}&j{ z>W8WOOkCUfqS_a=jQP#YKE2KAAA0|#NBqOn8EeUV>0TqlF7j%tD)JZeP1Eu_5n=PEtGT1ZWRXSD&u_oApvsldEg_HX zJ}TK>4WWoizY$JrS}3{FvS5GEN5xXgU(ooz)|<^`COz46=OStEY1Q)Ae4G<1a_D); zeye~<@KW5;C!@ei7EfCl`WuX|W;LeNC|qU}OuaBr=O;Z;P_n~)h!h#z)A1x;n4VYp zOJ&RwcjM+!MOq%ejb9A(@^nuu@-jM=`|kHMZY>d4@%)u_OR>LS#(VC=#MFFuv_arA zKb9{glsH>8HjBWuqnlbvb>?LD_DMGTFw?zo&F3;T+#)e%Hz{=JdrYmpJf-0OA?~e% zs_fghUqnDcx=T8xOG+B)X3-%fus}KmDe01u2I*Logi3cym!P5`jX{V(DDTPhduPwy zGkfoO_FsF(x$j%=1um|2UEi~g<8w+aDed&lxqu{3x_vmVpw8#kqv4Qc?2%66TQ0b& zToeujM$e2JKjko3yPX-G#H(#GJ-L08QZhzG^E?S3ui$OQ&uJ7Ff;NZ;Z+?g+sGE+% zXn>tga`@L++~)gUBdKIdhVu;38GAkSwI_<>oa+ z+UG^Nj{5HKJO@7?gHOZl6U}6n15;%yk>@vc{FcK8Ud%uGs=^~^PQ66@FY9RT^D5{! ztGiSBIDSvO8~*&1hu>esW!k~}o%b#Gha0I1U3QhmPx85GLvnWeky5eaC@LleOfANu z4r6DkJR`rCvV+zR@q|m5GOO7qd0)Ia|6wCsDE=XKWzif{B3F!y##YDW+4G&a zx<&3E(*{e_eYiY!t#SNsXSjG|RhHu$U9vqG&8(t&Ql@T22@jRI6kQkS!H`k4Nx@qy zcKL~l85XR2mrEg2B=N(Rscfq*a6xXSWQd0VU6e>9tbKC?Xs-0fr>i^j} zHd=`tFl7$1W%Eczo7o*gkytYe8qn1kms$MkXWh835GS5#f-G~`8_cRpkyreq&FI$4 zkM2gE%+I1q+2Za+tUSo`sSmR;DXje4ScRX14y&e{?#@HfS<2MR^6&Y5*#^vYxQLt? zNyedg4lB0cLJSs~G+OMTYh15~%TWdd z99%S4Q}v8r`1vSZJ4h)A<5?ZsZ0MKp>qTgWlt7jYcnJKGVL zbH!DsMg}f%%sLkBC5nl}lUMX65;{$2j~92x|9pA7sam^HNr*UiYouo1qNV%jF-c|^ zlkzKNn?Z}p(v~-JG$`6p?9uQtxFVM}DvO$RFu ztjSu!Ckh-#v7R{Rf}DCnns%6rvzi-BJVaO(Z zG_5;I-ywrLb8Xxvp6RB%VJBvQ>rk<k2xGTQ_* zFXD`6q@ue?!E9`$HP=vc5 zI3`w8Olf0O5{tigpB*d^BCwBbT7?fmh7-a}vLBN?C2QZdYO^VjoP|9yM<1mvQRIRi>XvD599# zT1=IubWtB6vBvhk`IjvU(U$homJMdCl< zk)2ZyVUumE{1Qt{=`_)~Z)APTQFiGzFGV9VFywc*hr+BnE4;sJN-$Smv7a5VZY4V&iPsrBWl01MPDhM zc}iaJp_9#-Pp&XS!MCTyZP?mupfej#M@Xbn)26D8%;zn)ZQ+}14X%d#L81Slh@$Aw z=(no-p>KnR6RYncE17`FyovU7K`m4D-=xxRj`8)6<<;RumIW;Q_&3(oj>tG)zj>xY zl$lj)Sv~NG6JhVtt21pJtsqK{&w4+#$5rF~*aX@A?Jphr=(W7s0dlSZW85qptnKj& zKF$5anRIimX;Wi;Hz-fT3>Q?IN88XdKeZRTotC3;Jk4+m*!jp0&eo$i#po_M17ocT z0zdP{lqP&8d6(mCAjeg*(6l2uxny%VF=r6hH9dtda80_J`+}#V>Nr{@xbnWI8zbd% zD<8MNSCn=&3 z5XhRsd+g!rjQc8&O3Gy1q;rWc;%1TDrwXr3c^rJpMWI;IE}FzW`TP*klrPceL(I(U zy^0R0g#}Z}9nNOdTz9+65y#>x4nh;VbP|7sqFQU`J1r9y3~xV2Y-~M95#dN&h@RGL zD)W?lgD^LqOQ5j%nlvup^264SmU^#sTV==QBeLqAYyHa>k>d60YZWB(OEqGdEqY&g z)r*{_Jv;2O=^fn`sLXx&@ve~@Xps&1-ygRpSxBx%F?Eb)gvWN3GUMCLH-Dc}rlDvy zHT|Y)rXtH&$QKaNm9uX$z@8W z(rIZ4k2)S~=$pATZk?GvV4SNR?YZ`Xm576#b_A*aP<1E%+#ju_;CPFrRv;Riq{%C- z-dp3Md$g-!?^&1V)J3XsZg za(G!lsAA9BRsCQWNBI4K+mLHoM($L*o!7I!R_Kefo#!%3*lLF<{6aodm`%94R)|~1 zhE_x3Qg{K$44Sin_uYQAp`G_jd=bgYc)lP!RhT*%&ELn3(VB4Ei|2XuZm??LBGX#S z`>5Qb(#ZhyV^b9par@pO^Y=Qy8SPd2Zp41gkE_Q$a*wKLSZc>F+wfgG35*Gk{gOl->kgRx^zcx(xB z;BMo+3O6qv!?nZ*R*ZceL~Z8M{L3vU0j0wydO*&WN)b^ zJND5|%ut#m(<^@%Yjw7~z?bN8S?_XV-+0wJ)iJ?#t-x*uN3y>DFdpBPWeeE;z@WI^&@x|2~jtfox7&Vq(xWe7QR8r!C?- zD)UwiSFowUGpg{eh?IV?I!jwG)4VLtAn9e~E0@=WWteyihQ>c@y$!QFc0W}+SGKnO zsd@5ZY3kpi#1}zwGK!B05*YfYBy-~AdB?x&upEA*TdAU)Nl#fC9ce_I8k%hGV`;kV8$0+@oBMbGD=eQ{O55H8PmQd1t3RqyNP_whx}a zy%-M0qeVrN%Q|nCvQnk8+-tu@YPl;M@ROL4st9ZUTeJn&-hPeVW^FT9&ot>5Avmw{ zv>Qu5ohop9XxvOUPWC>RgLy7xwO2kgQbhl-{N#EZ9kOl!IWJdh$-c*Up2hl%0AbZP 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

cmPMNMTUJANH&&~_d_ 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≫vj_Ww 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≦g#g^p8!;CD%j1W28&BKc{69V4hkuIC4LU@ruv-i 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